HTML Formatterhtmlformatteronline.com

Collapsible sidebar

A dashboard sidebar that collapses to icons and becomes a drawer on mobile.

navigationsidebardashboarddrawerresponsive
<div class="shell" id="shell">
  <aside class="side" id="side">
    <div class="side-head">
      <span class="mark">A</span>
      <span class="name">Acme</span>
      <button class="toggle" id="toggle" aria-label="Collapse sidebar" aria-expanded="true">
        <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 6l-6 6 6 6"/></svg>
      </button>
    </div>

    <nav aria-label="Sections">
      <a href="#" aria-current="page">
        <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12l9-9 9 9M5 10v10h14V10"/></svg>
        <span>Overview</span>
      </a>
      <a href="#">
        <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 19V9M10 19V5M16 19v-7M22 19H2"/></svg>
        <span>Reports</span>
      </a>
      <a href="#">
        <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h10"/></svg>
        <span>Orders</span>
      </a>
      <a href="#">
        <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="8" r="3"/><path d="M5 20c0-3.9 3.1-7 7-7s7 3.1 7 7"/></svg>
        <span>Customers</span>
      </a>
    </nav>

    <div class="side-foot">
      <a href="#">
        <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M2 12h3M19 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"/></svg>
        <span>Settings</span>
      </a>
    </div>
  </aside>

  <main class="content">
    <button class="drawer-open" id="drawer-open" aria-label="Open menu">
      <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg>
    </button>
    <h1>Overview</h1>
    <p>Collapse the sidebar with the arrow, or narrow the preview to see it become a drawer.</p>
  </main>

  <div class="scrim" id="scrim" hidden></div>
</div>

How it works

Two different behaviours from one component, which is what makes sidebars fiddly.

On desktop it collapses in place. The width animates from 232 to 64 pixels and the labels disappear, leaving an icon rail. The content area is a flex child, so it reflows automatically — no width calculation anywhere.

On mobile it becomes a drawer. Below 760 pixels the sidebar switches to position: fixed and slides in over the content with a scrim behind it. The collapse toggle is hidden, because collapsing to icons on a phone helps nobody.

The collapsed state overrides itself on mobile. Note the media query re-enables the labels even when .narrow is set. Without that, a user who collapsed the sidebar on desktop and then opened the site on a phone would get a drawer full of unlabelled icons.

Icons stay inline. Inline SVG with stroke: currentColor means the icons take their colour from the link state — grey normally, indigo when current — with no second set of files and no icon font.

Accessibility notes

The collapse button carries aria-expanded and its label changes with the state, so a screen reader user knows what pressing it will do.

When the drawer opens, focus moves to the first link inside it and Escape closes it. Icons are marked aria-hidden because the adjacent text already names each item — announcing both would read everything twice.

In the collapsed state the labels are hidden with display: none, which removes them from the accessibility tree too. For a production dashboard, add a title or a tooltip so the icon rail is not a memory test.

Making it yours

Change the two widths in .side and .shell.narrow .side to resize the rail. Keep the collapsed width above about 56 pixels or the icons stop having comfortable tap targets.

To remember the collapsed state between visits, write it to localStorage in the toggle handler and read it back before first paint — the same inline-script trick used for a dark-mode preference, which avoids a visible flash.

Related templates

Check your version

Once you have edited this, the HTML validator will catch any tag you left unclosed, and the formatter will tidy the indentation. Both run in your browser.