28 Tailwind Hamburger Menus
22 / 28

All 28 in 28 Tailwind Hamburger Menus

  1. 01Tailwind Hamburger Menu with Clip-Path Circle Expansion
  2. 02Tailwind Keyboard Navigable Hamburger Menu with ARIA Attributes
  3. 03Tailwind Animated 3-Line to X Hamburger Icon Toggle
  4. 04Tailwind Slide-In Off-Canvas Drawer with Dimmed Backdrop Overlay
  5. 05Pure Tailwind Checkbox Hack Hamburger Navigation (No JavaScript)
  6. 06Tailwind Responsive Hamburger Menu with Integrated Search Bar & Social Links
  7. 07Tailwind Glassmorphism Backdrop Blur Hamburger Navigation
  8. 08Tailwind Push Page Navigation Menu (Off-Canvas Content Shift)
  9. 09Pure Tailwind Hamburger Menu Without JavaScript
  10. 10Tailwind Hamburger Menu Morphing to Close Icon Animation
  11. 11Tailwind Full Screen Mobile Navigation Overlay
  12. 12Tailwind Slide Out Sidebar Navigation / Off Canvas Menu
  13. 13Tailwind Responsive Navbar Mobile Hamburger Toggle Breakpoints
  14. 14Tailwind Minimalist Floating Hamburger Button
  15. 15Tailwind CSS Responsive Hamburger Menu Component
  16. 16Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
  17. 17Tailwind WCAG Accessible Hamburger Menu Example
  18. 18Pure Tailwind Dropdown Slide Down Mobile Hamburger Menu
  19. 19Simple 3-Line Tailwind Hamburger Icon Hover Effect
  20. 20Tailwind Full Screen Blur Overlay Hamburger Menu
  21. 21Tailwind Mobile Bottom Navigation Bar with Floating Hamburger Toggle
  22. 22Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu
  23. 23Flexbox Navbar Right-Aligned Tailwind Hamburger Icon
  24. 24Radial Ripple Expand Tailwind Hamburger Menu
  25. 25Sticky Tailwind Navbar Shrinking Hamburger Icon on Scroll
  26. 26Neumorphic Tailwind Hamburger Toggle Button
  27. 27Brutalist Design Bordered Tailwind Hamburger Menu
  28. 28Tailwind 3D Flip Canvas Mobile Hamburger Navigation
Pure TailwindMIT licensed

Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu

