32 CSS Accordions — Vertical & Horizontal02 / 32

Pure CSSMIT licensed

Mobile Navigation Accordion Menu (CSS-Only Hamburger + Drill-Down)

A complete mobile nav in pure CSS: a checkbox-driven hamburger that morphs into an X, opening a slide-down panel where each category is a <details> accordion with 48px touch targets, rotating indicators and indented child links — the exact 'hamburger menu without JavaScript' pattern devs search for.

Published

Live Demo
Try it

The code

<section class="acc-02" aria-label="Mobile navigation accordion demo">
  <nav class="acc-02__nav">
    <div class="acc-02__bar">
      <span class="acc-02__logo">north<b>peak</b></span>
      <input type="checkbox" id="acc-02-burger" class="acc-02__check">
      <label for="acc-02-burger" class="acc-02__burger" aria-label="Toggle menu"><span></span><span></span><span></span></label>
    </div>
    <div class="acc-02__panel">
      <div class="acc-02__panel-in">
        <details class="acc-02__cat">
          <summary class="acc-02__row">Gear</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Backpacks</a></li>
            <li><a href="#" class="acc-02__link">Tents &amp; shelter</a></li>
            <li><a href="#" class="acc-02__link">Trail footwear</a></li>
          </ul>
        </details>
        <details class="acc-02__cat">
          <summary class="acc-02__row">Apparel</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Jackets</a></li>
            <li><a href="#" class="acc-02__link">Base layers</a></li>
          </ul>
        </details>
        <details class="acc-02__cat">
          <summary class="acc-02__row">Guides</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Route planning</a></li>
            <li><a href="#" class="acc-02__link">Winter safety</a></li>
          </ul>
        </details>
        <a href="#" class="acc-02__row acc-02__row--plain">About</a>
        <a href="#" class="acc-02__cta">Find a store</a>
      </div>
    </div>
  </nav>
</section>
.acc-02,
.acc-02 *,
.acc-02 *::before,
.acc-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.acc-02 {
  --accent: oklch(0.62 0.15 155);
  --ink: #14201a;
  --row: 52px;
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: #e9efe9;
  padding: 40px 20px;
}

.acc-02__nav {
  width: min(400px,100%);
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 24px 60px -34px rgba(20,50,30,.4);
  overflow: hidden;
}

.acc-02__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
}

.acc-02__logo {
  font-size: 19px;
  font-weight: 400;
  letter-spacing: -.02em;
  color: var(--ink);
}

.acc-02__logo b {
  font-weight: 800;
  color: var(--accent);
}

