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
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 & 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><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 & 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;
}
}.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. */
/* 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. */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 & 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. */
```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 & 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:0andoverflow:hiddenor content leaks during the slide.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 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.