Nested navigation with zero JavaScript and zero fake semantics: every level is a native <details>/<summary>, so keyboard support and screen-reader disclosure announcements come free. Tailwind's open: and group-open: variants read the element's own state, the name attribute makes the top level exclusive, and native toggling stays instant — the ::details-content animation is documented but deliberately not shipped.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="thb-22 w-full min-h-svh bg-thb22-bg font-thb22-sans text-thb22-ink px-5 py-9" aria-label="Multi-level accordion menu demo">
  <div class="min-h-120 flex flex-col items-center justify-center gap-3.5 p-6.5 rounded-[18px] border border-thb22-edge bg-thb22-bg">
    <aside aria-label="Menu" class="w-[min(340px,100%)] overflow-hidden rounded-[18px] border border-thb22-edge bg-thb22-panel shadow-[0_22px_48px_-30px_oklch(0.3_0.05_145/0.55)]">
      <header class="flex items-center justify-between px-4.5 pt-4 pb-3 border-b border-thb22-edge">
        <b class="text-base font-bold tracking-[-0.01em]">Browse</b>
        <span class="relative block w-4.5 h-3 opacity-50" aria-hidden="true">
          <i class="absolute left-0 top-0 w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
          <i class="absolute left-0 top-[5px] w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
          <i class="absolute left-0 top-2.5 w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
        </span>
      </header>
      <nav aria-label="Categories" class="grid px-2.5 pt-2 pb-3">
        <details name="thb22-top" open class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Shop
            <b class="ml-auto px-2 py-0.5 rounded-full bg-thb22-badge text-[10.5px] font-bold text-thb22-mut tabular-nums">312</b>
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#new" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">New arrivals</a></li>
            <li>
              <details class="thb22-d">
                <summary class="flex items-center gap-2.5 min-h-10.5 px-2.5 rounded-[10px] text-[13.5px] font-semibold text-thb22-mut cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
                  <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Clothing
                </summary>
                <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-1.5 border-l-[1.5px] border-thb22-edge">
                  <li><a href="#knitwear" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Knitwear</a></li>
                  <li><a href="#outerwear" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Outerwear</a></li>
                  <li><a href="#denim" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Denim</a></li>
                </ul>
              </details>
            </li>
            <li>
              <details class="thb22-d">
                <summary class="flex items-center gap-2.5 min-h-10.5 px-2.5 rounded-[10px] text-[13.5px] font-semibold text-thb22-mut cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
                  <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Objects
                </summary>
                <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-1.5 border-l-[1.5px] border-thb22-edge">
                  <li><a href="#ceramics" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Ceramics</a></li>
                  <li><a href="#lighting" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Lighting</a></li>
                </ul>
              </details>
            </li>
            <li><a href="#archive-sale" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Archive sale</a></li>
          </ul>
        </details>
        <details name="thb22-top" class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Stories
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#studio-visits" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Studio visits</a></li>
            <li><a href="#process" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Process notes</a></li>
          </ul>
        </details>
        <details name="thb22-top" class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Support
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#shipping" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Shipping &amp; returns</a></li>
            <li><a href="#care" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Care guides</a></li>
            <li><a href="#contact" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Contact</a></li>
          </ul>
        </details>
        <a href="#account" class="flex items-center min-h-10.5 mt-1.5 pt-1 px-2.5 border-t border-thb22-edge text-[13.5px] font-bold text-thb22-ink no-underline transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Account</a>
      </nav>
    </aside>
    <p class="text-center text-xs text-thb22-mut text-pretty" aria-hidden="true">native <code class="font-thb22-mono text-[11px] px-1.5 py-0.5 rounded bg-thb22-badge">&lt;details name&gt;</code> &mdash; top level is exclusive, keyboard &amp; screen-reader support built in, 0 JS</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-thb22-bg: #f6f8f4;
  --color-thb22-bg: oklch(0.97 0.006 130);
  --color-thb22-panel: #ffffff;
  --color-thb22-panel: oklch(1 0 0);
  --color-thb22-hover: #f0f4ee;
  --color-thb22-hover: oklch(0.96 0.01 135);
  --color-thb22-badge: #e7ece4;
  --color-thb22-badge: oklch(0.94 0.01 135);
  --color-thb22-edge: #dde3d9;
  --color-thb22-edge: oklch(0.89 0.012 130);
  --color-thb22-ink: #313a30;
  --color-thb22-ink: oklch(0.24 0.02 140);
  --color-thb22-mut: #6e766c;
  --color-thb22-mut: oklch(0.5 0.018 140);
  --color-thb22-acc: #4f8a5c;
  --color-thb22-acc: oklch(0.55 0.14 150);
  --font-thb22-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-thb22-mono: 'JetBrains Mono', ui-monospace, monospace;
  --ease-thb22: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* The ::details-content expansion animation is deliberately NOT shipped, and the recipe lives in
   the "How it works" notes rather than in commented-out CSS here. Engines differ on whether that
   slot reserves layout while its block-size animates; where it does not, an expanding submenu
   paints over its siblings — worse than no animation. Native toggling is instant and correct. */
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
   w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
   moment before the browser build has compiled its utilities. */

.thb-22 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-thb22-bg: #f6f8f4;
  --color-thb22-bg: oklch(0.97 0.006 130);
  --color-thb22-panel: #ffffff;
  --color-thb22-panel: oklch(1 0 0);
  --color-thb22-hover: #f0f4ee;
  --color-thb22-hover: oklch(0.96 0.01 135);
  --color-thb22-badge: #e7ece4;
  --color-thb22-badge: oklch(0.94 0.01 135);
  --color-thb22-edge: #dde3d9;
  --color-thb22-edge: oklch(0.89 0.012 130);
  --color-thb22-ink: #313a30;
  --color-thb22-ink: oklch(0.24 0.02 140);
  --color-thb22-mut: #6e766c;
  --color-thb22-mut: oklch(0.5 0.018 140);
  --color-thb22-acc: #4f8a5c;
  --color-thb22-acc: oklch(0.55 0.14 150);
  --font-thb22-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-thb22-mono: 'JetBrains Mono', ui-monospace, monospace;
  --ease-thb22: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* The ::details-content expansion animation is deliberately NOT shipped, and the recipe lives in
   the "How it works" notes rather than in commented-out CSS here. Engines differ on whether that
   slot reserves layout while its block-size animates; where it does not, an expanding submenu
   paints over its siblings — worse than no animation. Native toggling is instant and correct. */
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
   w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
   moment before the browser build has compiled its utilities. */

