365 Days of Code - Day 010

Today’s code was simple, get the mobile layout configuration set. Most importantly, can we configured a mobile-style hamburger menu using CSS only? Yes, for the most part. One UI feature missing is that if you click the hamburger menu, it will stay open unless you click a button to close it. Since there is no JavaScript, there is no detection for clicks elsewhere on the page. I was willing to give up that functionality, and added a button to close the menu.

Configuring the Mobile Hamburger Menu

The CSS that needed to be added is fairly simple. The rest of the CSS is configured through Tailwind.

css
.nav-details summary::-webkit-details-marker {
  display: none;
}

.nav-details[open] .icon-open {
  display: none;
}

.nav-details:not([open]) .icon-close {
  display: none;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.nav-details summary::-webkit-details-marker {
  display: none;
}

.nav-details[open] .icon-open {
  display: none;
}

.nav-details:not([open]) .icon-close {
  display: none;
}

The HTML is more complicated. This is our new nav section, which is included in our header component at /layouts/_partials/header.html:

html
<nav class="flex justify-between items-center gap-6 font-medium text-white" aria-label="Primary">
  {{- $brand := or site.Title "Jim Diroff II" -}}
  <a href="{{ " /" | relLangURL }}" class="text-lg font-bold">
    {{ $brand }}
  </a>

  {{- $links := slice
  (dict "name" "Home" "url" "/")
  (dict "name" "Blog" "url" "/posts/")
  -}}

  <!-- Desktop nav -->
  <div class="hidden md:flex gap-6">
    {{- range $links -}}
    <a href="{{ .url | relLangURL }}" class="hover:text-blue-500 transition-colors">
      {{ .name }}
    </a>
    {{- end -}}
  </div>

  <!-- Mobile menu (no JS) -->
  <details class="nav-details md:hidden relative">
    <summary
      class="inline-flex items-center justify-center rounded-lg p-2 hover:text-blue-500 transition-colors
              focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent"
      aria-label="Toggle navigation menu">
      <span class="sr-only">Menu</span>

      <!-- Hamburger icon -->
      <svg class="icon-open h-6 w-6" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
      </svg>

      <!-- Close (X) icon -->
      <svg class="icon-close h-6 w-6" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
      </svg>
    </summary>

    <!-- Dropdown panel -->
    <div class="absolute right-0 top-full mt-3 w-52 overflow-hidden rounded-xl border border-slate-800
              bg-slate-950/95 backdrop-blur shadow-xl">
      <div class="p-2 flex flex-col">
        {{- range $links -}}
        <a href="{{ .url | relLangURL }}"
          class="rounded-lg px-3 py-2 hover:bg-white/5 hover:text-blue-500 transition-colors">
          {{ .name }}
        </a>
        {{- end -}}
      </div>
    </div>
  </details>
</nav>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<nav class="flex justify-between items-center gap-6 font-medium text-white" aria-label="Primary">
  {{- $brand := or site.Title "Jim Diroff II" -}}
  <a href="{{ " /" | relLangURL }}" class="text-lg font-bold">
    {{ $brand }}
  </a>

  {{- $links := slice
  (dict "name" "Home" "url" "/")
  (dict "name" "Blog" "url" "/posts/")
  -}}

  <!-- Desktop nav -->
  <div class="hidden md:flex gap-6">
    {{- range $links -}}
    <a href="{{ .url | relLangURL }}" class="hover:text-blue-500 transition-colors">
      {{ .name }}
    </a>
    {{- end -}}
  </div>

  <!-- Mobile menu (no JS) -->
  <details class="nav-details md:hidden relative">
    <summary
      class="inline-flex items-center justify-center rounded-lg p-2 hover:text-blue-500 transition-colors
              focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent"
      aria-label="Toggle navigation menu">
      <span class="sr-only">Menu</span>

      <!-- Hamburger icon -->
      <svg class="icon-open h-6 w-6" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
      </svg>

      <!-- Close (X) icon -->
      <svg class="icon-close h-6 w-6" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
      </svg>
    </summary>

    <!-- Dropdown panel -->
    <div class="absolute right-0 top-full mt-3 w-52 overflow-hidden rounded-xl border border-slate-800
              bg-slate-950/95 backdrop-blur shadow-xl">
      <div class="p-2 flex flex-col">
        {{- range $links -}}
        <a href="{{ .url | relLangURL }}"
          class="rounded-lg px-3 py-2 hover:bg-white/5 hover:text-blue-500 transition-colors">
          {{ .name }}
        </a>
        {{- end -}}
      </div>
    </div>
  </details>
</nav>