04 / 20
All 20 in 20 Tailwind CSS Sidebars
- 01Tailwind Accessible Sidebar Navigation ARIA Reference
- 02Tailwind Sidebar Focus Trap and Restore on Close
- 03Tailwind Inert Background Content Behind a Drawer
- 04Tailwind Sidebar Navigation Is Not a Tab List
- 05Tailwind Announcing Collapsed and Expanded Sidebar State
- 06Tailwind Roving Tabindex for Sidebar Keyboard Navigation
- 07Tailwind Persisting Sidebar Collapse State Across Reloads
- 08Tailwind Sidebar Scroll Containment with Overscroll Behavior
- 09Tailwind Nested Submenu Keyboard and Screen Reader Behavior
- 10Tailwind Resizable Sidebar with a Keyboard Accessible Handle
- 11Pure Tailwind Collapsible Sidebar with Checkbox State
- 12Tailwind Responsive Sidebar Breakpoint Behavior
- 13Pure Tailwind Icon Rail Sidebar with Accessible Labels
- 14Tailwind Dashboard Sidebar with Current Page State
- 15Pure Tailwind Multi Level Sidebar Menu Disclosure Pattern
- 16Pure Tailwind Accordion Sidebar with Details and Summary
- 17Pure Tailwind Sticky Full Height Sidebar Layout Mechanics
- 18Pure Tailwind Floating Sidebar Stacking and Overlay Order
- 19Tailwind Off Canvas Sidebar Drawer with Focus Return
- 20Pure Tailwind Sidebar Footer Pinning with Scrollable Nav
Tailwind Sidebar Navigation Is Not a Tab List
The boundary marker. A sidebar is a list of links that navigate; a tab list is a set of controls that swap a panel in place, and the two have different roles, different attributes and different keyboard contracts. This is the correct build — <nav>, <ul>, <a>, aria-current="page" — with every visual decision in utilities and not one line of script.
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="tsb-04 block relative w-full min-h-svh bg-tsb04-bg text-tsb04-ink font-tsb04-sans leading-[1.55]" aria-label="Quill learning centre">
<div class="absolute top-3.5 right-3.5 z-60">
<input class="tsb-04-theme-input peer absolute inset-0 w-full h-full m-0 opacity-0 cursor-pointer" type="checkbox" id="tsb-04-theme">
<label class="grid place-items-center w-11 h-11 border-0 bg-tsb04-surf2 text-tsb04-ink cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-tsb04-acc peer-focus-visible:outline-offset-2" for="tsb-04-theme">
<svg class="w-4.5 h-4.5" 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="absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">Toggle dark theme</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] min-h-svh r46:grid-cols-[15rem_minmax(0,1fr)]">
<nav class="min-w-0 px-2 py-4 bg-tsb04-surf border-b border-tsb04-line r46:border-b-0 r46:border-r r46:border-tsb04-line" aria-label="Quill learning centre">
<p class="m-0 mb-3.5 px-3 font-tsb04-display text-[1.05rem] font-bold tracking-[0.18em] uppercase">Quill</p>
<ul class="list-none m-0 p-0 grid gap-[2px]">
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main" aria-current="page">Getting started</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Deployments</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Billing</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Audit log</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Integrations</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Certification</a></li>
</ul>
</nav>
<main class="min-w-0 content-start px-5 pt-6 pb-10 r46:px-9 r46:pt-10 r46:pb-12" id="tsb-04-main">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.14em] uppercase text-tsb04-mut">Course · 6 lessons</p>
<h2 class="m-0 mb-5.5 font-tsb04-display text-[clamp(1.6rem,4.4vw,2.4rem)] leading-[1.05] uppercase">Getting started</h2>
<div class="grid grid-cols-[minmax(0,1fr)] gap-[2px] bg-tsb04-line border border-tsb04-line r52:grid-cols-2">
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">01</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Create your first workspace</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">8 min · video</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">02</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Invite a teammate and set roles</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">5 min · reading</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">03</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Connect a repository</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">11 min · walkthrough</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">04</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Ship a preview environment</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">14 min · walkthrough</p>
</article>
</div>
</main>
</div>
</section><section class="tsb-04 block relative w-full min-h-svh bg-tsb04-bg text-tsb04-ink font-tsb04-sans leading-[1.55]" aria-label="Quill learning centre">
<div class="absolute top-3.5 right-3.5 z-60">
<input class="tsb-04-theme-input peer absolute inset-0 w-full h-full m-0 opacity-0 cursor-pointer" type="checkbox" id="tsb-04-theme">
<label class="grid place-items-center w-11 h-11 border-0 bg-tsb04-surf2 text-tsb04-ink cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-tsb04-acc peer-focus-visible:outline-offset-2" for="tsb-04-theme">
<svg class="w-4.5 h-4.5" 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="absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">Toggle dark theme</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] min-h-svh r46:grid-cols-[15rem_minmax(0,1fr)]">
<nav class="min-w-0 px-2 py-4 bg-tsb04-surf border-b border-tsb04-line r46:border-b-0 r46:border-r r46:border-tsb04-line" aria-label="Quill learning centre">
<p class="m-0 mb-3.5 px-3 font-tsb04-display text-[1.05rem] font-bold tracking-[0.18em] uppercase">Quill</p>
<ul class="list-none m-0 p-0 grid gap-[2px]">
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main" aria-current="page">Getting started</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Deployments</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Billing</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Audit log</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Integrations</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Certification</a></li>
</ul>
</nav>
<main class="min-w-0 content-start px-5 pt-6 pb-10 r46:px-9 r46:pt-10 r46:pb-12" id="tsb-04-main">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.14em] uppercase text-tsb04-mut">Course · 6 lessons</p>
<h2 class="m-0 mb-5.5 font-tsb04-display text-[clamp(1.6rem,4.4vw,2.4rem)] leading-[1.05] uppercase">Getting started</h2>
<div class="grid grid-cols-[minmax(0,1fr)] gap-[2px] bg-tsb04-line border border-tsb04-line r52:grid-cols-2">
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">01</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Create your first workspace</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">8 min · video</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">02</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Invite a teammate and set roles</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">5 min · reading</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">03</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Connect a repository</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">11 min · walkthrough</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">04</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Ship a preview environment</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">14 min · walkthrough</p>
</article>
</div>
</main>
</div>
</section>@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo+Narrow:wght@400;600;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Archivo+Narrow:wght@400;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The whole active treatment — tint, colour, weight, inset marker — is four
aria-[current=page]:utilities on the link. No active class exists, so none can drift from the attribute. box-shadow: inset 3px 0 0ports asshadow-[inset_3px_0_0_var(--color-tsb04-acc)], reading the same token the text colour reads.- Two
--breakpoint-*entries in@thememintr46:andr52:, keeping 46rem and 52rem instead of snapping the layout tomdandlg. - The hairline grid is
gap-[2px]over abg-tsb04-linewrapper — in utilities this reads more clearly than the CSS version, because the gap value and the rule colour sit on the same two elements. - Nothing here needs a
group: every state in this demo belongs to the element that displays it, which is what makes the zero-JS tag honest. - The stylesheet contains only tokens, the root contract and the theme flip. This is the shape every demo in the collection converges on.
Here's a working CSS demo 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: Tailwind Sidebar Navigation Is Not a Tab List
Source: https://codefronts.com/navigation/tailwind-css-sidebars/tailwind-sidebar-navigation-is-not-a-tab-list/
The boundary marker. A sidebar is a list of links that navigate; a tab list is a set of controls that swap a panel in place, and the two have different roles, different attributes and different keyboard contracts. This is the correct build — <nav>, <ul>, <a>, aria-current="page" — with every visual decision in utilities and not one line of script.
## HTML
```html
<section class="tsb-04 block relative w-full min-h-svh bg-tsb04-bg text-tsb04-ink font-tsb04-sans leading-[1.55]" aria-label="Quill learning centre">
<div class="absolute top-3.5 right-3.5 z-60">
<input class="tsb-04-theme-input peer absolute inset-0 w-full h-full m-0 opacity-0 cursor-pointer" type="checkbox" id="tsb-04-theme">
<label class="grid place-items-center w-11 h-11 border-0 bg-tsb04-surf2 text-tsb04-ink cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-tsb04-acc peer-focus-visible:outline-offset-2" for="tsb-04-theme">
<svg class="w-4.5 h-4.5" 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="absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">Toggle dark theme</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] min-h-svh r46:grid-cols-[15rem_minmax(0,1fr)]">
<nav class="min-w-0 px-2 py-4 bg-tsb04-surf border-b border-tsb04-line r46:border-b-0 r46:border-r r46:border-tsb04-line" aria-label="Quill learning centre">
<p class="m-0 mb-3.5 px-3 font-tsb04-display text-[1.05rem] font-bold tracking-[0.18em] uppercase">Quill</p>
<ul class="list-none m-0 p-0 grid gap-[2px]">
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main" aria-current="page">Getting started</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Deployments</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Billing</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Audit log</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Integrations</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Certification</a></li>
</ul>
</nav>
<main class="min-w-0 content-start px-5 pt-6 pb-10 r46:px-9 r46:pt-10 r46:pb-12" id="tsb-04-main">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.14em] uppercase text-tsb04-mut">Course · 6 lessons</p>
<h2 class="m-0 mb-5.5 font-tsb04-display text-[clamp(1.6rem,4.4vw,2.4rem)] leading-[1.05] uppercase">Getting started</h2>
<div class="grid grid-cols-[minmax(0,1fr)] gap-[2px] bg-tsb04-line border border-tsb04-line r52:grid-cols-2">
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">01</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Create your first workspace</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">8 min · video</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">02</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Invite a teammate and set roles</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">5 min · reading</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">03</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Connect a repository</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">11 min · walkthrough</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">04</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Ship a preview environment</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">14 min · walkthrough</p>
</article>
</div>
</main>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}
```Here's a working CSS demo 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: Tailwind Sidebar Navigation Is Not a Tab List
Source: https://codefronts.com/navigation/tailwind-css-sidebars/tailwind-sidebar-navigation-is-not-a-tab-list/
The boundary marker. A sidebar is a list of links that navigate; a tab list is a set of controls that swap a panel in place, and the two have different roles, different attributes and different keyboard contracts. This is the correct build — <nav>, <ul>, <a>, aria-current="page" — with every visual decision in utilities and not one line of script.
## HTML
```html
<section class="tsb-04 block relative w-full min-h-svh bg-tsb04-bg text-tsb04-ink font-tsb04-sans leading-[1.55]" aria-label="Quill learning centre">
<div class="absolute top-3.5 right-3.5 z-60">
<input class="tsb-04-theme-input peer absolute inset-0 w-full h-full m-0 opacity-0 cursor-pointer" type="checkbox" id="tsb-04-theme">
<label class="grid place-items-center w-11 h-11 border-0 bg-tsb04-surf2 text-tsb04-ink cursor-pointer peer-focus-visible:outline-2 peer-focus-visible:outline-tsb04-acc peer-focus-visible:outline-offset-2" for="tsb-04-theme">
<svg class="w-4.5 h-4.5" 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="absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">Toggle dark theme</span>
</label>
</div>
<div class="grid grid-cols-[minmax(0,1fr)] min-h-svh r46:grid-cols-[15rem_minmax(0,1fr)]">
<nav class="min-w-0 px-2 py-4 bg-tsb04-surf border-b border-tsb04-line r46:border-b-0 r46:border-r r46:border-tsb04-line" aria-label="Quill learning centre">
<p class="m-0 mb-3.5 px-3 font-tsb04-display text-[1.05rem] font-bold tracking-[0.18em] uppercase">Quill</p>
<ul class="list-none m-0 p-0 grid gap-[2px]">
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main" aria-current="page">Getting started</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Deployments</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Billing</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Audit log</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Integrations</a></li>
<li><a class="flex items-center min-w-0 min-h-11 px-3 py-2 bg-tsb04-surf text-[0.92rem] text-tsb04-ink no-underline hover:bg-tsb04-accsoft focus-visible:outline-2 focus-visible:outline-tsb04-acc focus-visible:-outline-offset-2 aria-[current=page]:bg-tsb04-accsoft aria-[current=page]:text-tsb04-acc aria-[current=page]:font-semibold aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]" href="#tsb-04-main">Certification</a></li>
</ul>
</nav>
<main class="min-w-0 content-start px-5 pt-6 pb-10 r46:px-9 r46:pt-10 r46:pb-12" id="tsb-04-main">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.14em] uppercase text-tsb04-mut">Course · 6 lessons</p>
<h2 class="m-0 mb-5.5 font-tsb04-display text-[clamp(1.6rem,4.4vw,2.4rem)] leading-[1.05] uppercase">Getting started</h2>
<div class="grid grid-cols-[minmax(0,1fr)] gap-[2px] bg-tsb04-line border border-tsb04-line r52:grid-cols-2">
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">01</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Create your first workspace</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">8 min · video</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">02</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Invite a teammate and set roles</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">5 min · reading</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">03</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Connect a repository</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">11 min · walkthrough</p>
</article>
<article class="min-w-0 p-4 bg-tsb04-surf">
<p class="m-0 mb-1.5 font-tsb04-display text-[0.8rem] tracking-[0.12em] text-tsb04-acc">04</p>
<h3 class="m-0 mb-1 text-base leading-[1.25]">Ship a preview environment</h3>
<p class="m-0 text-[0.83rem] text-tsb04-mut">14 min · walkthrough</p>
</article>
</div>
</main>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-bg: oklch(96% 0.002 285);
--color-tsb04-surf: #ffffff;
--color-tsb04-surf: oklch(100% 0 0);
--color-tsb04-surf2: #ebebed;
--color-tsb04-surf2: oklch(93% 0.003 285);
--color-tsb04-line: #d4d4d8;
--color-tsb04-line: oklch(87% 0.005 285);
--color-tsb04-ink: #18181b;
--color-tsb04-ink: oklch(21% 0.005 285);
--color-tsb04-mut: #5f5f68;
--color-tsb04-mut: oklch(48% 0.01 285);
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
--color-tsb04-accsoft: oklch(94% 0.035 291);
--font-tsb04-sans: system-ui, -apple-system, 'Segoe UI', sans-serif;
--font-tsb04-display: 'Archivo Narrow', 'Roboto Condensed', ui-sans-serif, system-ui, sans-serif;
/* Custom breakpoints, so the port keeps the CSS version's own numbers rather than
being re-tuned to Tailwind's md/lg scale. */
--breakpoint-r46: 46rem;
--breakpoint-r52: 52rem;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.tsb-04 {
width: 100%;
min-height: 100svh;
}
.tsb-04 *,
.tsb-04 *::before,
.tsb-04 *::after {
box-sizing: border-box;
}
/* Token flip for the pure-CSS theme toggle, plus the two mandatory mirror blocks. */
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
@media (prefers-color-scheme: dark) {
.tsb-04 {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #f4f4f5;
--color-tsb04-surf: #ffffff;
--color-tsb04-surf2: #ebebed;
--color-tsb04-line: #d4d4d8;
--color-tsb04-ink: #18181b;
--color-tsb04-mut: #5f5f68;
--color-tsb04-acc: #6d3ce0;
--color-tsb04-acc: oklch(52% 0.22 291);
--color-tsb04-accsoft: #ece4fd;
}
}
@media (prefers-color-scheme: light) {
.tsb-04:has(.tsb-04-theme-input:checked) {
--color-tsb04-bg: #0e0e11;
--color-tsb04-surf: #17171b;
--color-tsb04-surf2: #1f1f25;
--color-tsb04-line: #2c2c33;
--color-tsb04-ink: #f2f2f5;
--color-tsb04-mut: #9d9da8;
--color-tsb04-acc: #b79bff;
--color-tsb04-acc: oklch(80% 0.13 292);
--color-tsb04-accsoft: #241a3d;
}
}
```How this works
What makes this navigation rather than tabs is three things, and none of them is visual. The links are real <a href> elements inside a named <nav> landmark. The active one carries aria-current="page", not aria-selected. And Tab reaches every item, because a list of links has no roving tabindex — that is a tab list's contract, not a navigation's.
The port makes the attribute impossible to forget. Every piece of the active treatment is a variant on the same attribute:
class="… aria-[current=page]:bg-tsb04-accsoft
aria-[current=page]:text-tsb04-acc
aria-[current=page]:font-semibold
aria-[current=page]:shadow-[inset_3px_0_0_var(--color-tsb04-acc)]"There is no active class to add, so there is nothing that can be styled-but-unannounced.
Why custom breakpoints instead of md:. The rail appears at 46rem and the lesson grid splits at 52rem. Tailwind's defaults are 48rem and 64rem, and quietly re-tuning a layout to fit the framework's scale is how a port loses its original design. Two @theme entries keep both numbers and read better in the markup than arbitrary min-[46rem]: variants repeated a dozen times.
Zero script, permanently. There is no state in this demo beyond the current page, which the server or router already knows.
Make it yours
- Change the rail width in one place:
r46:grid-cols-[15rem_minmax(0,1fr)]. - Swap the inset marker for a left border by replacing the
shadow-[inset_…]utility withborder-l-2 aria-[current=page]:border-l-tsb04-acc. - Retheme through
--color-tsb04-accand--color-tsb04-accsoft. - Change the display face in
--font-tsb04-displayplus the font<link>; the condensed uppercase treatment is the demo's only decorative move. - Turn the hairline grid solid by deleting
gap-[2px]and the wrapper'sbg-tsb04-line. - If you genuinely need tabs, use
role="tablist",aria-selectedand arrow-key roving focus — demo 06 has the roving-focus mechanics.
Gotchas — read before shipping
aria-selectedon a navigation link tells a screen-reader user that activating it will swap a panel in place. It is the single most common wrong attribute in a sidebar.- The bare
aria-current:variant tests="true". For a link the value ispage, soaria-[current=page]:is the one that matches. - Do not add roving tabindex to a list of links. Removing items from the tab order to be “more like tabs” makes the sidebar harder to use, not more accessible.
role="tab"without the full keyboard implementation is worse than no role at all — the announcement promises behaviour that does not exist.- Custom breakpoints are
--breakpoint-*in@theme, not--screen-*; the wrong prefix mints no variant and everyr46:class silently produces nothing. - Styling the active item from a class while announcing it from an attribute gives two states that can disagree, and the announced one is the one that matters.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4's generated sheet sets the floor; the ARIA and the semantics have no version floor at all, and the layout is grid, Baseline 2017. Engines below the oklch() floor take the sRGB hex declared first on every token and keep the operating-system colour scheme instead of the toggle.