.thb-22 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • This is the collection's clearest example of a variant that looks right and is wrong: group-open: on a summary-scoped group never matches ([open] is on the <details>), and hoisting the group to the <details> leaks through nesting. The port keeps the original selector shape as a direct-child arbitrary variant instead, and the .d2 blocks still disappear.
  • open: is equally a trap here — it styles the element that HAS [open], so it works on a <details> and does nothing on a <summary>. The label uses [details[open]>&]: for that reason.
  • Marker hiding needed three declarations and still does: list-none marker:hidden [&::-webkit-details-marker]:hidden. Utilities make it verbose but keep the Safari workaround attached to the element that needs it.
  • The indent guideline (ml-4 pl-3.5 border-l-[1.5px]) is now identical on every nested list, so depth costs a copy rather than a new class name.
  • The ::details-content animation stays in CSS — and stays switched OFF. It is the one enhancement in the collection that failed its own smoke test: in an engine that does not reserve layout for the animating slot, the submenu overlapped its siblings, so the demo ships the native instant toggle and keeps the recipe as a comment.
  • Zero JavaScript, and the name attribute does what a JS accordion controller used to: exclusivity is declarative.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/pure-tailwind-multi-level-accordion-hamburger-dropdown-menu/

Nested navigation with zero JavaScript and zero fake semantics: every level is a native <details>/<summary>, so keyboard support and screen-reader disclosure announcements come free. Tailwind's open: and group-open: variants read the element's own state, the name attribute makes the top level exclusive, and native toggling stays instant &mdash; the ::details-content animation is documented but deliberately not shipped.
## HTML
```html
<section class="thb-22 w-full min-h-svh bg-thb22-bg font-thb22-sans text-thb22-ink px-5 py-9" aria-label="Multi-level accordion menu demo">
  <div class="min-h-120 flex flex-col items-center justify-center gap-3.5 p-6.5 rounded-[18px] border border-thb22-edge bg-thb22-bg">
    <aside aria-label="Menu" class="w-[min(340px,100%)] overflow-hidden rounded-[18px] border border-thb22-edge bg-thb22-panel shadow-[0_22px_48px_-30px_oklch(0.3_0.05_145/0.55)]">
      <header class="flex items-center justify-between px-4.5 pt-4 pb-3 border-b border-thb22-edge">
        <b class="text-base font-bold tracking-[-0.01em]">Browse</b>
        <span class="relative block w-4.5 h-3 opacity-50" aria-hidden="true">
          <i class="absolute left-0 top-0 w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
          <i class="absolute left-0 top-[5px] w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
          <i class="absolute left-0 top-2.5 w-4.5 h-0.5 rounded-sm bg-thb22-ink"></i>
        </span>
      </header>
      <nav aria-label="Categories" class="grid px-2.5 pt-2 pb-3">
        <details name="thb22-top" open class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Shop
            <b class="ml-auto px-2 py-0.5 rounded-full bg-thb22-badge text-[10.5px] font-bold text-thb22-mut tabular-nums">312</b>
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#new" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">New arrivals</a></li>
            <li>
              <details class="thb22-d">
                <summary class="flex items-center gap-2.5 min-h-10.5 px-2.5 rounded-[10px] text-[13.5px] font-semibold text-thb22-mut cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
                  <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Clothing
                </summary>
                <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-1.5 border-l-[1.5px] border-thb22-edge">
                  <li><a href="#knitwear" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Knitwear</a></li>
                  <li><a href="#outerwear" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Outerwear</a></li>
                  <li><a href="#denim" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Denim</a></li>
                </ul>
              </details>
            </li>
            <li>
              <details class="thb22-d">
                <summary class="flex items-center gap-2.5 min-h-10.5 px-2.5 rounded-[10px] text-[13.5px] font-semibold text-thb22-mut cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
                  <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] transition-[rotate] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Objects
                </summary>
                <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-1.5 border-l-[1.5px] border-thb22-edge">
                  <li><a href="#ceramics" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Ceramics</a></li>
                  <li><a href="#lighting" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Lighting</a></li>
                </ul>
              </details>
            </li>
            <li><a href="#archive-sale" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Archive sale</a></li>
          </ul>
        </details>
        <details name="thb22-top" class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Stories
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#studio-visits" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Studio visits</a></li>
            <li><a href="#process" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Process notes</a></li>
          </ul>
        </details>
        <details name="thb22-top" class="thb22-d">
          <summary class="flex items-center gap-2.5 min-h-11.5 px-2.5 rounded-[10px] text-[14.5px] font-bold cursor-pointer list-none marker:hidden [&amp;::-webkit-details-marker]:hidden transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2 [details[open]>&]:text-thb22-acc">
            <svg viewBox="0 0 24 24" class="w-3.75 h-3.75 shrink-0 fill-none stroke-current stroke-[2.2] [stroke-linecap:round] [stroke-linejoin:round] text-thb22-mut transition-[rotate,color] duration-250 ease-thb22 [details[open]>summary>&]:rotate-90 [details[open]>summary>&]:text-thb22-acc motion-reduce:transition-none" aria-hidden="true"><path d="m9 5 7 7-7 7"/></svg>Support
          </summary>
          <ul class="list-none grid ml-4 pl-3.5 pt-0.5 pb-2 border-l-[1.5px] border-thb22-edge">
            <li><a href="#shipping" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Shipping &amp; returns</a></li>
            <li><a href="#care" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Care guides</a></li>
            <li><a href="#contact" class="flex items-center min-h-10.5 px-2.5 rounded-[9px] text-[13.5px] font-semibold text-thb22-mut no-underline transition-[background,color,translate] duration-150 hover:bg-thb22-hover hover:text-thb22-ink hover:translate-x-0.5 focus-visible:bg-thb22-hover focus-visible:text-thb22-ink focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Contact</a></li>
          </ul>
        </details>
        <a href="#account" class="flex items-center min-h-10.5 mt-1.5 pt-1 px-2.5 border-t border-thb22-edge text-[13.5px] font-bold text-thb22-ink no-underline transition-[background,color] duration-150 hover:bg-thb22-hover focus-visible:bg-thb22-hover focus-visible:outline-2 focus-visible:outline-thb22-acc focus-visible:-outline-offset-2">Account</a>
      </nav>
    </aside>
    <p class="text-center text-xs text-thb22-mut text-pretty" aria-hidden="true">native <code class="font-thb22-mono text-[11px] px-1.5 py-0.5 rounded bg-thb22-badge">&lt;details name&gt;</code> &mdash; top level is exclusive, keyboard &amp; screen-reader support built in, 0 JS</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-thb22-bg: #f6f8f4;
  --color-thb22-bg: oklch(0.97 0.006 130);
  --color-thb22-panel: #ffffff;
  --color-thb22-panel: oklch(1 0 0);
  --color-thb22-hover: #f0f4ee;
  --color-thb22-hover: oklch(0.96 0.01 135);
  --color-thb22-badge: #e7ece4;
  --color-thb22-badge: oklch(0.94 0.01 135);
  --color-thb22-edge: #dde3d9;
  --color-thb22-edge: oklch(0.89 0.012 130);
  --color-thb22-ink: #313a30;
  --color-thb22-ink: oklch(0.24 0.02 140);
  --color-thb22-mut: #6e766c;
  --color-thb22-mut: oklch(0.5 0.018 140);
  --color-thb22-acc: #4f8a5c;
  --color-thb22-acc: oklch(0.55 0.14 150);
  --font-thb22-sans: 'Space Grotesk', system-ui, sans-serif;
  --font-thb22-mono: 'JetBrains Mono', ui-monospace, monospace;
  --ease-thb22: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* The ::details-content expansion animation is deliberately NOT shipped, and the recipe lives in
   the "How it works" notes rather than in commented-out CSS here. Engines differ on whether that
   slot reserves layout while its block-size animates; where it does not, an expanding submenu
   paints over its siblings — worse than no animation. Native toggling is instant and correct. */
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
   w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
   moment before the browser build has compiled its utilities. */

.thb-22 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The component is the platform's own disclosure element, nested.

<details name="thb22-top">      <!-- shared name = exclusive group -->
  <summary class="[details[open]>&]:text-thb22-acc …">
    <svg class="[details[open]>summary>&]:rotate-90 …"></svg>Shop
  </summary>
  <ul class="ml-4 pl-3.5 border-l-[1.5px]">…</ul>
</details>

<summary> is a real button to the platform: Enter and Space toggle it, and screen readers announce “Shop, button, collapsed” with no ARIA written by you. The name attribute gives mutually exclusive top-level sections declaratively — remove it and they open independently.

Where the port is subtle. The obvious move is group on the summary plus group-open: on the chevron — and it silently fails, because [open] lands on the <details>, not the summary. Moving group up to the <details> fails differently: group-open: compiles to .group[open] &, which matches through ANY open ancestor, so a closed submenu's chevron rotates whenever its parent section is open. The honest answer is a direct-child arbitrary variant, [details[open]>summary>&]:rotate-90, which is exactly the original CSS selector written from the element's side.

Where it does not. Hiding the default marker still takes three declarations, one of them a vendor pseudo-element, and the ::details-content animation cannot be a utility — it targets an internal slot and depends on interpolate-size being set on an ancestor. Both stay in the stylesheet, and that is the right call.

Why the animation is not shipped here. ::details-content is an internal slot, and engines differ on whether it reserves layout while its block-size animates. Where it does not, an expanding submenu paints straight over the items below it — strictly worse than no animation. To try it: set interpolate-size: allow-keywords on the demo root, then give .thb22-d::details-content block-size:0; overflow:hidden with a block-size transition (plus content-visibility with allow-discrete), and block-size:auto when [open] — but test it in your own target engines first. Native toggling is instant, correct everywhere, and precisely what <details> has done for fifteen years.

Make it yours

  • Exclusive versus independent top level is the name attribute. Delete it for multi-open; sub-levels here are independent on purpose, because users compare subcategories.
  • Depth is structurally unlimited; visually stop at three. Each level adds ml-4 pl-3.5, and three of those is all a 320px drawer can afford.
  • Pre-open the current path by server-rendering open on the <details> chain — the user lands seeing where they are.
  • To force one open at every level, add a name per level (name=&quot;lvl2-shop&quot;). Grouping is per-name, so it nests cleanly.
  • Retheme through --color-thb22-acc, which drives every open chevron, open label and focus ring.

Gotchas — read before shipping

  • Do not put an <a> inside a <summary> for rows that both navigate and expand — a control cannot honestly be both link and button. Split it, or make the parent a pure category as here.
  • Hiding the marker needs BOTH list-none and the ::-webkit-details-marker rule, or Safari leaves a ghost gap. marker:hidden alone is not enough.
  • The name attribute silently no-ops in engines older than 2024 — sections just open independently. Fine for navigation; if exclusivity is business-critical you need a shim.
  • Animating with max-h-* re-introduces the classic timing lag AND fights the name attribute, because the closing panel loses [open] immediately and snaps. Test any ::details-content animation for layout reservation before shipping it: if the slot does not take space while animating, the submenu draws over the rows beneath it.
  • Neither open: on a summary nor group-open: from a summary-scoped group works: [open] belongs to the <details>. And group-open: from a details-scoped group matches through every open ancestor, so nested chevrons rotate together. Use the direct-child form.
  • interpolate-size is inherited. Scope it to the component root so unrelated height:auto transitions elsewhere on the page stay unaffected.

Browser support

ChromeSafariFirefoxEdge
120+17.4+130+120+

<details>/<summary> are universal. name-exclusivity is Chrome 120, Safari 17.2 and Firefox 130; the the ::details-content animation is Chrome 131+ and rolling out elsewhere; this demo documents it but ships the native instant toggle.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…