.acc-02__check {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.acc-02__burger {
  width: 48px;
  height: 48px;
  display: grid;
  place-content: center;
  gap: 5px;
  cursor: pointer;
  border-radius: 12px;
  transition: background .15s;
}

.acc-02__burger:hover {
  background: #f0f4f0;
}

.acc-02__burger span {
  display: block;
  width: 22px;
  height: 2.5px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform .3s cubic-bezier(.4,0,.2,1),opacity .2s;
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(1) {
  transform: translateY(7.5px) rotate(45deg);
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(2) {
  opacity: 0;
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(3) {
  transform: translateY(-7.5px) rotate(-45deg);
}

.acc-02__check:focus-visible + .acc-02__burger {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.acc-02__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .4s cubic-bezier(.4,0,.2,1);
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__panel {
  grid-template-rows: 1fr;
}

.acc-02__panel-in {
  min-height: 0;
  overflow: hidden;
  padding: 0 10px;
}

.acc-02__cat {
  border-top: 1px solid #edf1ed;
}

.acc-02__row {
  list-style: none;
  display: flex;
  align-items: center;
  min-height: var(--row);
  padding: 0 10px;
  font-size: 16px;
  font-weight: 650;
  color: var(--ink);
  cursor: pointer;
  position: relative;
  border-radius: 10px;
  transition: background .15s;
  text-decoration: none;
}

.acc-02__row::-webkit-details-marker {
  display: none;
}

.acc-02__row::marker {
  content: "";
}

.acc-02__row:hover {
  background: #f2f6f2;
}

summary.acc-02__row::after {
  content: "";
  position: absolute;
  right: 16px;
  width: 12px;
  height: 12px;
  background: linear-gradient(currentColor,currentColor) center/12px 2.5px no-repeat,linear-gradient(currentColor,currentColor) center/2.5px 12px no-repeat;
  color: var(--accent);
  transition: transform .3s;
}

.acc-02__cat[open] > summary.acc-02__row::after {
  transform: rotate(135deg);
}

.acc-02__sub {
  list-style: none;
  margin: 2px 0 10px 20px;
  border-left: 2px solid #dfe8df;
}

.acc-02__link {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 14px;
  font-size: 15px;
  color: #43554a;
  text-decoration: none;
  border-radius: 8px;
}

.acc-02__link:hover {
  color: var(--accent);
  background: #f2f6f2;
}

.acc-02__row--plain {
  border-top: 1px solid #edf1ed;
  border-radius: 0;
}

.acc-02__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  margin: 10px 10px 16px;
  background: var(--accent);
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  border-radius: 12px;
  transition: filter .15s;
}

.acc-02__cta:hover {
  filter: brightness(1.08);
}

.acc-02__row:focus-visible,
.acc-02__link:focus-visible,
.acc-02__cta:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .acc-02__panel,
    .acc-02__burger span,
    summary.acc-02__row::after {
    transition: none;
  }
}
/* No JavaScript — a visually-hidden checkbox + :has(:checked) drives the hamburger morph and the grid-template-rows 0fr→1fr panel slide; each nav category is a native <details> accordion. */
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 Accordions — Vertical & Horizontal 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: Mobile Navigation Accordion Menu (CSS-Only Hamburger + Drill-Down)
Source: https://codefronts.com/navigation/css-accordions/mobile-nav-accordion-menu-css-only/

A complete mobile nav in pure CSS: a checkbox-driven hamburger that morphs into an X, opening a slide-down panel where each category is a <details> accordion with 48px touch targets, rotating indicators and indented child links — the exact 'hamburger menu without JavaScript' pattern devs search for.
## HTML
```html
<section class="acc-02" aria-label="Mobile navigation accordion demo">
  <nav class="acc-02__nav">
    <div class="acc-02__bar">
      <span class="acc-02__logo">north<b>peak</b></span>
      <input type="checkbox" id="acc-02-burger" class="acc-02__check">
      <label for="acc-02-burger" class="acc-02__burger" aria-label="Toggle menu"><span></span><span></span><span></span></label>
    </div>
    <div class="acc-02__panel">
      <div class="acc-02__panel-in">
        <details class="acc-02__cat">
          <summary class="acc-02__row">Gear</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Backpacks</a></li>
            <li><a href="#" class="acc-02__link">Tents &amp; shelter</a></li>
            <li><a href="#" class="acc-02__link">Trail footwear</a></li>
          </ul>
        </details>
        <details class="acc-02__cat">
          <summary class="acc-02__row">Apparel</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Jackets</a></li>
            <li><a href="#" class="acc-02__link">Base layers</a></li>
          </ul>
        </details>
        <details class="acc-02__cat">
          <summary class="acc-02__row">Guides</summary>
          <ul class="acc-02__sub">
            <li><a href="#" class="acc-02__link">Route planning</a></li>
            <li><a href="#" class="acc-02__link">Winter safety</a></li>
          </ul>
        </details>
        <a href="#" class="acc-02__row acc-02__row--plain">About</a>
        <a href="#" class="acc-02__cta">Find a store</a>
      </div>
    </div>
  </nav>
</section>
```
## CSS
```css
.acc-02,
.acc-02 *,
.acc-02 *::before,
.acc-02 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.acc-02 {
  --accent: oklch(0.62 0.15 155);
  --ink: #14201a;
  --row: 52px;
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: #e9efe9;
  padding: 40px 20px;
}

.acc-02__nav {
  width: min(400px,100%);
  background: #fff;
  border-radius: 18px;
  box-shadow: 0 24px 60px -34px rgba(20,50,30,.4);
  overflow: hidden;
}

.acc-02__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
}

.acc-02__logo {
  font-size: 19px;
  font-weight: 400;
  letter-spacing: -.02em;
  color: var(--ink);
}

.acc-02__logo b {
  font-weight: 800;
  color: var(--accent);
}

.acc-02__check {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.acc-02__burger {
  width: 48px;
  height: 48px;
  display: grid;
  place-content: center;
  gap: 5px;
  cursor: pointer;
  border-radius: 12px;
  transition: background .15s;
}

.acc-02__burger:hover {
  background: #f0f4f0;
}

.acc-02__burger span {
  display: block;
  width: 22px;
  height: 2.5px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform .3s cubic-bezier(.4,0,.2,1),opacity .2s;
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(1) {
  transform: translateY(7.5px) rotate(45deg);
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(2) {
  opacity: 0;
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__burger span:nth-child(3) {
  transform: translateY(-7.5px) rotate(-45deg);
}

.acc-02__check:focus-visible + .acc-02__burger {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

.acc-02__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .4s cubic-bezier(.4,0,.2,1);
}

.acc-02__nav:has(.acc-02__check:checked) .acc-02__panel {
  grid-template-rows: 1fr;
}

.acc-02__panel-in {
  min-height: 0;
  overflow: hidden;
  padding: 0 10px;
}

.acc-02__cat {
  border-top: 1px solid #edf1ed;
}

.acc-02__row {
  list-style: none;
  display: flex;
  align-items: center;
  min-height: var(--row);
  padding: 0 10px;
  font-size: 16px;
  font-weight: 650;
  color: var(--ink);
  cursor: pointer;
  position: relative;
  border-radius: 10px;
  transition: background .15s;
  text-decoration: none;
}

.acc-02__row::-webkit-details-marker {
  display: none;
}

.acc-02__row::marker {
  content: "";
}

.acc-02__row:hover {
  background: #f2f6f2;
}

summary.acc-02__row::after {
  content: "";
  position: absolute;
  right: 16px;
  width: 12px;
  height: 12px;
  background: linear-gradient(currentColor,currentColor) center/12px 2.5px no-repeat,linear-gradient(currentColor,currentColor) center/2.5px 12px no-repeat;
  color: var(--accent);
  transition: transform .3s;
}

.acc-02__cat[open] > summary.acc-02__row::after {
  transform: rotate(135deg);
}

.acc-02__sub {
  list-style: none;
  margin: 2px 0 10px 20px;
  border-left: 2px solid #dfe8df;
}

.acc-02__link {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 14px;
  font-size: 15px;
  color: #43554a;
  text-decoration: none;
  border-radius: 8px;
}

.acc-02__link:hover {
  color: var(--accent);
  background: #f2f6f2;
}

.acc-02__row--plain {
  border-top: 1px solid #edf1ed;
  border-radius: 0;
}

.acc-02__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  margin: 10px 10px 16px;
  background: var(--accent);
  color: #fff;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  border-radius: 12px;
  transition: filter .15s;
}

.acc-02__cta:hover {
  filter: brightness(1.08);
}

.acc-02__row:focus-visible,
.acc-02__link:focus-visible,
.acc-02__cta:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .acc-02__panel,
    .acc-02__burger span,
    summary.acc-02__row::after {
    transition: none;
  }
}
```

## JavaScript
```js
/* No JavaScript — a visually-hidden checkbox + :has(:checked) drives the hamburger morph and the grid-template-rows 0fr→1fr panel slide; each nav category is a native <details> accordion. */
```

How this works

Two native mechanisms are layered. The hamburger is a visually-hidden <input type=checkbox> whose <label> draws three bars; :has(:checked) on the nav root slides the panel open with grid-template-rows: 0fr → 1fr (the modern height-auto animation trick) and morphs the bars into an X with transform-only moves.

Inside the panel, each category with children is a <details>: the <summary> is a full-width 52px-tall row (comfortably above the 48×48px WCAG touch-target minimum), a plus indicator rotates 45° into an X on open, and child links indent with a left rule so hierarchy is obvious at a glance. Because both mechanisms are native, Back-button, focus order and screen-reader operation all behave correctly with zero scripting.

Techniques used: :has(:checked) hamburger state on the nav root · grid-template-rows 0fr→1fr panel slide (animates to height auto) · transform-only hamburger→X morph · details/summary category accordions with 52px touch rows · indented child list with border-left hierarchy rule

Make it yours

  • Panel slide speed = the grid-template-rows transition duration.
  • Touch row height = --row (keep ≥48px for WCAG 2.5.8).
  • Dark navbar? Flip --bg/--ink — everything else derives.
  • Add more categories freely; details items are order-independent.

Gotchas — read before shipping

  • Keep the checkbox in the DOM before the panel and its label 44px+ — a tiny hamburger is the #1 mobile a11y failure.
  • The checkbox hack gives the hamburger checkbox semantics, not button semantics; screen readers say "checked" rather than "expanded". Acceptable for a demo — on production sites consider a 3-line JS enhancement adding aria-expanded to a real button.
  • Don't nest details more than one level in a nav — drill-downs deeper than that disorient users; link to a landing page instead.
  • grid-template-rows animation needs the inner wrapper to have min-height:0 and overflow:hidden or content leaks during the slide.

Browser support

ChromeSafariFirefoxEdge
111+15.4+121+111+

Floor set by :has(), oklch() as this demo is written. The core technique itself goes back further — Chrome 105+ (:has).

:has() and grid-rows animation are baseline since 2023. Without :has() the panel simply stays open — content is never lost.

Techniques used in this demo

Search CodeFronts

Loading…