02 / 17
All 17 in 17 Tailwind CSS Dashboard Layouts
- 01Tailwind CSS Bento Grid Dashboard Layout
- 02Tailwind CSS Collapsible Sidebar Admin Dashboard
- 03Tailwind CSS Dashboard KPI Stat Cards Grid
- 04Tailwind CSS Master-Detail Split View Inbox Layout
- 05Tailwind CSS Fixed Header Dashboard with Independent Scroll Panels
- 06Tailwind CSS Grid Template Areas Dashboard Layout
- 07Tailwind CSS Responsive Off-Canvas Sidebar Drawer Dashboard
- 08Tailwind CSS Container Query Responsive Dashboard Cards
- 09Tailwind CSS E-commerce Admin Dashboard with Bulk Actions
- 10Tailwind CSS Dark Mode Dashboard with Light / Dark / System Toggle
- 11Tailwind CSS Multi-Level Nested Sidebar Navigation Layout
- 12Minimal Tailwind CSS Admin Dashboard Template (No JavaScript)
- 13Tailwind CSS AI Copilot Chat Sidebar Dashboard Layout
- 14Tailwind CSS Data Table with Sticky Header and Sticky First Column
- 15Tailwind CSS Masonry Grid Dashboard Layout (Columns vs Grid Dense)
- 16Tailwind CSS Full-Screen Analytics Dashboard (100vh, No Scroll)
- 17Tailwind CSS Top Navigation Bar Dashboard Layout with Tabs
Tailwind CSS Collapsible Sidebar Admin Dashboard
The canonical SaaS shell: top bar, a sidebar that folds from 236px to a 68px icon rail, and a fluid main area. The fold is w-59 → group-data-collapsed/shell:w-17, labels fade with group-data-collapsed/shell:opacity-0, and rail tooltips are generated by after:content-[attr(data-label)]. The script sets one attribute — it never touches a class — and now remembers the choice and answers the [ shortcut.
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="tdl-02 w-full min-h-svh bg-tdl02-bg font-tdl02-sans text-tdl02-ink antialiased" aria-label="Collapsible sidebar admin shell demo">
<div data-shell class="group/shell @container/shell flex h-svh min-h-[34rem] flex-col">
<header class="flex h-15 flex-none items-center gap-3 border-b border-tdl02-edge bg-tdl02-panel px-3 sm:px-4">
<button data-toggle type="button" aria-expanded="true" aria-controls="tdl02-nav" aria-label="Collapse navigation" class="group/btn relative grid size-11 place-items-center rounded-xl text-tdl02-ink transition-colors hover:bg-tdl02-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-5 fill-none stroke-current stroke-2 [stroke-linecap:round]" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/></svg>
<span aria-hidden="true" class="pointer-events-none absolute top-[calc(100%+6px)] left-0 z-20 flex items-center gap-1.5 rounded-lg bg-tdl02-ink px-2.5 py-1.5 text-xs font-semibold whitespace-nowrap text-white opacity-0 transition-opacity group-hover/btn:opacity-100 group-focus-visible/btn:opacity-100">Toggle sidebar<kbd class="rounded border border-white/25 px-1.5 font-sans text-[10px]">[</kbd></span>
</button>
<strong class="flex items-center gap-2.5 text-[15px] font-bold tracking-tight"><span aria-hidden="true" class="size-6 rounded-lg bg-[conic-gradient(from_200deg,var(--color-tdl02-acc),var(--color-tdl02-acc2),var(--color-tdl02-acc))]"></span>Ledgerly</strong>
<label class="ml-auto flex h-10 w-[min(18rem,40%)] items-center gap-2 rounded-full border border-tdl02-edge bg-tdl02-bg px-3.5 text-[13px] text-tdl02-mut transition focus-within:border-tdl02-acc focus-within:ring-3 focus-within:ring-tdl02-acc/15 @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-3.5 flex-none fill-none stroke-current stroke-2" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<span class="sr-only">Search</span><input type="search" placeholder="Search orders, customers…" class="min-w-0 flex-1 bg-transparent text-tdl02-ink outline-none placeholder:text-tdl02-mut">
<kbd class="rounded-md border border-tdl02-edge bg-tdl02-panel px-1.5 py-0.5 font-sans text-[10px] font-semibold">⌘K</kbd>
</label>
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-linear-140 from-tdl02-acc to-tdl02-acc3 text-[11px] font-bold text-white @max-[45rem]/shell:ml-auto">JD</span>
</header>
<div class="flex min-h-0 flex-1">
<nav id="tdl02-nav" aria-label="Primary" class="relative z-10 flex w-59 flex-none flex-col gap-1 border-r border-tdl02-edge bg-tdl02-panel p-2.5 transition-[width] duration-300 ease-[cubic-bezier(.2,.7,.2,1)] group-data-collapsed/shell:w-17 @max-[45rem]/shell:w-17 [&>a]:relative [&>a]:flex [&>a]:h-11 [&>a]:items-center [&>a]:gap-3 [&>a]:rounded-xl [&>a]:px-3.25 [&>a]:text-[13.5px] [&>a]:font-semibold [&>a]:whitespace-nowrap [&>a]:text-tdl02-mut [&>a]:no-underline [&>a]:transition-colors [&>a:hover]:bg-tdl02-hover [&>a:hover]:text-tdl02-ink [&>a:focus-visible]:outline-2 [&>a:focus-visible]:-outline-offset-2 [&>a:focus-visible]:outline-tdl02-acc [&>a]:after:pointer-events-none [&>a]:after:absolute [&>a]:after:top-1/2 [&>a]:after:left-[calc(100%+12px)] [&>a]:after:-translate-y-1/2 [&>a]:after:scale-95 [&>a]:after:rounded-lg [&>a]:after:bg-tdl02-ink [&>a]:after:px-2.75 [&>a]:after:py-1.5 [&>a]:after:text-xs [&>a]:after:text-white [&>a]:after:opacity-0 [&>a]:after:shadow-lg [&>a]:after:transition [&>a]:after:content-[attr(data-label)] group-data-collapsed/shell:[&>a:hover]:after:scale-100 group-data-collapsed/shell:[&>a:hover]:after:opacity-100 group-data-collapsed/shell:[&>a:focus-visible]:after:opacity-100 @max-[45rem]/shell:[&>a:hover]:after:opacity-100 [&>a[aria-current=page]]:bg-tdl02-acc/10 [&>a[aria-current=page]]:text-tdl02-acc">
<p class="px-3.25 pt-1 pb-1.5 text-[10.5px] font-bold tracking-[0.12em] whitespace-nowrap text-tdl02-mut uppercase transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Workspace</p>
<a href="#overview" data-label="Overview" aria-current="page"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3 12 12 4l9 8"/><path d="M5 10v10h14V10"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Overview</span></a>
<a href="#orders" data-label="Orders · 12"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 7h16l-1.5 12h-13Z"/><path d="M8 7a4 4 0 0 1 8 0"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Orders</span><b class="ml-auto rounded-full bg-tdl02-acc px-2 py-0.5 text-[10.5px] text-white transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">12</b><i aria-hidden="true" class="absolute top-2.5 left-8 size-2 rounded-full bg-tdl02-acc opacity-0 ring-2 ring-tdl02-panel transition-opacity group-data-collapsed/shell:opacity-100 @max-[45rem]/shell:opacity-100"></i></a>
<a href="#customers" data-label="Customers"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M17.5 13.6a6.5 6.5 0 0 1 4 6.4"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Customers</span></a>
<a href="#reports" data-label="Reports"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V10M12 20V4M19 20v-7"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Reports</span></a>
<a href="#billing" data-label="Billing"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Billing</span></a>
<span aria-hidden="true" class="flex-1"></span>
<a href="#settings" data-label="Settings"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Settings</span></a>
</nav>
<main class="flex min-w-0 flex-1 flex-col gap-4 overflow-auto overscroll-contain p-4 sm:p-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div><h2 class="text-xl font-bold tracking-tight">Overview</h2><p class="text-[13px] text-tdl02-mut">Week of Sep 28 · all stores</p></div>
<button type="button" class="flex h-11 items-center gap-2 rounded-xl bg-tdl02-acc px-4.5 text-[13px] font-semibold text-white shadow-[0_8px_20px_-8px_var(--color-tdl02-acc)] transition hover:bg-tdl02-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc active:translate-y-px"><svg viewBox="0 0 24 24" class="size-4 fill-none stroke-current stroke-[2.4] [stroke-linecap:round]" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>New order</button>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(11rem,100%),1fr))] gap-3.5">
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Revenue</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">$128,430</p><small class="text-xs font-semibold text-tdl02-good">▲ 8.2% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Orders</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">1,203</p><small class="text-xs font-semibold text-tdl02-good">▲ 3.1% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Refund rate</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">0.8%</p><small class="text-xs font-semibold text-tdl02-good">▼ 0.2 pts vs last week</small></article>
</div>
<section class="flex min-h-56 flex-1 flex-col gap-3 rounded-2xl border border-tdl02-edge bg-tdl02-panel p-4.5" aria-labelledby="tdl02-chart">
<h3 id="tdl02-chart" class="flex justify-between text-[13px] font-semibold text-tdl02-mut">Sales this quarter<span class="text-tdl02-ink">$1.42M</span></h3>
<div class="flex flex-1 items-end gap-2.5 border-b border-tdl02-edge">
<div class="group/bar relative flex h-[35%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$98k</span></div>
<div class="group/bar relative flex h-[52%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$146k</span></div>
<div class="group/bar relative flex h-[44%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$123k</span></div>
<div class="group/bar relative flex h-[61%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$171k</span></div>
<div class="group/bar relative flex h-[57%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$160k</span></div>
<div class="group/bar relative flex h-[73%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$204k</span></div>
<div class="group/bar relative flex h-[66%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$185k</span></div>
<div class="group/bar relative flex h-[84%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$235k</span></div>
<div class="group/bar relative flex h-[92%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc transition"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$258k</span></div>
</div>
<p class="flex justify-between text-[11px] text-tdl02-mut"><span>Jul</span><span>Aug</span><span>Sep</span></p>
</section>
</main>
</div>
</div>
</section><section class="tdl-02 w-full min-h-svh bg-tdl02-bg font-tdl02-sans text-tdl02-ink antialiased" aria-label="Collapsible sidebar admin shell demo">
<div data-shell class="group/shell @container/shell flex h-svh min-h-[34rem] flex-col">
<header class="flex h-15 flex-none items-center gap-3 border-b border-tdl02-edge bg-tdl02-panel px-3 sm:px-4">
<button data-toggle type="button" aria-expanded="true" aria-controls="tdl02-nav" aria-label="Collapse navigation" class="group/btn relative grid size-11 place-items-center rounded-xl text-tdl02-ink transition-colors hover:bg-tdl02-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-5 fill-none stroke-current stroke-2 [stroke-linecap:round]" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/></svg>
<span aria-hidden="true" class="pointer-events-none absolute top-[calc(100%+6px)] left-0 z-20 flex items-center gap-1.5 rounded-lg bg-tdl02-ink px-2.5 py-1.5 text-xs font-semibold whitespace-nowrap text-white opacity-0 transition-opacity group-hover/btn:opacity-100 group-focus-visible/btn:opacity-100">Toggle sidebar<kbd class="rounded border border-white/25 px-1.5 font-sans text-[10px]">[</kbd></span>
</button>
<strong class="flex items-center gap-2.5 text-[15px] font-bold tracking-tight"><span aria-hidden="true" class="size-6 rounded-lg bg-[conic-gradient(from_200deg,var(--color-tdl02-acc),var(--color-tdl02-acc2),var(--color-tdl02-acc))]"></span>Ledgerly</strong>
<label class="ml-auto flex h-10 w-[min(18rem,40%)] items-center gap-2 rounded-full border border-tdl02-edge bg-tdl02-bg px-3.5 text-[13px] text-tdl02-mut transition focus-within:border-tdl02-acc focus-within:ring-3 focus-within:ring-tdl02-acc/15 @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-3.5 flex-none fill-none stroke-current stroke-2" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<span class="sr-only">Search</span><input type="search" placeholder="Search orders, customers…" class="min-w-0 flex-1 bg-transparent text-tdl02-ink outline-none placeholder:text-tdl02-mut">
<kbd class="rounded-md border border-tdl02-edge bg-tdl02-panel px-1.5 py-0.5 font-sans text-[10px] font-semibold">⌘K</kbd>
</label>
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-linear-140 from-tdl02-acc to-tdl02-acc3 text-[11px] font-bold text-white @max-[45rem]/shell:ml-auto">JD</span>
</header>
<div class="flex min-h-0 flex-1">
<nav id="tdl02-nav" aria-label="Primary" class="relative z-10 flex w-59 flex-none flex-col gap-1 border-r border-tdl02-edge bg-tdl02-panel p-2.5 transition-[width] duration-300 ease-[cubic-bezier(.2,.7,.2,1)] group-data-collapsed/shell:w-17 @max-[45rem]/shell:w-17 [&>a]:relative [&>a]:flex [&>a]:h-11 [&>a]:items-center [&>a]:gap-3 [&>a]:rounded-xl [&>a]:px-3.25 [&>a]:text-[13.5px] [&>a]:font-semibold [&>a]:whitespace-nowrap [&>a]:text-tdl02-mut [&>a]:no-underline [&>a]:transition-colors [&>a:hover]:bg-tdl02-hover [&>a:hover]:text-tdl02-ink [&>a:focus-visible]:outline-2 [&>a:focus-visible]:-outline-offset-2 [&>a:focus-visible]:outline-tdl02-acc [&>a]:after:pointer-events-none [&>a]:after:absolute [&>a]:after:top-1/2 [&>a]:after:left-[calc(100%+12px)] [&>a]:after:-translate-y-1/2 [&>a]:after:scale-95 [&>a]:after:rounded-lg [&>a]:after:bg-tdl02-ink [&>a]:after:px-2.75 [&>a]:after:py-1.5 [&>a]:after:text-xs [&>a]:after:text-white [&>a]:after:opacity-0 [&>a]:after:shadow-lg [&>a]:after:transition [&>a]:after:content-[attr(data-label)] group-data-collapsed/shell:[&>a:hover]:after:scale-100 group-data-collapsed/shell:[&>a:hover]:after:opacity-100 group-data-collapsed/shell:[&>a:focus-visible]:after:opacity-100 @max-[45rem]/shell:[&>a:hover]:after:opacity-100 [&>a[aria-current=page]]:bg-tdl02-acc/10 [&>a[aria-current=page]]:text-tdl02-acc">
<p class="px-3.25 pt-1 pb-1.5 text-[10.5px] font-bold tracking-[0.12em] whitespace-nowrap text-tdl02-mut uppercase transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Workspace</p>
<a href="#overview" data-label="Overview" aria-current="page"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3 12 12 4l9 8"/><path d="M5 10v10h14V10"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Overview</span></a>
<a href="#orders" data-label="Orders · 12"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 7h16l-1.5 12h-13Z"/><path d="M8 7a4 4 0 0 1 8 0"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Orders</span><b class="ml-auto rounded-full bg-tdl02-acc px-2 py-0.5 text-[10.5px] text-white transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">12</b><i aria-hidden="true" class="absolute top-2.5 left-8 size-2 rounded-full bg-tdl02-acc opacity-0 ring-2 ring-tdl02-panel transition-opacity group-data-collapsed/shell:opacity-100 @max-[45rem]/shell:opacity-100"></i></a>
<a href="#customers" data-label="Customers"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M17.5 13.6a6.5 6.5 0 0 1 4 6.4"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Customers</span></a>
<a href="#reports" data-label="Reports"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V10M12 20V4M19 20v-7"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Reports</span></a>
<a href="#billing" data-label="Billing"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Billing</span></a>
<span aria-hidden="true" class="flex-1"></span>
<a href="#settings" data-label="Settings"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Settings</span></a>
</nav>
<main class="flex min-w-0 flex-1 flex-col gap-4 overflow-auto overscroll-contain p-4 sm:p-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div><h2 class="text-xl font-bold tracking-tight">Overview</h2><p class="text-[13px] text-tdl02-mut">Week of Sep 28 · all stores</p></div>
<button type="button" class="flex h-11 items-center gap-2 rounded-xl bg-tdl02-acc px-4.5 text-[13px] font-semibold text-white shadow-[0_8px_20px_-8px_var(--color-tdl02-acc)] transition hover:bg-tdl02-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc active:translate-y-px"><svg viewBox="0 0 24 24" class="size-4 fill-none stroke-current stroke-[2.4] [stroke-linecap:round]" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>New order</button>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(11rem,100%),1fr))] gap-3.5">
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Revenue</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">$128,430</p><small class="text-xs font-semibold text-tdl02-good">▲ 8.2% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Orders</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">1,203</p><small class="text-xs font-semibold text-tdl02-good">▲ 3.1% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Refund rate</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">0.8%</p><small class="text-xs font-semibold text-tdl02-good">▼ 0.2 pts vs last week</small></article>
</div>
<section class="flex min-h-56 flex-1 flex-col gap-3 rounded-2xl border border-tdl02-edge bg-tdl02-panel p-4.5" aria-labelledby="tdl02-chart">
<h3 id="tdl02-chart" class="flex justify-between text-[13px] font-semibold text-tdl02-mut">Sales this quarter<span class="text-tdl02-ink">$1.42M</span></h3>
<div class="flex flex-1 items-end gap-2.5 border-b border-tdl02-edge">
<div class="group/bar relative flex h-[35%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$98k</span></div>
<div class="group/bar relative flex h-[52%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$146k</span></div>
<div class="group/bar relative flex h-[44%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$123k</span></div>
<div class="group/bar relative flex h-[61%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$171k</span></div>
<div class="group/bar relative flex h-[57%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$160k</span></div>
<div class="group/bar relative flex h-[73%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$204k</span></div>
<div class="group/bar relative flex h-[66%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$185k</span></div>
<div class="group/bar relative flex h-[84%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$235k</span></div>
<div class="group/bar relative flex h-[92%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc transition"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$258k</span></div>
</div>
<p class="flex justify-between text-[11px] text-tdl02-mut"><span>Jul</span><span>Aug</span><span>Sep</span></p>
</section>
</main>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}@import "tailwindcss";
@theme {
--color-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}(() => {
document.querySelectorAll('.tdl-02 [data-shell]').forEach((shell) => {
if (shell.dataset.bound) return; shell.dataset.bound = '1';
const btn = shell.querySelector('[data-toggle]');
const set = (collapsed) => {
shell.toggleAttribute('data-collapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.setAttribute('aria-label', collapsed ? 'Expand navigation' : 'Collapse navigation');
try { localStorage.setItem('tdl02-collapsed', collapsed ? '1' : '0'); } catch (e) {}
};
let saved = null; try { saved = localStorage.getItem('tdl02-collapsed'); } catch (e) {}
if (saved === '1') set(true);
btn.addEventListener('click', () => set(!shell.hasAttribute('data-collapsed')));
document.addEventListener('keydown', (e) => {
if (e.key !== '[' || e.metaKey || e.ctrlKey || e.altKey) return;
if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
set(!shell.hasAttribute('data-collapsed'));
});
});
})();(() => {
document.querySelectorAll('.tdl-02 [data-shell]').forEach((shell) => {
if (shell.dataset.bound) return; shell.dataset.bound = '1';
const btn = shell.querySelector('[data-toggle]');
const set = (collapsed) => {
shell.toggleAttribute('data-collapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.setAttribute('aria-label', collapsed ? 'Expand navigation' : 'Collapse navigation');
try { localStorage.setItem('tdl02-collapsed', collapsed ? '1' : '0'); } catch (e) {}
};
let saved = null; try { saved = localStorage.getItem('tdl02-collapsed'); } catch (e) {}
if (saved === '1') set(true);
btn.addEventListener('click', () => set(!shell.hasAttribute('data-collapsed')));
document.addEventListener('keydown', (e) => {
if (e.key !== '[' || e.metaKey || e.ctrlKey || e.altKey) return;
if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
set(!shell.hasAttribute('data-collapsed'));
});
});
})();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-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}@import "tailwindcss";
@theme {
--color-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}<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=Plus+Jakarta+Sans:wght@400;500;600;700;800&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=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
[data-collapsed] .cdl-02__side,[data-collapsed] .cdl-02__labeland the tooltip rules becamegroup-data-collapsed/shell:variants on the elements themselves — the script still only sets an attribute.- The repeated link styles moved to the nav as
[&>a]:child variants, so each link is justhref,data-labeland an icon. - Bug fix: the CSS original put
overflow:hiddenon the sidebar, which clipped its own rail tooltips. The nav is nowoverflow-visible z-10; tooltips render beside the rail. - UI upgrade: collapsed state persists across reloads (
localStorage), [ toggles it, the toggle has its own tooltip, a dot badge replaces the hidden count on the rail, the search is a real labelled input with a focus ring, and chart bars reveal their values on hover. - UI upgrade: all hit targets are 44px (toggle was 40px); muted copy darkened from
0.55to0.49lightness for 6.5:1.
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 CSS Collapsible Sidebar Admin Dashboard
Source: https://codefronts.com/layouts/tailwind-css-dashboard-layouts/tailwind-css-collapsible-sidebar-admin-dashboard/
The canonical SaaS shell: top bar, a sidebar that folds from 236px to a 68px icon rail, and a fluid main area. The fold is w-59 → group-data-collapsed/shell:w-17, labels fade with group-data-collapsed/shell:opacity-0, and rail tooltips are generated by after:content-[attr(data-label)]. The script sets one attribute — it never touches a class — and now remembers the choice and answers the [ shortcut.
## HTML
```html
<section class="tdl-02 w-full min-h-svh bg-tdl02-bg font-tdl02-sans text-tdl02-ink antialiased" aria-label="Collapsible sidebar admin shell demo">
<div data-shell class="group/shell @container/shell flex h-svh min-h-[34rem] flex-col">
<header class="flex h-15 flex-none items-center gap-3 border-b border-tdl02-edge bg-tdl02-panel px-3 sm:px-4">
<button data-toggle type="button" aria-expanded="true" aria-controls="tdl02-nav" aria-label="Collapse navigation" class="group/btn relative grid size-11 place-items-center rounded-xl text-tdl02-ink transition-colors hover:bg-tdl02-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-5 fill-none stroke-current stroke-2 [stroke-linecap:round]" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/></svg>
<span aria-hidden="true" class="pointer-events-none absolute top-[calc(100%+6px)] left-0 z-20 flex items-center gap-1.5 rounded-lg bg-tdl02-ink px-2.5 py-1.5 text-xs font-semibold whitespace-nowrap text-white opacity-0 transition-opacity group-hover/btn:opacity-100 group-focus-visible/btn:opacity-100">Toggle sidebar<kbd class="rounded border border-white/25 px-1.5 font-sans text-[10px]">[</kbd></span>
</button>
<strong class="flex items-center gap-2.5 text-[15px] font-bold tracking-tight"><span aria-hidden="true" class="size-6 rounded-lg bg-[conic-gradient(from_200deg,var(--color-tdl02-acc),var(--color-tdl02-acc2),var(--color-tdl02-acc))]"></span>Ledgerly</strong>
<label class="ml-auto flex h-10 w-[min(18rem,40%)] items-center gap-2 rounded-full border border-tdl02-edge bg-tdl02-bg px-3.5 text-[13px] text-tdl02-mut transition focus-within:border-tdl02-acc focus-within:ring-3 focus-within:ring-tdl02-acc/15 @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-3.5 flex-none fill-none stroke-current stroke-2" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<span class="sr-only">Search</span><input type="search" placeholder="Search orders, customers…" class="min-w-0 flex-1 bg-transparent text-tdl02-ink outline-none placeholder:text-tdl02-mut">
<kbd class="rounded-md border border-tdl02-edge bg-tdl02-panel px-1.5 py-0.5 font-sans text-[10px] font-semibold">⌘K</kbd>
</label>
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-linear-140 from-tdl02-acc to-tdl02-acc3 text-[11px] font-bold text-white @max-[45rem]/shell:ml-auto">JD</span>
</header>
<div class="flex min-h-0 flex-1">
<nav id="tdl02-nav" aria-label="Primary" class="relative z-10 flex w-59 flex-none flex-col gap-1 border-r border-tdl02-edge bg-tdl02-panel p-2.5 transition-[width] duration-300 ease-[cubic-bezier(.2,.7,.2,1)] group-data-collapsed/shell:w-17 @max-[45rem]/shell:w-17 [&>a]:relative [&>a]:flex [&>a]:h-11 [&>a]:items-center [&>a]:gap-3 [&>a]:rounded-xl [&>a]:px-3.25 [&>a]:text-[13.5px] [&>a]:font-semibold [&>a]:whitespace-nowrap [&>a]:text-tdl02-mut [&>a]:no-underline [&>a]:transition-colors [&>a:hover]:bg-tdl02-hover [&>a:hover]:text-tdl02-ink [&>a:focus-visible]:outline-2 [&>a:focus-visible]:-outline-offset-2 [&>a:focus-visible]:outline-tdl02-acc [&>a]:after:pointer-events-none [&>a]:after:absolute [&>a]:after:top-1/2 [&>a]:after:left-[calc(100%+12px)] [&>a]:after:-translate-y-1/2 [&>a]:after:scale-95 [&>a]:after:rounded-lg [&>a]:after:bg-tdl02-ink [&>a]:after:px-2.75 [&>a]:after:py-1.5 [&>a]:after:text-xs [&>a]:after:text-white [&>a]:after:opacity-0 [&>a]:after:shadow-lg [&>a]:after:transition [&>a]:after:content-[attr(data-label)] group-data-collapsed/shell:[&>a:hover]:after:scale-100 group-data-collapsed/shell:[&>a:hover]:after:opacity-100 group-data-collapsed/shell:[&>a:focus-visible]:after:opacity-100 @max-[45rem]/shell:[&>a:hover]:after:opacity-100 [&>a[aria-current=page]]:bg-tdl02-acc/10 [&>a[aria-current=page]]:text-tdl02-acc">
<p class="px-3.25 pt-1 pb-1.5 text-[10.5px] font-bold tracking-[0.12em] whitespace-nowrap text-tdl02-mut uppercase transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Workspace</p>
<a href="#overview" data-label="Overview" aria-current="page"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3 12 12 4l9 8"/><path d="M5 10v10h14V10"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Overview</span></a>
<a href="#orders" data-label="Orders · 12"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 7h16l-1.5 12h-13Z"/><path d="M8 7a4 4 0 0 1 8 0"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Orders</span><b class="ml-auto rounded-full bg-tdl02-acc px-2 py-0.5 text-[10.5px] text-white transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">12</b><i aria-hidden="true" class="absolute top-2.5 left-8 size-2 rounded-full bg-tdl02-acc opacity-0 ring-2 ring-tdl02-panel transition-opacity group-data-collapsed/shell:opacity-100 @max-[45rem]/shell:opacity-100"></i></a>
<a href="#customers" data-label="Customers"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M17.5 13.6a6.5 6.5 0 0 1 4 6.4"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Customers</span></a>
<a href="#reports" data-label="Reports"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V10M12 20V4M19 20v-7"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Reports</span></a>
<a href="#billing" data-label="Billing"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Billing</span></a>
<span aria-hidden="true" class="flex-1"></span>
<a href="#settings" data-label="Settings"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Settings</span></a>
</nav>
<main class="flex min-w-0 flex-1 flex-col gap-4 overflow-auto overscroll-contain p-4 sm:p-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div><h2 class="text-xl font-bold tracking-tight">Overview</h2><p class="text-[13px] text-tdl02-mut">Week of Sep 28 · all stores</p></div>
<button type="button" class="flex h-11 items-center gap-2 rounded-xl bg-tdl02-acc px-4.5 text-[13px] font-semibold text-white shadow-[0_8px_20px_-8px_var(--color-tdl02-acc)] transition hover:bg-tdl02-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc active:translate-y-px"><svg viewBox="0 0 24 24" class="size-4 fill-none stroke-current stroke-[2.4] [stroke-linecap:round]" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>New order</button>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(11rem,100%),1fr))] gap-3.5">
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Revenue</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">$128,430</p><small class="text-xs font-semibold text-tdl02-good">▲ 8.2% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Orders</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">1,203</p><small class="text-xs font-semibold text-tdl02-good">▲ 3.1% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Refund rate</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">0.8%</p><small class="text-xs font-semibold text-tdl02-good">▼ 0.2 pts vs last week</small></article>
</div>
<section class="flex min-h-56 flex-1 flex-col gap-3 rounded-2xl border border-tdl02-edge bg-tdl02-panel p-4.5" aria-labelledby="tdl02-chart">
<h3 id="tdl02-chart" class="flex justify-between text-[13px] font-semibold text-tdl02-mut">Sales this quarter<span class="text-tdl02-ink">$1.42M</span></h3>
<div class="flex flex-1 items-end gap-2.5 border-b border-tdl02-edge">
<div class="group/bar relative flex h-[35%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$98k</span></div>
<div class="group/bar relative flex h-[52%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$146k</span></div>
<div class="group/bar relative flex h-[44%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$123k</span></div>
<div class="group/bar relative flex h-[61%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$171k</span></div>
<div class="group/bar relative flex h-[57%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$160k</span></div>
<div class="group/bar relative flex h-[73%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$204k</span></div>
<div class="group/bar relative flex h-[66%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$185k</span></div>
<div class="group/bar relative flex h-[84%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$235k</span></div>
<div class="group/bar relative flex h-[92%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc transition"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$258k</span></div>
</div>
<p class="flex justify-between text-[11px] text-tdl02-mut"><span>Jul</span><span>Aug</span><span>Sep</span></p>
</section>
</main>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('.tdl-02 [data-shell]').forEach((shell) => {
if (shell.dataset.bound) return; shell.dataset.bound = '1';
const btn = shell.querySelector('[data-toggle]');
const set = (collapsed) => {
shell.toggleAttribute('data-collapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.setAttribute('aria-label', collapsed ? 'Expand navigation' : 'Collapse navigation');
try { localStorage.setItem('tdl02-collapsed', collapsed ? '1' : '0'); } catch (e) {}
};
let saved = null; try { saved = localStorage.getItem('tdl02-collapsed'); } catch (e) {}
if (saved === '1') set(true);
btn.addEventListener('click', () => set(!shell.hasAttribute('data-collapsed')));
document.addEventListener('keydown', (e) => {
if (e.key !== '[' || e.metaKey || e.ctrlKey || e.altKey) return;
if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
set(!shell.hasAttribute('data-collapsed'));
});
});
})();
```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 CSS Collapsible Sidebar Admin Dashboard
Source: https://codefronts.com/layouts/tailwind-css-dashboard-layouts/tailwind-css-collapsible-sidebar-admin-dashboard/
The canonical SaaS shell: top bar, a sidebar that folds from 236px to a 68px icon rail, and a fluid main area. The fold is w-59 → group-data-collapsed/shell:w-17, labels fade with group-data-collapsed/shell:opacity-0, and rail tooltips are generated by after:content-[attr(data-label)]. The script sets one attribute — it never touches a class — and now remembers the choice and answers the [ shortcut.
## HTML
```html
<section class="tdl-02 w-full min-h-svh bg-tdl02-bg font-tdl02-sans text-tdl02-ink antialiased" aria-label="Collapsible sidebar admin shell demo">
<div data-shell class="group/shell @container/shell flex h-svh min-h-[34rem] flex-col">
<header class="flex h-15 flex-none items-center gap-3 border-b border-tdl02-edge bg-tdl02-panel px-3 sm:px-4">
<button data-toggle type="button" aria-expanded="true" aria-controls="tdl02-nav" aria-label="Collapse navigation" class="group/btn relative grid size-11 place-items-center rounded-xl text-tdl02-ink transition-colors hover:bg-tdl02-hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-5 fill-none stroke-current stroke-2 [stroke-linecap:round]" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M9 4v16"/></svg>
<span aria-hidden="true" class="pointer-events-none absolute top-[calc(100%+6px)] left-0 z-20 flex items-center gap-1.5 rounded-lg bg-tdl02-ink px-2.5 py-1.5 text-xs font-semibold whitespace-nowrap text-white opacity-0 transition-opacity group-hover/btn:opacity-100 group-focus-visible/btn:opacity-100">Toggle sidebar<kbd class="rounded border border-white/25 px-1.5 font-sans text-[10px]">[</kbd></span>
</button>
<strong class="flex items-center gap-2.5 text-[15px] font-bold tracking-tight"><span aria-hidden="true" class="size-6 rounded-lg bg-[conic-gradient(from_200deg,var(--color-tdl02-acc),var(--color-tdl02-acc2),var(--color-tdl02-acc))]"></span>Ledgerly</strong>
<label class="ml-auto flex h-10 w-[min(18rem,40%)] items-center gap-2 rounded-full border border-tdl02-edge bg-tdl02-bg px-3.5 text-[13px] text-tdl02-mut transition focus-within:border-tdl02-acc focus-within:ring-3 focus-within:ring-tdl02-acc/15 @max-[45rem]/shell:hidden">
<svg viewBox="0 0 24 24" class="size-3.5 flex-none fill-none stroke-current stroke-2" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<span class="sr-only">Search</span><input type="search" placeholder="Search orders, customers…" class="min-w-0 flex-1 bg-transparent text-tdl02-ink outline-none placeholder:text-tdl02-mut">
<kbd class="rounded-md border border-tdl02-edge bg-tdl02-panel px-1.5 py-0.5 font-sans text-[10px] font-semibold">⌘K</kbd>
</label>
<span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-linear-140 from-tdl02-acc to-tdl02-acc3 text-[11px] font-bold text-white @max-[45rem]/shell:ml-auto">JD</span>
</header>
<div class="flex min-h-0 flex-1">
<nav id="tdl02-nav" aria-label="Primary" class="relative z-10 flex w-59 flex-none flex-col gap-1 border-r border-tdl02-edge bg-tdl02-panel p-2.5 transition-[width] duration-300 ease-[cubic-bezier(.2,.7,.2,1)] group-data-collapsed/shell:w-17 @max-[45rem]/shell:w-17 [&>a]:relative [&>a]:flex [&>a]:h-11 [&>a]:items-center [&>a]:gap-3 [&>a]:rounded-xl [&>a]:px-3.25 [&>a]:text-[13.5px] [&>a]:font-semibold [&>a]:whitespace-nowrap [&>a]:text-tdl02-mut [&>a]:no-underline [&>a]:transition-colors [&>a:hover]:bg-tdl02-hover [&>a:hover]:text-tdl02-ink [&>a:focus-visible]:outline-2 [&>a:focus-visible]:-outline-offset-2 [&>a:focus-visible]:outline-tdl02-acc [&>a]:after:pointer-events-none [&>a]:after:absolute [&>a]:after:top-1/2 [&>a]:after:left-[calc(100%+12px)] [&>a]:after:-translate-y-1/2 [&>a]:after:scale-95 [&>a]:after:rounded-lg [&>a]:after:bg-tdl02-ink [&>a]:after:px-2.75 [&>a]:after:py-1.5 [&>a]:after:text-xs [&>a]:after:text-white [&>a]:after:opacity-0 [&>a]:after:shadow-lg [&>a]:after:transition [&>a]:after:content-[attr(data-label)] group-data-collapsed/shell:[&>a:hover]:after:scale-100 group-data-collapsed/shell:[&>a:hover]:after:opacity-100 group-data-collapsed/shell:[&>a:focus-visible]:after:opacity-100 @max-[45rem]/shell:[&>a:hover]:after:opacity-100 [&>a[aria-current=page]]:bg-tdl02-acc/10 [&>a[aria-current=page]]:text-tdl02-acc">
<p class="px-3.25 pt-1 pb-1.5 text-[10.5px] font-bold tracking-[0.12em] whitespace-nowrap text-tdl02-mut uppercase transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Workspace</p>
<a href="#overview" data-label="Overview" aria-current="page"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M3 12 12 4l9 8"/><path d="M5 10v10h14V10"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Overview</span></a>
<a href="#orders" data-label="Orders · 12"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M4 7h16l-1.5 12h-13Z"/><path d="M8 7a4 4 0 0 1 8 0"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Orders</span><b class="ml-auto rounded-full bg-tdl02-acc px-2 py-0.5 text-[10.5px] text-white transition-opacity group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">12</b><i aria-hidden="true" class="absolute top-2.5 left-8 size-2 rounded-full bg-tdl02-acc opacity-0 ring-2 ring-tdl02-panel transition-opacity group-data-collapsed/shell:opacity-100 @max-[45rem]/shell:opacity-100"></i></a>
<a href="#customers" data-label="Customers"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M17.5 13.6a6.5 6.5 0 0 1 4 6.4"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Customers</span></a>
<a href="#reports" data-label="Reports"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><path d="M5 20V10M12 20V4M19 20v-7"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Reports</span></a>
<a href="#billing" data-label="Billing"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Billing</span></a>
<span aria-hidden="true" class="flex-1"></span>
<a href="#settings" data-label="Settings"><svg viewBox="0 0 24 24" class="size-5 flex-none fill-none stroke-current stroke-[1.8] [stroke-linecap:round] [stroke-linejoin:round]" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1"/></svg><span class="transition-[opacity,translate] duration-200 group-data-collapsed/shell:-translate-x-2 group-data-collapsed/shell:opacity-0 @max-[45rem]/shell:opacity-0">Settings</span></a>
</nav>
<main class="flex min-w-0 flex-1 flex-col gap-4 overflow-auto overscroll-contain p-4 sm:p-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div><h2 class="text-xl font-bold tracking-tight">Overview</h2><p class="text-[13px] text-tdl02-mut">Week of Sep 28 · all stores</p></div>
<button type="button" class="flex h-11 items-center gap-2 rounded-xl bg-tdl02-acc px-4.5 text-[13px] font-semibold text-white shadow-[0_8px_20px_-8px_var(--color-tdl02-acc)] transition hover:bg-tdl02-acc/90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tdl02-acc active:translate-y-px"><svg viewBox="0 0 24 24" class="size-4 fill-none stroke-current stroke-[2.4] [stroke-linecap:round]" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>New order</button>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(11rem,100%),1fr))] gap-3.5">
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Revenue</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">$128,430</p><small class="text-xs font-semibold text-tdl02-good">▲ 8.2% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Orders</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">1,203</p><small class="text-xs font-semibold text-tdl02-good">▲ 3.1% vs last week</small></article>
<article class="rounded-2xl border border-tdl02-edge bg-tdl02-panel px-4.5 py-4"><h3 class="text-[11px] font-semibold tracking-[0.1em] text-tdl02-mut uppercase">Refund rate</h3><p class="mt-1.5 mb-0.5 text-2xl font-bold tracking-tight tabular-nums">0.8%</p><small class="text-xs font-semibold text-tdl02-good">▼ 0.2 pts vs last week</small></article>
</div>
<section class="flex min-h-56 flex-1 flex-col gap-3 rounded-2xl border border-tdl02-edge bg-tdl02-panel p-4.5" aria-labelledby="tdl02-chart">
<h3 id="tdl02-chart" class="flex justify-between text-[13px] font-semibold text-tdl02-mut">Sales this quarter<span class="text-tdl02-ink">$1.42M</span></h3>
<div class="flex flex-1 items-end gap-2.5 border-b border-tdl02-edge">
<div class="group/bar relative flex h-[35%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$98k</span></div>
<div class="group/bar relative flex h-[52%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$146k</span></div>
<div class="group/bar relative flex h-[44%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$123k</span></div>
<div class="group/bar relative flex h-[61%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$171k</span></div>
<div class="group/bar relative flex h-[57%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$160k</span></div>
<div class="group/bar relative flex h-[73%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$204k</span></div>
<div class="group/bar relative flex h-[66%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$185k</span></div>
<div class="group/bar relative flex h-[84%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc/80 transition hover:to-tdl02-acc"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$235k</span></div>
<div class="group/bar relative flex h-[92%] flex-1 justify-center rounded-t-lg bg-linear-to-t from-tdl02-acc/25 to-tdl02-acc transition"><span class="pointer-events-none absolute -top-8 rounded-md bg-tdl02-ink px-2 py-1 text-[11px] font-semibold text-white opacity-0 transition group-hover/bar:opacity-100">$258k</span></div>
</div>
<p class="flex justify-between text-[11px] text-tdl02-mut"><span>Jul</span><span>Aug</span><span>Sep</span></p>
</section>
</main>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdl02-bg: #f6f7fb;
--color-tdl02-bg: oklch(0.975 0.005 265);
--color-tdl02-panel: #ffffff;
--color-tdl02-panel: oklch(1 0 0);
--color-tdl02-hover: #eef0f6;
--color-tdl02-hover: oklch(0.955 0.008 265);
--color-tdl02-edge: #e2e4ec;
--color-tdl02-edge: oklch(0.915 0.01 265);
--color-tdl02-ink: #252a3a;
--color-tdl02-ink: oklch(0.27 0.025 265);
--color-tdl02-mut: #5d6375;
--color-tdl02-mut: oklch(0.49 0.025 265);
--color-tdl02-acc: #4651e0;
--color-tdl02-acc: oklch(0.53 0.2 268);
--color-tdl02-acc2: #3fb0d6;
--color-tdl02-acc2: oklch(0.7 0.12 220);
--color-tdl02-acc3: #9a5ae0;
--color-tdl02-acc3: oklch(0.6 0.18 300);
--color-tdl02-good: #16834a;
--color-tdl02-good: oklch(0.53 0.13 155);
--font-tdl02-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tdl-02 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-02 * {
transition-duration: 0.01s !important;
}
}
```
## JavaScript
```js
(() => {
document.querySelectorAll('.tdl-02 [data-shell]').forEach((shell) => {
if (shell.dataset.bound) return; shell.dataset.bound = '1';
const btn = shell.querySelector('[data-toggle]');
const set = (collapsed) => {
shell.toggleAttribute('data-collapsed', collapsed);
btn.setAttribute('aria-expanded', String(!collapsed));
btn.setAttribute('aria-label', collapsed ? 'Expand navigation' : 'Collapse navigation');
try { localStorage.setItem('tdl02-collapsed', collapsed ? '1' : '0'); } catch (e) {}
};
let saved = null; try { saved = localStorage.getItem('tdl02-collapsed'); } catch (e) {}
if (saved === '1') set(true);
btn.addEventListener('click', () => set(!shell.hasAttribute('data-collapsed')));
document.addEventListener('keydown', (e) => {
if (e.key !== '[' || e.metaKey || e.ctrlKey || e.altKey) return;
if (e.target.closest && e.target.closest('input, textarea, select, [contenteditable]')) return;
set(!shell.hasAttribute('data-collapsed'));
});
});
})();
```How this works
One attribute, many variants. The script toggles data-collapsed on the shell; every element that changes reads it through a named group:
<div data-shell class="group/shell …">
<nav class="w-59 transition-[width] group-data-collapsed/shell:w-17">
<span class="group-data-collapsed/shell:opacity-0">Orders</span>Tooltips only when they help. Each link carries data-label; its ::after is always generated but stays at opacity-0 until group-data-collapsed/shell:hover:after:opacity-100 (or focus-visible) matches — so expanded links never show a redundant tooltip.
Accessible disclosure. The toggle is a real <button aria-expanded aria-controls>; the script flips aria-expanded, swaps the label, persists the state to localStorage and binds the [ key (ignored while typing in a field).
Make it yours
- Widths:
w-59(236px) andw-17(68px) — any spacing step works in v4, no config. - Start collapsed: add
data-collapsedto the shell andaria-expanded="false"to the button. - Shortcut: change the
e.key === '['check in the script, and the<kbd>hint in the toggle's tooltip. - Accent: edit
--color-tdl02-acc; active link, badge, avatar, CTA and chart follow.
Gotchas — read before shipping
- The nav must not be
overflow-hiddenor the rail tooltips are clipped (a bug in the CSS original). Labels arewhitespace-nowrap+opacity-0instead, so nothing visible overflows. data-collapsed:matches the bare attribute; if you writedata-collapsed="false"it still matches. Remove the attribute instead.- Transitioning
widthreflows the main column each frame; on very heavy pages switch to agrid-template-columnstransition or a transform-based overlay.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4 floor. attr() in content is universal.