11 / 20
All 20 in 20 CSS Sidebar Navigation
- 01Accessible Sidebar Navigation ARIA Reference
- 02Sidebar Focus Trap and Restore on Close
- 03Inert Background Content Behind a Drawer
- 04Sidebar Navigation Is Not a Tab List
- 05Announcing Collapsed and Expanded Sidebar State
- 06Roving Tabindex for Sidebar Keyboard Navigation
- 07Persisting Sidebar Collapse State Across Reloads
- 08Sidebar Scroll Containment with Overscroll Behavior
- 09Nested Submenu Keyboard and Screen Reader Behavior
- 10Resizable Sidebar with a Keyboard Accessible Handle
- 11Collapsible Sidebar with Checkbox State
- 12Responsive Sidebar Breakpoint Behavior
- 13Icon Rail Sidebar with Accessible Labels
- 14Dashboard Sidebar with Current Page State
- 15Multi Level Sidebar Menu Disclosure Pattern
- 16Accordion Sidebar with Details and Summary
- 17Sticky Full Height Sidebar Layout Mechanics
- 18Floating Sidebar Stacking and Overlay Order
- 19Off Canvas Sidebar Drawer with Focus Return
- 20Sidebar Footer Pinning with Scrollable Nav
Collapsible Sidebar with Checkbox State
The no-script collapse: a visually hidden checkbox, :has(:checked) on the root, and one width custom property doing all the work. One honest limitation: a checkbox cannot carry aria-expanded, so the pattern needs a clearly labelled control, and the scripted version in demo 05 is the accessible ceiling. css-side-menu covers the look; this is the state mechanism under it.
Mosaic by Cruip: a complete Tailwind admin dashboard template in HTML, React, Next.js and Vue · $69
Cruip · Mosaic by Cruip · Affiliate link
The code
<section class="sdb-11" aria-label="Vale account settings">
<div class="sdb-11__theme">
<input class="sdb-11__theme-input" type="checkbox" id="sdb-11-theme">
<label class="sdb-11__theme-label" for="sdb-11-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sdb-11__sr">Toggle dark theme</span>
</label>
</div>
<div class="sdb-11__app">
<input class="sdb-11__state" type="checkbox" id="sdb-11-collapse">
<aside class="sdb-11__rail">
<div class="sdb-11__rail-head">
<label class="sdb-11__toggle" for="sdb-11-collapse">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span class="sdb-11__toggle-open">Collapse</span>
<span class="sdb-11__toggle-shut">Expand</span>
</label>
</div>
<nav class="sdb-11__nav" aria-label="Vale account settings">
<ul class="sdb-11__list">
<li><a class="sdb-11__link" href="#sdb-11-main" aria-current="page"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Profile</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Notifications</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Security</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Billing</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">API keys</span></a></li>
</ul>
</nav>
<p class="sdb-11__rail-foot"><span class="sdb-11__label">Signed in as Priya Sharma</span></p>
</aside>
<main class="sdb-11__main" id="sdb-11-main">
<h2 class="sdb-11__main-h">Profile</h2>
<dl class="sdb-11__fields">
<div><dt>Display name</dt><dd>Priya Sharma</dd></div>
<div><dt>Workspace</dt><dd>Vale · Operations</dd></div>
<div><dt>Time zone</dt><dd>Europe/Lisbon (UTC+1)</dd></div>
<div><dt>Two-factor</dt><dd>Authenticator app</dd></div>
<div><dt>Recovery email</dt><dd>p.sharma@vale.example</dd></div>
<div><dt>Member since</dt><dd>March 2024</dd></div>
</dl>
</main>
</div>
</section><section class="sdb-11" aria-label="Vale account settings">
<div class="sdb-11__theme">
<input class="sdb-11__theme-input" type="checkbox" id="sdb-11-theme">
<label class="sdb-11__theme-label" for="sdb-11-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sdb-11__sr">Toggle dark theme</span>
</label>
</div>
<div class="sdb-11__app">
<input class="sdb-11__state" type="checkbox" id="sdb-11-collapse">
<aside class="sdb-11__rail">
<div class="sdb-11__rail-head">
<label class="sdb-11__toggle" for="sdb-11-collapse">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span class="sdb-11__toggle-open">Collapse</span>
<span class="sdb-11__toggle-shut">Expand</span>
</label>
</div>
<nav class="sdb-11__nav" aria-label="Vale account settings">
<ul class="sdb-11__list">
<li><a class="sdb-11__link" href="#sdb-11-main" aria-current="page"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Profile</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Notifications</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Security</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Billing</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">API keys</span></a></li>
</ul>
</nav>
<p class="sdb-11__rail-foot"><span class="sdb-11__label">Signed in as Priya Sharma</span></p>
</aside>
<main class="sdb-11__main" id="sdb-11-main">
<h2 class="sdb-11__main-h">Profile</h2>
<dl class="sdb-11__fields">
<div><dt>Display name</dt><dd>Priya Sharma</dd></div>
<div><dt>Workspace</dt><dd>Vale · Operations</dd></div>
<div><dt>Time zone</dt><dd>Europe/Lisbon (UTC+1)</dd></div>
<div><dt>Two-factor</dt><dd>Authenticator app</dd></div>
<div><dt>Recovery email</dt><dd>p.sharma@vale.example</dd></div>
<div><dt>Member since</dt><dd>March 2024</dd></div>
</dl>
</main>
</div>
</section>.sdb-11 {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
--sdb-11-w-open: 15rem;
--sdb-11-w-rail: 4.5rem;
--sdb-11-w: var(--sdb-11-w-open);
--sdb-11-font-display: "Avenir Next Rounded", Nunito, "Trebuchet MS", ui-rounded, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--sdb-11-bg);
color: var(--sdb-11-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.55;
}
.sdb-11 *,
.sdb-11 *::before,
.sdb-11 *::after {
box-sizing: border-box;
}
.sdb-11:has(.sdb-11__state:checked) {
--sdb-11-w: var(--sdb-11-w-rail);
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
@media (prefers-color-scheme: dark) {
.sdb-11 {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
}
}
@media (prefers-color-scheme: light) {
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
}
.sdb-11__theme {
position: absolute;
inset-block-start: 0.85rem;
inset-inline-end: 0.85rem;
z-index: 60;
}
.sdb-11__theme-input {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.sdb-11__theme-label {
display: grid;
place-items: center;
inline-size: 2.75rem;
block-size: 2.75rem;
border-radius: 1.4rem;
border: 0;
background: var(--sdb-11-surf2);
color: var(--sdb-11-ink);
cursor: pointer;
}
.sdb-11__theme-label svg {
inline-size: 1.15rem;
block-size: 1.15rem;
}
.sdb-11__theme-input:focus-visible + .sdb-11__theme-label {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__sr,
.sdb-11__state {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
margin: 0;
}
.sdb-11__app {
display: flex;
min-inline-size: 0;
min-block-size: 100vh;
background: var(--sdb-11-surf);
}
.sdb-11__rail {
flex: none;
inline-size: var(--sdb-11-w);
min-inline-size: 0;
display: flex;
flex-direction: column;
background: var(--sdb-11-surf2);
transition: inline-size 200ms ease;
}
.sdb-11__rail-head {
padding: 0.6rem;
}
.sdb-11__toggle {
display: flex;
align-items: center;
gap: 0.6rem;
min-block-size: 2.75rem;
padding-inline: 0.7rem;
border-radius: 1.4rem;
background: var(--sdb-11-surf);
cursor: pointer;
font-size: 0.86rem;
font-weight: 600;
white-space: nowrap;
min-inline-size: 2.75rem;
}
.sdb-11__toggle svg {
inline-size: 1.15rem;
block-size: 1.15rem;
flex: none;
}
.sdb-11__state:focus-visible ~ .sdb-11__rail .sdb-11__toggle {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__toggle-shut {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-open {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-shut,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle span {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle {
justify-content: center;
padding-inline: 0;
}
.sdb-11__nav {
flex: 1;
min-block-size: 0;
padding: 0.2rem 0.6rem;
}
.sdb-11__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.15rem;
}
.sdb-11__link {
display: flex;
align-items: center;
gap: 0.75rem;
min-block-size: 2.75rem;
padding: 0.5rem 0.7rem;
border-radius: 1.4rem;
color: var(--sdb-11-ink);
text-decoration: none;
font-size: 0.92rem;
white-space: nowrap;
min-inline-size: 0;
}
.sdb-11__glyph {
flex: none;
font-size: 0.6rem;
color: var(--sdb-11-mut);
}
.sdb-11__link:hover {
background: var(--sdb-11-surf);
}
.sdb-11__link[aria-current="page"] {
background: var(--sdb-11-acc);
color: var(--sdb-11-surf);
font-weight: 650;
}
.sdb-11__link[aria-current="page"] .sdb-11__glyph {
color: inherit;
}
.sdb-11__link:focus-visible {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__rail-foot {
margin: 0;
padding: 0.7rem 1rem 0.9rem;
font-size: 0.78rem;
color: var(--sdb-11-mut);
white-space: nowrap;
overflow: hidden;
}
.sdb-11__label {
min-inline-size: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__label {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__link {
justify-content: center;
gap: 0;
}
.sdb-11__main {
flex: 1;
min-inline-size: 0;
padding: 1.5rem 1.25rem 2.5rem;
}
@media (min-width: 46rem) {
.sdb-11__main {
padding: 2.4rem 2.25rem 3rem;
}
}
.sdb-11__main-h {
margin: 0 0 1.2rem;
font-family: var(--sdb-11-font-display);
font-size: clamp(1.35rem, 3.4vw, 1.9rem);
line-height: 1.15;
}
.sdb-11__fields {
margin: 0;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.6rem;
max-inline-size: 44rem;
}
@media (min-width: 34rem) {
.sdb-11__fields {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.sdb-11__fields div {
min-inline-size: 0;
padding: 0.75rem 0.9rem;
border-radius: 0.9rem;
background: var(--sdb-11-surf2);
}
.sdb-11__fields dt {
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sdb-11-mut);
}
.sdb-11__fields dd {
margin: 0.15rem 0 0;
font-size: 0.92rem;
}
@media (max-width: 30rem) {
.sdb-11__app {
flex-direction: column;
}
.sdb-11__rail,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__rail {
inline-size: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.sdb-11__rail {
transition: none;
}
}.sdb-11 {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
--sdb-11-w-open: 15rem;
--sdb-11-w-rail: 4.5rem;
--sdb-11-w: var(--sdb-11-w-open);
--sdb-11-font-display: "Avenir Next Rounded", Nunito, "Trebuchet MS", ui-rounded, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--sdb-11-bg);
color: var(--sdb-11-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.55;
}
.sdb-11 *,
.sdb-11 *::before,
.sdb-11 *::after {
box-sizing: border-box;
}
.sdb-11:has(.sdb-11__state:checked) {
--sdb-11-w: var(--sdb-11-w-rail);
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
@media (prefers-color-scheme: dark) {
.sdb-11 {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
}
}
@media (prefers-color-scheme: light) {
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
}
.sdb-11__theme {
position: absolute;
inset-block-start: 0.85rem;
inset-inline-end: 0.85rem;
z-index: 60;
}
.sdb-11__theme-input {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.sdb-11__theme-label {
display: grid;
place-items: center;
inline-size: 2.75rem;
block-size: 2.75rem;
border-radius: 1.4rem;
border: 0;
background: var(--sdb-11-surf2);
color: var(--sdb-11-ink);
cursor: pointer;
}
.sdb-11__theme-label svg {
inline-size: 1.15rem;
block-size: 1.15rem;
}
.sdb-11__theme-input:focus-visible + .sdb-11__theme-label {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__sr,
.sdb-11__state {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
margin: 0;
}
.sdb-11__app {
display: flex;
min-inline-size: 0;
min-block-size: 100vh;
background: var(--sdb-11-surf);
}
.sdb-11__rail {
flex: none;
inline-size: var(--sdb-11-w);
min-inline-size: 0;
display: flex;
flex-direction: column;
background: var(--sdb-11-surf2);
transition: inline-size 200ms ease;
}
.sdb-11__rail-head {
padding: 0.6rem;
}
.sdb-11__toggle {
display: flex;
align-items: center;
gap: 0.6rem;
min-block-size: 2.75rem;
padding-inline: 0.7rem;
border-radius: 1.4rem;
background: var(--sdb-11-surf);
cursor: pointer;
font-size: 0.86rem;
font-weight: 600;
white-space: nowrap;
min-inline-size: 2.75rem;
}
.sdb-11__toggle svg {
inline-size: 1.15rem;
block-size: 1.15rem;
flex: none;
}
.sdb-11__state:focus-visible ~ .sdb-11__rail .sdb-11__toggle {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__toggle-shut {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-open {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-shut,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle span {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle {
justify-content: center;
padding-inline: 0;
}
.sdb-11__nav {
flex: 1;
min-block-size: 0;
padding: 0.2rem 0.6rem;
}
.sdb-11__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.15rem;
}
.sdb-11__link {
display: flex;
align-items: center;
gap: 0.75rem;
min-block-size: 2.75rem;
padding: 0.5rem 0.7rem;
border-radius: 1.4rem;
color: var(--sdb-11-ink);
text-decoration: none;
font-size: 0.92rem;
white-space: nowrap;
min-inline-size: 0;
}
.sdb-11__glyph {
flex: none;
font-size: 0.6rem;
color: var(--sdb-11-mut);
}
.sdb-11__link:hover {
background: var(--sdb-11-surf);
}
.sdb-11__link[aria-current="page"] {
background: var(--sdb-11-acc);
color: var(--sdb-11-surf);
font-weight: 650;
}
.sdb-11__link[aria-current="page"] .sdb-11__glyph {
color: inherit;
}
.sdb-11__link:focus-visible {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__rail-foot {
margin: 0;
padding: 0.7rem 1rem 0.9rem;
font-size: 0.78rem;
color: var(--sdb-11-mut);
white-space: nowrap;
overflow: hidden;
}
.sdb-11__label {
min-inline-size: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__label {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__link {
justify-content: center;
gap: 0;
}
.sdb-11__main {
flex: 1;
min-inline-size: 0;
padding: 1.5rem 1.25rem 2.5rem;
}
@media (min-width: 46rem) {
.sdb-11__main {
padding: 2.4rem 2.25rem 3rem;
}
}
.sdb-11__main-h {
margin: 0 0 1.2rem;
font-family: var(--sdb-11-font-display);
font-size: clamp(1.35rem, 3.4vw, 1.9rem);
line-height: 1.15;
}
.sdb-11__fields {
margin: 0;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.6rem;
max-inline-size: 44rem;
}
@media (min-width: 34rem) {
.sdb-11__fields {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.sdb-11__fields div {
min-inline-size: 0;
padding: 0.75rem 0.9rem;
border-radius: 0.9rem;
background: var(--sdb-11-surf2);
}
.sdb-11__fields dt {
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sdb-11-mut);
}
.sdb-11__fields dd {
margin: 0.15rem 0 0;
font-size: 0.92rem;
}
@media (max-width: 30rem) {
.sdb-11__app {
flex-direction: column;
}
.sdb-11__rail,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__rail {
inline-size: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.sdb-11__rail {
transition: none;
}
}Here's a working CSS Sidebar Navigation 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: Collapsible Sidebar with Checkbox State
Source: https://codefronts.com/navigation/css-sidebar-navigation/pure-css-collapsible-sidebar-navigation-snippet/
The no-script collapse: a visually hidden checkbox, :has(:checked) on the root, and one width custom property doing all the work. One honest limitation: a checkbox cannot carry aria-expanded, so the pattern needs a clearly labelled control, and the scripted version in demo 05 is the accessible ceiling. css-side-menu covers the look; this is the state mechanism under it.
## HTML
```html
<section class="sdb-11" aria-label="Vale account settings">
<div class="sdb-11__theme">
<input class="sdb-11__theme-input" type="checkbox" id="sdb-11-theme">
<label class="sdb-11__theme-label" for="sdb-11-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sdb-11__sr">Toggle dark theme</span>
</label>
</div>
<div class="sdb-11__app">
<input class="sdb-11__state" type="checkbox" id="sdb-11-collapse">
<aside class="sdb-11__rail">
<div class="sdb-11__rail-head">
<label class="sdb-11__toggle" for="sdb-11-collapse">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span class="sdb-11__toggle-open">Collapse</span>
<span class="sdb-11__toggle-shut">Expand</span>
</label>
</div>
<nav class="sdb-11__nav" aria-label="Vale account settings">
<ul class="sdb-11__list">
<li><a class="sdb-11__link" href="#sdb-11-main" aria-current="page"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Profile</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Notifications</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Security</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Billing</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">API keys</span></a></li>
</ul>
</nav>
<p class="sdb-11__rail-foot"><span class="sdb-11__label">Signed in as Priya Sharma</span></p>
</aside>
<main class="sdb-11__main" id="sdb-11-main">
<h2 class="sdb-11__main-h">Profile</h2>
<dl class="sdb-11__fields">
<div><dt>Display name</dt><dd>Priya Sharma</dd></div>
<div><dt>Workspace</dt><dd>Vale · Operations</dd></div>
<div><dt>Time zone</dt><dd>Europe/Lisbon (UTC+1)</dd></div>
<div><dt>Two-factor</dt><dd>Authenticator app</dd></div>
<div><dt>Recovery email</dt><dd>p.sharma@vale.example</dd></div>
<div><dt>Member since</dt><dd>March 2024</dd></div>
</dl>
</main>
</div>
</section>
```
## CSS
```css
.sdb-11 {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
--sdb-11-w-open: 15rem;
--sdb-11-w-rail: 4.5rem;
--sdb-11-w: var(--sdb-11-w-open);
--sdb-11-font-display: "Avenir Next Rounded", Nunito, "Trebuchet MS", ui-rounded, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--sdb-11-bg);
color: var(--sdb-11-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.55;
}
.sdb-11 *,
.sdb-11 *::before,
.sdb-11 *::after {
box-sizing: border-box;
}
.sdb-11:has(.sdb-11__state:checked) {
--sdb-11-w: var(--sdb-11-w-rail);
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
@media (prefers-color-scheme: dark) {
.sdb-11 {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
}
}
@media (prefers-color-scheme: light) {
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
}
.sdb-11__theme {
position: absolute;
inset-block-start: 0.85rem;
inset-inline-end: 0.85rem;
z-index: 60;
}
.sdb-11__theme-input {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.sdb-11__theme-label {
display: grid;
place-items: center;
inline-size: 2.75rem;
block-size: 2.75rem;
border-radius: 1.4rem;
border: 0;
background: var(--sdb-11-surf2);
color: var(--sdb-11-ink);
cursor: pointer;
}
.sdb-11__theme-label svg {
inline-size: 1.15rem;
block-size: 1.15rem;
}
.sdb-11__theme-input:focus-visible + .sdb-11__theme-label {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__sr,
.sdb-11__state {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
margin: 0;
}
.sdb-11__app {
display: flex;
min-inline-size: 0;
min-block-size: 100vh;
background: var(--sdb-11-surf);
}
.sdb-11__rail {
flex: none;
inline-size: var(--sdb-11-w);
min-inline-size: 0;
display: flex;
flex-direction: column;
background: var(--sdb-11-surf2);
transition: inline-size 200ms ease;
}
.sdb-11__rail-head {
padding: 0.6rem;
}
.sdb-11__toggle {
display: flex;
align-items: center;
gap: 0.6rem;
min-block-size: 2.75rem;
padding-inline: 0.7rem;
border-radius: 1.4rem;
background: var(--sdb-11-surf);
cursor: pointer;
font-size: 0.86rem;
font-weight: 600;
white-space: nowrap;
min-inline-size: 2.75rem;
}
.sdb-11__toggle svg {
inline-size: 1.15rem;
block-size: 1.15rem;
flex: none;
}
.sdb-11__state:focus-visible ~ .sdb-11__rail .sdb-11__toggle {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__toggle-shut {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-open {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-shut,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle span {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle {
justify-content: center;
padding-inline: 0;
}
.sdb-11__nav {
flex: 1;
min-block-size: 0;
padding: 0.2rem 0.6rem;
}
.sdb-11__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.15rem;
}
.sdb-11__link {
display: flex;
align-items: center;
gap: 0.75rem;
min-block-size: 2.75rem;
padding: 0.5rem 0.7rem;
border-radius: 1.4rem;
color: var(--sdb-11-ink);
text-decoration: none;
font-size: 0.92rem;
white-space: nowrap;
min-inline-size: 0;
}
.sdb-11__glyph {
flex: none;
font-size: 0.6rem;
color: var(--sdb-11-mut);
}
.sdb-11__link:hover {
background: var(--sdb-11-surf);
}
.sdb-11__link[aria-current="page"] {
background: var(--sdb-11-acc);
color: var(--sdb-11-surf);
font-weight: 650;
}
.sdb-11__link[aria-current="page"] .sdb-11__glyph {
color: inherit;
}
.sdb-11__link:focus-visible {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__rail-foot {
margin: 0;
padding: 0.7rem 1rem 0.9rem;
font-size: 0.78rem;
color: var(--sdb-11-mut);
white-space: nowrap;
overflow: hidden;
}
.sdb-11__label {
min-inline-size: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__label {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__link {
justify-content: center;
gap: 0;
}
.sdb-11__main {
flex: 1;
min-inline-size: 0;
padding: 1.5rem 1.25rem 2.5rem;
}
@media (min-width: 46rem) {
.sdb-11__main {
padding: 2.4rem 2.25rem 3rem;
}
}
.sdb-11__main-h {
margin: 0 0 1.2rem;
font-family: var(--sdb-11-font-display);
font-size: clamp(1.35rem, 3.4vw, 1.9rem);
line-height: 1.15;
}
.sdb-11__fields {
margin: 0;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.6rem;
max-inline-size: 44rem;
}
@media (min-width: 34rem) {
.sdb-11__fields {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.sdb-11__fields div {
min-inline-size: 0;
padding: 0.75rem 0.9rem;
border-radius: 0.9rem;
background: var(--sdb-11-surf2);
}
.sdb-11__fields dt {
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sdb-11-mut);
}
.sdb-11__fields dd {
margin: 0.15rem 0 0;
font-size: 0.92rem;
}
@media (max-width: 30rem) {
.sdb-11__app {
flex-direction: column;
}
.sdb-11__rail,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__rail {
inline-size: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.sdb-11__rail {
transition: none;
}
}
```Here's a working CSS Sidebar Navigation 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: Collapsible Sidebar with Checkbox State
Source: https://codefronts.com/navigation/css-sidebar-navigation/pure-css-collapsible-sidebar-navigation-snippet/
The no-script collapse: a visually hidden checkbox, :has(:checked) on the root, and one width custom property doing all the work. One honest limitation: a checkbox cannot carry aria-expanded, so the pattern needs a clearly labelled control, and the scripted version in demo 05 is the accessible ceiling. css-side-menu covers the look; this is the state mechanism under it.
## HTML
```html
<section class="sdb-11" aria-label="Vale account settings">
<div class="sdb-11__theme">
<input class="sdb-11__theme-input" type="checkbox" id="sdb-11-theme">
<label class="sdb-11__theme-label" for="sdb-11-theme">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M12 3.5a8.5 8.5 0 0 0 0 17Z" fill="currentColor"/></svg>
<span class="sdb-11__sr">Toggle dark theme</span>
</label>
</div>
<div class="sdb-11__app">
<input class="sdb-11__state" type="checkbox" id="sdb-11-collapse">
<aside class="sdb-11__rail">
<div class="sdb-11__rail-head">
<label class="sdb-11__toggle" for="sdb-11-collapse">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 6h16M4 12h16M4 18h16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<span class="sdb-11__toggle-open">Collapse</span>
<span class="sdb-11__toggle-shut">Expand</span>
</label>
</div>
<nav class="sdb-11__nav" aria-label="Vale account settings">
<ul class="sdb-11__list">
<li><a class="sdb-11__link" href="#sdb-11-main" aria-current="page"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Profile</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Notifications</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Security</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">Billing</span></a></li>
<li><a class="sdb-11__link" href="#sdb-11-main"><span class="sdb-11__glyph" aria-hidden="true">●</span><span class="sdb-11__label">API keys</span></a></li>
</ul>
</nav>
<p class="sdb-11__rail-foot"><span class="sdb-11__label">Signed in as Priya Sharma</span></p>
</aside>
<main class="sdb-11__main" id="sdb-11-main">
<h2 class="sdb-11__main-h">Profile</h2>
<dl class="sdb-11__fields">
<div><dt>Display name</dt><dd>Priya Sharma</dd></div>
<div><dt>Workspace</dt><dd>Vale · Operations</dd></div>
<div><dt>Time zone</dt><dd>Europe/Lisbon (UTC+1)</dd></div>
<div><dt>Two-factor</dt><dd>Authenticator app</dd></div>
<div><dt>Recovery email</dt><dd>p.sharma@vale.example</dd></div>
<div><dt>Member since</dt><dd>March 2024</dd></div>
</dl>
</main>
</div>
</section>
```
## CSS
```css
.sdb-11 {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
--sdb-11-w-open: 15rem;
--sdb-11-w-rail: 4.5rem;
--sdb-11-w: var(--sdb-11-w-open);
--sdb-11-font-display: "Avenir Next Rounded", Nunito, "Trebuchet MS", ui-rounded, system-ui, sans-serif;
width: 100%;
min-height: 100vh;
display: block;
position: relative;
box-sizing: border-box;
background: var(--sdb-11-bg);
color: var(--sdb-11-ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.55;
}
.sdb-11 *,
.sdb-11 *::before,
.sdb-11 *::after {
box-sizing: border-box;
}
.sdb-11:has(.sdb-11__state:checked) {
--sdb-11-w: var(--sdb-11-w-rail);
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
@media (prefers-color-scheme: dark) {
.sdb-11 {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #f5f1ee;
--sdb-11-surf: #fffcfa;
--sdb-11-surf2: #ebe3dd;
--sdb-11-line: #d9cdc4;
--sdb-11-ink: #241c17;
--sdb-11-mut: #6d5c52;
--sdb-11-acc: #7d4a35;
--sdb-11-acc: oklch(46% 0.09 40);
--sdb-11-acc-soft: #eee0d8;
}
}
@media (prefers-color-scheme: light) {
.sdb-11:has(.sdb-11__theme-input:checked) {
--sdb-11-bg: #14100e;
--sdb-11-surf: #1c1714;
--sdb-11-surf2: #251e1a;
--sdb-11-line: #362c26;
--sdb-11-ink: #f1e9e3;
--sdb-11-mut: #a89689;
--sdb-11-acc: #dda183;
--sdb-11-acc: oklch(78% 0.09 45);
--sdb-11-acc-soft: #32241d;
}
}
.sdb-11__theme {
position: absolute;
inset-block-start: 0.85rem;
inset-inline-end: 0.85rem;
z-index: 60;
}
.sdb-11__theme-input {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
}
.sdb-11__theme-label {
display: grid;
place-items: center;
inline-size: 2.75rem;
block-size: 2.75rem;
border-radius: 1.4rem;
border: 0;
background: var(--sdb-11-surf2);
color: var(--sdb-11-ink);
cursor: pointer;
}
.sdb-11__theme-label svg {
inline-size: 1.15rem;
block-size: 1.15rem;
}
.sdb-11__theme-input:focus-visible + .sdb-11__theme-label {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__sr,
.sdb-11__state {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
margin: 0;
}
.sdb-11__app {
display: flex;
min-inline-size: 0;
min-block-size: 100vh;
background: var(--sdb-11-surf);
}
.sdb-11__rail {
flex: none;
inline-size: var(--sdb-11-w);
min-inline-size: 0;
display: flex;
flex-direction: column;
background: var(--sdb-11-surf2);
transition: inline-size 200ms ease;
}
.sdb-11__rail-head {
padding: 0.6rem;
}
.sdb-11__toggle {
display: flex;
align-items: center;
gap: 0.6rem;
min-block-size: 2.75rem;
padding-inline: 0.7rem;
border-radius: 1.4rem;
background: var(--sdb-11-surf);
cursor: pointer;
font-size: 0.86rem;
font-weight: 600;
white-space: nowrap;
min-inline-size: 2.75rem;
}
.sdb-11__toggle svg {
inline-size: 1.15rem;
block-size: 1.15rem;
flex: none;
}
.sdb-11__state:focus-visible ~ .sdb-11__rail .sdb-11__toggle {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__toggle-shut {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-open {
display: none;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle-shut,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle span {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__toggle {
justify-content: center;
padding-inline: 0;
}
.sdb-11__nav {
flex: 1;
min-block-size: 0;
padding: 0.2rem 0.6rem;
}
.sdb-11__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.15rem;
}
.sdb-11__link {
display: flex;
align-items: center;
gap: 0.75rem;
min-block-size: 2.75rem;
padding: 0.5rem 0.7rem;
border-radius: 1.4rem;
color: var(--sdb-11-ink);
text-decoration: none;
font-size: 0.92rem;
white-space: nowrap;
min-inline-size: 0;
}
.sdb-11__glyph {
flex: none;
font-size: 0.6rem;
color: var(--sdb-11-mut);
}
.sdb-11__link:hover {
background: var(--sdb-11-surf);
}
.sdb-11__link[aria-current="page"] {
background: var(--sdb-11-acc);
color: var(--sdb-11-surf);
font-weight: 650;
}
.sdb-11__link[aria-current="page"] .sdb-11__glyph {
color: inherit;
}
.sdb-11__link:focus-visible {
outline: 2px solid var(--sdb-11-acc);
outline-offset: 2px;
}
.sdb-11__rail-foot {
margin: 0;
padding: 0.7rem 1rem 0.9rem;
font-size: 0.78rem;
color: var(--sdb-11-mut);
white-space: nowrap;
overflow: hidden;
}
.sdb-11__label {
min-inline-size: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__label {
position: absolute;
inline-size: 1px;
block-size: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.sdb-11:has(.sdb-11__state:checked) .sdb-11__link {
justify-content: center;
gap: 0;
}
.sdb-11__main {
flex: 1;
min-inline-size: 0;
padding: 1.5rem 1.25rem 2.5rem;
}
@media (min-width: 46rem) {
.sdb-11__main {
padding: 2.4rem 2.25rem 3rem;
}
}
.sdb-11__main-h {
margin: 0 0 1.2rem;
font-family: var(--sdb-11-font-display);
font-size: clamp(1.35rem, 3.4vw, 1.9rem);
line-height: 1.15;
}
.sdb-11__fields {
margin: 0;
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.6rem;
max-inline-size: 44rem;
}
@media (min-width: 34rem) {
.sdb-11__fields {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.sdb-11__fields div {
min-inline-size: 0;
padding: 0.75rem 0.9rem;
border-radius: 0.9rem;
background: var(--sdb-11-surf2);
}
.sdb-11__fields dt {
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--sdb-11-mut);
}
.sdb-11__fields dd {
margin: 0.15rem 0 0;
font-size: 0.92rem;
}
@media (max-width: 30rem) {
.sdb-11__app {
flex-direction: column;
}
.sdb-11__rail,
.sdb-11:has(.sdb-11__state:checked) .sdb-11__rail {
inline-size: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
.sdb-11__rail {
transition: none;
}
}
```How this works
The whole mechanism is one selector and one custom property. A checkbox sits visually hidden inside the rail; its <label> is the button a user sees. When the box is checked, :has(.sdb-11__state:checked) matches on the root and --sdb-11-w drops from 15rem to 4.5rem. Everything else — the clipped labels, the centred icons, the rotated chevron — reads from that same state. There is no script and nothing to initialise.
What you give up is the state announcement. A checkbox has a checked state, but "checked" is not "expanded", and there is no way to put aria-expanded on a checkbox that means anything to assistive technology. So a screen-reader user gets a checkbox called "Collapse sidebar" and no announcement of the sidebar's state as such. That is a real limitation, not a rounding error — and it is why demo 05 exists.
The pattern is still worth having, in two specific situations. Static sites with no JavaScript at all, and a first-paint-correct default before the app's own script has loaded. In both, a clearly labelled control — the label text says what it does, and it changes with the state — carries most of the weight. What it must not do is hide the labels with display: none, which would strip the accessible names on top of the missing state.
The trap: the checkbox has to be reachable and it has to look focused. Hiding it with display: none or visibility: hidden removes it from the tab order, so the collapse becomes mouse-only. Clip it instead, keep it in the DOM, and draw the focus ring on the label via :focus-visible on the input — otherwise keyboard users get a control they can activate but cannot see.
Make it yours
- Change the two widths in
--sdb-11-w-openand--sdb-11-w-rail; every dependent rule reads the same token. - Flip the default to collapsed by adding
checkedto the input — useful when the rail is secondary to the content. - Move the label to the top bar instead of the rail head, and point its
forat the same id. - Swap the width interpolation for a translate if you want an overlay collapse rather than a layout one; the state selector does not change.
- Add a second checkbox for a pinned state if you need three widths, and switch on the combination with
:has(). - Upgrade to the scripted version from demo 05 when you can — same visual, plus a real
aria-expanded.
Gotchas — read before shipping
- A checkbox cannot express
aria-expanded, so the collapsed state is never announced. Label the control clearly and treat this as the ceiling of the pure-CSS approach. - Hiding the input with
display: nonetakes it out of the tab order and makes the collapse mouse-only. Clip it with the visually-hidden pattern instead. - Hiding the item labels with
display: nonewhen collapsed strips their accessible names, leaving a rail of unnamed links. - Shrinking the row height along with the width to make the collapsed rail look tighter drops the hit targets below 44px. Only the width should change.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 121+ | 111+ |
:has() sets the floor for the collapse itself (Chrome 105 / Safari 15.4 / Firefox 121) and oklch() raises Chrome to 111. There is no graceful partial here: without :has() the rail simply stays at its expanded width, which is a usable default rather than a broken layout. Colour degrades to the sRGB hex declared before every oklch() value.