Skip to content

Nav Collapse

Mobile menu for Navbar. Prefer native <details> — open and close with no JavaScript. The default panel is a surface under the trigger; inside a navbar it becomes a full-width sheet under the bar. af-is-overlay is the full-page variant.

Menu
<details class="af-nav-collapse">
  <summary class="af-btn af-is-sm">Menu</summary>
  <nav aria-label="Site">
    <ul>
      <li>
        <a href="#" aria-current="page">Home</a>
      </li>
      <li>
        <a href="#">Docs</a>
      </li>
      <li>
        <a href="#">Blog</a>
      </li>
    </ul>
  </nav>
</details>

Put the toggle in af-navbar-actions. Below md (768px), af-navbar-nav hides when a collapse is present. af-is-compact forces that chrome at any width — use it for demos, or for an always-hamburger header.

<nav class="af-navbar af-is-compact">
  <a href="#" class="af-navbar-brand">Brand</a>
  <ul class="af-navbar-nav">
    <li>
      <a href="#">Home</a>
    </li>
    <li>
      <a href="#">Docs</a>
    </li>
  </ul>
  <div class="af-navbar-actions">
    <details class="af-nav-collapse">
      <summary class="af-btn af-is-sm">Menu</summary>
      <nav aria-label="Site">
        <ul>
          <li>
            <a href="#" aria-current="page">Home</a>
          </li>
          <li>
            <a href="#">Docs</a>
          </li>
          <li>
            <a href="#">Blog</a>
          </li>
        </ul>
      </nav>
    </details>
  </div>
</nav>

Open with the open attribute (or .af-nav-collapse-open on the menu for the button path). summary takes af-btn so it matches other controls; the native disclosure marker is removed.

If you cannot use <details>, a <button> plus your own toggle still works. You own ARIA, focus, and Escape:

<div class="af-nav-collapse" open>
  <button
  type="button"
  class="af-btn af-is-sm"
  aria-expanded="true"
  aria-controls="nav-menu"
>Menu</button>
  <nav id="nav-menu">
    <ul>
      <li>
        <a href="#" aria-current="page">Home</a>
      </li>
      <li>
        <a href="#">Docs</a>
      </li>
    </ul>
  </nav>
</div>

Toggle open on the container, or hidden / .af-nav-collapse-open on the menu. Focus trap, Escape, and return-focus are not in the CSS.

af-is-overlay fills the viewport below the bar — a full-page mobile menu, not a dropdown sheet. Pair it with a sticky or fixed navbar. Set --af-navbar-height on the bar to the real height (default 3.5rem). Overlay open locks document scroll in CSS. Close <details> on navigate. Focus trap stays in your script.

<!-- Required: --af-navbar-height must match the real bar (default 3.5rem). Close this details on route change. -->
<nav class="af-navbar af-is-compact" style="--af-navbar-height: 3.5rem">
  <a href="#" class="af-navbar-brand">Brand</a>
  <ul class="af-navbar-nav">
    <li>
      <a href="#">Home</a>
    </li>
    <li>
      <a href="#">Docs</a>
    </li>
  </ul>
  <div class="af-navbar-actions">
    <details class="af-nav-collapse af-is-overlay">
      <summary class="af-btn af-is-sm">Menu</summary>
      <nav aria-label="Site">
        <ul>
          <li>
            <a href="#" aria-current="page">Home</a>
          </li>
          <li>
            <a href="#">Docs</a>
          </li>
          <li>
            <a href="#">Blog</a>
          </li>
        </ul>
        <div class="af-nav-collapse-cta">
          <a href="#" class="af-btn af-w-full">Get Started</a>
        </div>
      </nav>
    </details>
  </div>
</nav>
  • .af-nav-collapse — container (<details> or a <div>)
  • .af-nav-collapse-open — show the menu (on the <nav> / <ul>)
  • .af-nav-collapse-menu — optional class on the menu panel
  • .af-nav-collapse.af-is-overlay — full-page panel below --af-navbar-height. Open overlay locks document scroll.
  • .af-nav-collapse-cta — optional pinned action in the overlay
  • .af-navbar.af-is-compact — always hide inline nav and show the collapse
  • aria-expanded on the trigger
  • aria-controls linking trigger to menu
  • hidden on the menu when closed

Close <details> on route change. Overlay open already locks document scroll in CSS. Focus trap stays in the app.

  • Navbar — below md, af-navbar-nav hides when an af-nav-collapse is present. Put the toggle in af-navbar-actions.