SaaS admin shell with sidebar and header
- md:grid-cols-[16rem_1fr]
- aria-[current=page]:
- @container
- min-w-0
- aria-expanded
The shell is one grid: unprefixed classes stack the sidebar above the content on phones, and md:grid md:grid-cols-[16rem_1fr] puts it in a fixed 16rem column next to a flexible one. min-w-0 on the content column stops long rows from widening the grid, and the main area is an @container so the stat cards switch to three columns by their own width. The only JS is the phone menu button, which flips aria-expanded and the hidden class on the nav.
View code
<div class="min-h-[560px] bg-slate-50 text-slate-900 md:grid md:grid-cols-[16rem_1fr]">
<aside class="border-b border-slate-200 bg-white md:border-r md:border-b-0">
<div class="flex h-16 items-center justify-between gap-3 px-4">
<a href="#" class="flex items-center gap-2.5 rounded-md text-base font-semibold tracking-tight text-slate-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600">
<svg viewBox="0 0 32 32" class="size-8" aria-hidden="true"><rect width="32" height="32" rx="8" class="fill-violet-600"/><path d="M10 22V10h4v8h8v4z" class="fill-white"/></svg>
Ledgerly
</a>
<button id="tdb-saas-toggle" type="button" aria-expanded="false" aria-controls="tdb-saas-nav" class="grid size-11 place-items-center rounded-lg text-slate-700 ring-1 ring-slate-200 hover:bg-slate-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600 md:hidden">
<span class="sr-only">Menu</span>
<svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg>
</button>
</div>
<nav id="tdb-saas-nav" aria-label="Main" class="hidden px-3 pb-5 md:block">
<ul class="space-y-1 text-sm font-medium">
<li><a href="#" aria-current="page" class="flex min-h-10 items-center gap-3 rounded-lg px-3 text-slate-700 hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-2 focus-visible:outline-violet-600 aria-[current=page]:bg-violet-50 aria-[current=page]:text-violet-800"><svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M4 10.5 12 4l8 6.5V20h-5v-5H9v5H4z"/></svg>Overview</a></li>
<li><a href="#" class="flex min-h-10 items-center gap-3 rounded-lg px-3 text-slate-700 hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-2 focus-visible:outline-violet-600 aria-[current=page]:bg-violet-50 aria-[current=page]:text-violet-800"><svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20c.8-3.5 3.4-5.5 6.5-5.5s5.7 2 6.5 5.5M16 4.8a3.5 3.5 0 0 1 0 6.4M18.5 14.8c1.5.8 2.6 2.6 3 5.2"/></svg>Customers<span class="ml-auto rounded-full bg-slate-100 px-2 py-0.5 text-xs text-slate-700">1,284</span></a></li>
<li><a href="#" class="flex min-h-10 items-center gap-3 rounded-lg px-3 text-slate-700 hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-2 focus-visible:outline-violet-600 aria-[current=page]:bg-violet-50 aria-[current=page]:text-violet-800"><svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" aria-hidden="true"><path d="M6 3h8l4 4v14H6zM14 3v4h4M9 12h6M9 16h6"/></svg>Invoices</a></li>
<li><a href="#" class="flex min-h-10 items-center gap-3 rounded-lg px-3 text-slate-700 hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-2 focus-visible:outline-violet-600 aria-[current=page]:bg-violet-50 aria-[current=page]:text-violet-800"><svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M4 20V11M10 20V5M16 20v-6M21 20H3"/></svg>Reports</a></li>
<li><a href="#" class="flex min-h-10 items-center gap-3 rounded-lg px-3 text-slate-700 hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-2 focus-visible:outline-violet-600 aria-[current=page]:bg-violet-50 aria-[current=page]:text-violet-800"><svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2.5v3M12 18.5v3M2.5 12h3M18.5 12h3M5.3 5.3l2.1 2.1M16.6 16.6l2.1 2.1M5.3 18.7l2.1-2.1M16.6 7.4l2.1-2.1"/></svg>Settings</a></li>
</ul>
<div class="mt-6 rounded-xl bg-slate-100 p-4">
<p class="text-sm font-medium text-slate-900">Growth plan</p>
<p class="mt-1 text-xs text-slate-600">7,840 of 10,000 invoices this month</p>
<div class="mt-3 h-2 rounded-full bg-slate-200" role="progressbar" aria-label="Invoice quota used" aria-valuenow="78" aria-valuemin="0" aria-valuemax="100"><div class="h-full w-[78%] rounded-full bg-violet-600"></div></div>
</div>
</nav>
</aside>
<div class="flex min-w-0 flex-col">
<header class="flex h-16 items-center gap-3 border-b border-slate-200 bg-white px-4 sm:px-6">
<form role="search" class="relative min-w-0 flex-1 sm:max-w-xs">
<label for="tdb-saas-q" class="sr-only">Search customers and invoices</label>
<svg viewBox="0 0 24 24" class="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-slate-500" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/></svg>
<input id="tdb-saas-q" type="search" placeholder="Search" class="h-10 w-full rounded-lg bg-slate-100 pr-3 pl-9 text-sm text-slate-900 placeholder:text-slate-600 focus:bg-white focus:outline-2 focus:outline-violet-600">
</form>
<button type="button" class="relative ml-auto grid size-10 shrink-0 place-items-center rounded-lg text-slate-600 hover:bg-slate-100 focus-visible:outline-2 focus-visible:outline-violet-600">
<span class="sr-only">Notifications, 3 unread</span>
<svg viewBox="0 0 24 24" class="size-5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9a6 6 0 1 1 12 0c0 6 2.5 8 2.5 8h-17S6 15 6 9M10 20.5h4"/></svg>
<span class="absolute top-2 right-2 size-2 rounded-full bg-rose-500 ring-2 ring-white" aria-hidden="true"></span>
</button>
<svg viewBox="0 0 40 40" class="size-9 shrink-0" role="img" aria-label="Signed in as Priya Natarajan"><circle cx="20" cy="20" r="20" class="fill-amber-200"/><text x="20" y="25" text-anchor="middle" class="fill-amber-900 text-sm font-semibold">PN</text></svg>
</header>
<main class="@container flex-1 p-4 sm:p-6">
<div class="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 class="text-xl font-semibold tracking-tight text-slate-900">Overview</h1>
<p class="text-sm text-slate-600">Tuesday, 27 October 2026</p>
</div>
<a href="#" class="inline-flex min-h-10 items-center gap-2 rounded-lg bg-violet-600 px-4 text-sm font-semibold text-white hover:bg-violet-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600"><svg viewBox="0 0 20 20" class="size-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M10 4v12M4 10h12"/></svg>New invoice</a>
</div>
<dl class="mt-5 grid gap-3 @md:grid-cols-3">
<div class="rounded-xl bg-white p-4 ring-1 ring-slate-200">
<dt class="text-sm text-slate-600">Outstanding</dt>
<dd class="mt-1 text-2xl font-semibold tabular-nums">$48,210</dd>
<dd class="text-xs text-slate-600">23 open invoices</dd>
</div>
<div class="rounded-xl bg-white p-4 ring-1 ring-slate-200">
<dt class="text-sm text-slate-600">Paid this month</dt>
<dd class="mt-1 text-2xl font-semibold tabular-nums">$126,904</dd>
<dd class="text-xs text-emerald-700">Up 12% on September</dd>
</div>
<div class="rounded-xl bg-white p-4 ring-1 ring-slate-200">
<dt class="text-sm text-slate-600">Overdue</dt>
<dd class="mt-1 text-2xl font-semibold text-rose-700 tabular-nums">$6,380</dd>
<dd class="text-xs text-slate-600">4 invoices past 30 days</dd>
</div>
</dl>
<section aria-labelledby="tdb-saas-recent" class="mt-5 rounded-xl bg-white ring-1 ring-slate-200">
<div class="flex items-center justify-between border-b border-slate-200 px-4 py-3">
<h2 id="tdb-saas-recent" class="text-sm font-semibold text-slate-900">Recent invoices</h2>
<a href="#" class="rounded text-sm font-medium text-violet-700 hover:underline focus-visible:outline-2 focus-visible:outline-violet-600">View all</a>
</div>
<ul class="divide-y divide-slate-200 text-sm">
<li class="flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-3">
<div class="min-w-0 flex-1"><p class="truncate font-medium text-slate-900">Northwind Joinery</p><p class="text-slate-600">INV-2093, due 2 Nov</p></div>
<span class="rounded-full bg-emerald-50 px-2 py-0.5 text-xs font-medium text-emerald-800 ring-1 ring-emerald-600/20 ring-inset">Paid</span>
<p class="w-24 text-right font-medium tabular-nums">$4,200.00</p>
</li>
<li class="flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-3">
<div class="min-w-0 flex-1"><p class="truncate font-medium text-slate-900">Kestrel Dental Group</p><p class="text-slate-600">INV-2092, due 30 Oct</p></div>
<span class="rounded-full bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-800 ring-1 ring-amber-600/20 ring-inset">Sent</span>
<p class="w-24 text-right font-medium tabular-nums">$1,860.00</p>
</li>
<li class="flex flex-wrap items-center gap-x-4 gap-y-1 px-4 py-3">
<div class="min-w-0 flex-1"><p class="truncate font-medium text-slate-900">Marlow & Finch</p><p class="text-slate-600">INV-2087, due 21 Sep</p></div>
<span class="rounded-full bg-rose-50 px-2 py-0.5 text-xs font-medium text-rose-800 ring-1 ring-rose-600/20 ring-inset">Overdue</span>
<p class="w-24 text-right font-medium tabular-nums">$2,975.50</p>
</li>
</ul>
</section>
</main>
</div>
</div>(function () {
var btn = document.getElementById('tdb-saas-toggle');
var nav = document.getElementById('tdb-saas-nav');
if (!btn || !nav) return;
btn.addEventListener('click', function () {
var open = btn.getAttribute('aria-expanded') !== 'true';
btn.setAttribute('aria-expanded', String(open));
nav.classList.toggle('hidden', !open);
});
})();