03 / 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 Dashboard KPI Stat Cards Grid
Four KPI cards that flow 4 → 2 → 1 columns from the width of their own container (@min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4) — never the orphaned 3 + 1 row that auto-fit produces. New in the port: a working 7d / 28d / 90d range switch built from a radio group, where group-has-[#tdl03-7:checked]/kpi:inline swaps every value, delta and sparkline with zero JavaScript.
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-03 w-full min-h-svh bg-tdl03-bg bg-linear-175 from-tdl03-bg to-tdl03-bg2 font-tdl03-sans text-tdl03-ink antialiased" aria-label="KPI card grid demo">
<div class="group/kpi @container/kpi mx-auto grid min-h-svh w-full max-w-6xl content-center gap-5 p-4 sm:p-8">
<header class="flex flex-wrap items-end justify-between gap-3">
<div><p class="text-[12px] font-semibold tracking-[0.12em] text-tdl03-acc uppercase">Storefront</p><h2 class="text-2xl font-bold tracking-tight">Performance</h2></div>
<fieldset class="flex rounded-xl border border-tdl03-edge bg-tdl03-track p-1 *:relative *:grid *:h-9 *:min-w-14 *:cursor-pointer *:place-items-center *:rounded-lg *:px-3 *:text-[13px] *:font-semibold *:text-tdl03-mut *:transition *:has-checked:bg-tdl03-panel *:has-checked:text-tdl03-ink *:has-checked:shadow-[0_1px_3px_oklch(0.3_0.03_200/0.18)] *:has-focus-visible:outline-2 *:has-focus-visible:outline-tdl03-acc">
<legend class="sr-only">Date range</legend>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-7">7d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-28" checked>28d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-90">90d</label>
</fieldset>
</header>
<div class="grid grid-cols-1 gap-4 @min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4">
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Revenue</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$21,980</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$86,410</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$251,300</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">4.2%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">12.4%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">18.9%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:38%"></i><i style="--h:52%;animation-delay:60ms"></i><i style="--h:46%;animation-delay:120ms"></i><i style="--h:64%;animation-delay:180ms"></i><i style="--h:58%;animation-delay:240ms"></i><i style="--h:78%;animation-delay:300ms"></i><i style="--h:100%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Orders</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1,072</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">4,203</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">12,880</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1.8%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.1%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">7.6%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:55%"></i><i style="--h:48%;animation-delay:60ms"></i><i style="--h:66%;animation-delay:120ms"></i><i style="--h:60%;animation-delay:180ms"></i><i style="--h:74%;animation-delay:240ms"></i><i style="--h:69%;animation-delay:300ms"></i><i style="--h:88%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Conversion rate</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">3.31%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.42%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">3.58%</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-bad/12 px-2.25 py-0.75 font-bold text-tdl03-bad tabular-nums"><span class="sr-only">Down </span>▼ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">0.6 pts</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">0.4 pts</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">0.1 pts</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-bad/30 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-bad"><i style="--h:82%"></i><i style="--h:74%;animation-delay:60ms"></i><i style="--h:88%;animation-delay:120ms"></i><i style="--h:70%;animation-delay:180ms"></i><i style="--h:64%;animation-delay:240ms"></i><i style="--h:58%;animation-delay:300ms"></i><i style="--h:52%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Avg. order value</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$20.50</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$20.56</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$19.51</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">2.3%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">5.8%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">4.4%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:44%"></i><i style="--h:58%;animation-delay:60ms"></i><i style="--h:50%;animation-delay:120ms"></i><i style="--h:62%;animation-delay:180ms"></i><i style="--h:70%;animation-delay:240ms"></i><i style="--h:66%;animation-delay:300ms"></i><i style="--h:80%;animation-delay:360ms"></i></div>
</article>
</div>
</div>
</section><section class="tdl-03 w-full min-h-svh bg-tdl03-bg bg-linear-175 from-tdl03-bg to-tdl03-bg2 font-tdl03-sans text-tdl03-ink antialiased" aria-label="KPI card grid demo">
<div class="group/kpi @container/kpi mx-auto grid min-h-svh w-full max-w-6xl content-center gap-5 p-4 sm:p-8">
<header class="flex flex-wrap items-end justify-between gap-3">
<div><p class="text-[12px] font-semibold tracking-[0.12em] text-tdl03-acc uppercase">Storefront</p><h2 class="text-2xl font-bold tracking-tight">Performance</h2></div>
<fieldset class="flex rounded-xl border border-tdl03-edge bg-tdl03-track p-1 *:relative *:grid *:h-9 *:min-w-14 *:cursor-pointer *:place-items-center *:rounded-lg *:px-3 *:text-[13px] *:font-semibold *:text-tdl03-mut *:transition *:has-checked:bg-tdl03-panel *:has-checked:text-tdl03-ink *:has-checked:shadow-[0_1px_3px_oklch(0.3_0.03_200/0.18)] *:has-focus-visible:outline-2 *:has-focus-visible:outline-tdl03-acc">
<legend class="sr-only">Date range</legend>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-7">7d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-28" checked>28d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-90">90d</label>
</fieldset>
</header>
<div class="grid grid-cols-1 gap-4 @min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4">
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Revenue</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$21,980</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$86,410</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$251,300</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">4.2%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">12.4%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">18.9%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:38%"></i><i style="--h:52%;animation-delay:60ms"></i><i style="--h:46%;animation-delay:120ms"></i><i style="--h:64%;animation-delay:180ms"></i><i style="--h:58%;animation-delay:240ms"></i><i style="--h:78%;animation-delay:300ms"></i><i style="--h:100%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Orders</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1,072</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">4,203</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">12,880</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1.8%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.1%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">7.6%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:55%"></i><i style="--h:48%;animation-delay:60ms"></i><i style="--h:66%;animation-delay:120ms"></i><i style="--h:60%;animation-delay:180ms"></i><i style="--h:74%;animation-delay:240ms"></i><i style="--h:69%;animation-delay:300ms"></i><i style="--h:88%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Conversion rate</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">3.31%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.42%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">3.58%</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-bad/12 px-2.25 py-0.75 font-bold text-tdl03-bad tabular-nums"><span class="sr-only">Down </span>▼ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">0.6 pts</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">0.4 pts</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">0.1 pts</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-bad/30 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-bad"><i style="--h:82%"></i><i style="--h:74%;animation-delay:60ms"></i><i style="--h:88%;animation-delay:120ms"></i><i style="--h:70%;animation-delay:180ms"></i><i style="--h:64%;animation-delay:240ms"></i><i style="--h:58%;animation-delay:300ms"></i><i style="--h:52%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Avg. order value</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$20.50</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$20.56</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$19.51</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">2.3%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">5.8%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">4.4%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:44%"></i><i style="--h:58%;animation-delay:60ms"></i><i style="--h:50%;animation-delay:120ms"></i><i style="--h:62%;animation-delay:180ms"></i><i style="--h:70%;animation-delay:240ms"></i><i style="--h:66%;animation-delay:300ms"></i><i style="--h:80%;animation-delay:360ms"></i></div>
</article>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !important;
}
}@import "tailwindcss";
@theme {
--color-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !important;
}
}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-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !important;
}
}@import "tailwindcss";
@theme {
--color-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !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=Instrument+Sans:wght@400..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=Instrument+Sans:wght@400..700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The
auto-fitgrid became container-query columns (see below); card hover lift ishover:-translate-y-0.75plus a token-driven shadow. - Per-bar
--istagger indices became plain inlineanimation-delays; heights stay as--hand are read by*:h-(--h). - UI upgrade from the CSS original: the inert “Last 28 days ▾” pill is now a working, keyboard-accessible 7d / 28d / 90d segmented control that swaps every metric through
group-has-[…:checked]— still zero JS. - UI upgrade: no more 3 + 1 orphan row — the grid snaps 4 → 2 → 1 — and it is centred in a
max-w-6xlcolumn instead of hugging the top of an empty stage; the negative metric's sparkline turns red to match its delta; trend arrows gainedsr-only“Up/Down” text; muted copy rose to 6.4: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 Dashboard KPI Stat Cards Grid
Source: https://codefronts.com/layouts/tailwind-css-dashboard-layouts/tailwind-css-dashboard-kpi-stat-cards-grid/
Four KPI cards that flow 4 → 2 → 1 columns from the width of their own container (@min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4) — never the orphaned 3 + 1 row that auto-fit produces. New in the port: a working 7d / 28d / 90d range switch built from a radio group, where group-has-[#tdl03-7:checked]/kpi:inline swaps every value, delta and sparkline with zero JavaScript.
## HTML
```html
<section class="tdl-03 w-full min-h-svh bg-tdl03-bg bg-linear-175 from-tdl03-bg to-tdl03-bg2 font-tdl03-sans text-tdl03-ink antialiased" aria-label="KPI card grid demo">
<div class="group/kpi @container/kpi mx-auto grid min-h-svh w-full max-w-6xl content-center gap-5 p-4 sm:p-8">
<header class="flex flex-wrap items-end justify-between gap-3">
<div><p class="text-[12px] font-semibold tracking-[0.12em] text-tdl03-acc uppercase">Storefront</p><h2 class="text-2xl font-bold tracking-tight">Performance</h2></div>
<fieldset class="flex rounded-xl border border-tdl03-edge bg-tdl03-track p-1 *:relative *:grid *:h-9 *:min-w-14 *:cursor-pointer *:place-items-center *:rounded-lg *:px-3 *:text-[13px] *:font-semibold *:text-tdl03-mut *:transition *:has-checked:bg-tdl03-panel *:has-checked:text-tdl03-ink *:has-checked:shadow-[0_1px_3px_oklch(0.3_0.03_200/0.18)] *:has-focus-visible:outline-2 *:has-focus-visible:outline-tdl03-acc">
<legend class="sr-only">Date range</legend>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-7">7d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-28" checked>28d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-90">90d</label>
</fieldset>
</header>
<div class="grid grid-cols-1 gap-4 @min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4">
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Revenue</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$21,980</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$86,410</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$251,300</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">4.2%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">12.4%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">18.9%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:38%"></i><i style="--h:52%;animation-delay:60ms"></i><i style="--h:46%;animation-delay:120ms"></i><i style="--h:64%;animation-delay:180ms"></i><i style="--h:58%;animation-delay:240ms"></i><i style="--h:78%;animation-delay:300ms"></i><i style="--h:100%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Orders</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1,072</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">4,203</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">12,880</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1.8%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.1%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">7.6%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:55%"></i><i style="--h:48%;animation-delay:60ms"></i><i style="--h:66%;animation-delay:120ms"></i><i style="--h:60%;animation-delay:180ms"></i><i style="--h:74%;animation-delay:240ms"></i><i style="--h:69%;animation-delay:300ms"></i><i style="--h:88%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Conversion rate</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">3.31%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.42%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">3.58%</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-bad/12 px-2.25 py-0.75 font-bold text-tdl03-bad tabular-nums"><span class="sr-only">Down </span>▼ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">0.6 pts</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">0.4 pts</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">0.1 pts</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-bad/30 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-bad"><i style="--h:82%"></i><i style="--h:74%;animation-delay:60ms"></i><i style="--h:88%;animation-delay:120ms"></i><i style="--h:70%;animation-delay:180ms"></i><i style="--h:64%;animation-delay:240ms"></i><i style="--h:58%;animation-delay:300ms"></i><i style="--h:52%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Avg. order value</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$20.50</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$20.56</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$19.51</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">2.3%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">5.8%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">4.4%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:44%"></i><i style="--h:58%;animation-delay:60ms"></i><i style="--h:50%;animation-delay:120ms"></i><i style="--h:62%;animation-delay:180ms"></i><i style="--h:70%;animation-delay:240ms"></i><i style="--h:66%;animation-delay:300ms"></i><i style="--h:80%;animation-delay:360ms"></i></div>
</article>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !important;
}
}
```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 Dashboard KPI Stat Cards Grid
Source: https://codefronts.com/layouts/tailwind-css-dashboard-layouts/tailwind-css-dashboard-kpi-stat-cards-grid/
Four KPI cards that flow 4 → 2 → 1 columns from the width of their own container (@min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4) — never the orphaned 3 + 1 row that auto-fit produces. New in the port: a working 7d / 28d / 90d range switch built from a radio group, where group-has-[#tdl03-7:checked]/kpi:inline swaps every value, delta and sparkline with zero JavaScript.
## HTML
```html
<section class="tdl-03 w-full min-h-svh bg-tdl03-bg bg-linear-175 from-tdl03-bg to-tdl03-bg2 font-tdl03-sans text-tdl03-ink antialiased" aria-label="KPI card grid demo">
<div class="group/kpi @container/kpi mx-auto grid min-h-svh w-full max-w-6xl content-center gap-5 p-4 sm:p-8">
<header class="flex flex-wrap items-end justify-between gap-3">
<div><p class="text-[12px] font-semibold tracking-[0.12em] text-tdl03-acc uppercase">Storefront</p><h2 class="text-2xl font-bold tracking-tight">Performance</h2></div>
<fieldset class="flex rounded-xl border border-tdl03-edge bg-tdl03-track p-1 *:relative *:grid *:h-9 *:min-w-14 *:cursor-pointer *:place-items-center *:rounded-lg *:px-3 *:text-[13px] *:font-semibold *:text-tdl03-mut *:transition *:has-checked:bg-tdl03-panel *:has-checked:text-tdl03-ink *:has-checked:shadow-[0_1px_3px_oklch(0.3_0.03_200/0.18)] *:has-focus-visible:outline-2 *:has-focus-visible:outline-tdl03-acc">
<legend class="sr-only">Date range</legend>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-7">7d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-28" checked>28d</label>
<label><input class="sr-only" type="radio" name="tdl03-range" id="tdl03-90">90d</label>
</fieldset>
</header>
<div class="grid grid-cols-1 gap-4 @min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4">
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Revenue</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$21,980</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$86,410</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$251,300</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">4.2%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">12.4%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">18.9%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:38%"></i><i style="--h:52%;animation-delay:60ms"></i><i style="--h:46%;animation-delay:120ms"></i><i style="--h:64%;animation-delay:180ms"></i><i style="--h:58%;animation-delay:240ms"></i><i style="--h:78%;animation-delay:300ms"></i><i style="--h:100%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Orders</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1,072</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">4,203</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">12,880</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">1.8%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.1%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">7.6%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:55%"></i><i style="--h:48%;animation-delay:60ms"></i><i style="--h:66%;animation-delay:120ms"></i><i style="--h:60%;animation-delay:180ms"></i><i style="--h:74%;animation-delay:240ms"></i><i style="--h:69%;animation-delay:300ms"></i><i style="--h:88%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Conversion rate</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">3.31%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">3.42%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">3.58%</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-bad/12 px-2.25 py-0.75 font-bold text-tdl03-bad tabular-nums"><span class="sr-only">Down </span>▼ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">0.6 pts</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">0.4 pts</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">0.1 pts</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-bad/30 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-bad"><i style="--h:82%"></i><i style="--h:74%;animation-delay:60ms"></i><i style="--h:88%;animation-delay:120ms"></i><i style="--h:70%;animation-delay:180ms"></i><i style="--h:64%;animation-delay:240ms"></i><i style="--h:58%;animation-delay:300ms"></i><i style="--h:52%;animation-delay:360ms"></i></div>
</article>
<article class="flex flex-col gap-1.5 rounded-2xl border border-tdl03-edge bg-tdl03-panel p-5 transition duration-200 hover:-translate-y-0.75 hover:border-tdl03-acc/50 hover:shadow-[0_14px_30px_-18px_var(--color-tdl03-shadow)]">
<h3 class="text-[11px] font-semibold tracking-[0.11em] text-tdl03-mut uppercase">Avg. order value</h3>
<p class="text-[30px] font-bold tracking-[-0.02em] tabular-nums"><span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$20.50</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$20.56</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">$19.51</span></p>
<p class="flex items-center gap-2 text-[11.5px]"><span class="rounded-full bg-tdl03-good/13 px-2.25 py-0.75 font-bold text-tdl03-good tabular-nums"><span class="sr-only">Up </span>▲ <span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">2.3%</span><span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">5.8%</span><span class="hidden group-has-[#tdl03-90:checked]/kpi:inline">4.4%</span></span><span class="text-tdl03-mut">vs prior period</span></p>
<div aria-hidden="true" class="mt-3 flex h-12 items-end gap-1.25 *:h-(--h) *:flex-1 *:origin-bottom *:rounded-t-[3px] *:bg-tdl03-acc/40 *:animate-tdl03-rise [&>i:last-child]:bg-tdl03-acc"><i style="--h:44%"></i><i style="--h:58%;animation-delay:60ms"></i><i style="--h:50%;animation-delay:120ms"></i><i style="--h:62%;animation-delay:180ms"></i><i style="--h:70%;animation-delay:240ms"></i><i style="--h:66%;animation-delay:300ms"></i><i style="--h:80%;animation-delay:360ms"></i></div>
</article>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tdl03-bg: #f5f9f9;
--color-tdl03-bg: oklch(0.977 0.006 190);
--color-tdl03-bg2: #ebf2f2;
--color-tdl03-bg2: oklch(0.955 0.01 190);
--color-tdl03-track: #e5eded;
--color-tdl03-track: oklch(0.935 0.01 190);
--color-tdl03-panel: #ffffff;
--color-tdl03-panel: oklch(1 0 0);
--color-tdl03-edge: #dbe5e5;
--color-tdl03-edge: oklch(0.91 0.012 190);
--color-tdl03-ink: #1d2d31;
--color-tdl03-ink: oklch(0.27 0.025 210);
--color-tdl03-mut: #55666a;
--color-tdl03-mut: oklch(0.49 0.02 210);
--color-tdl03-acc: #00899a;
--color-tdl03-acc: oklch(0.57 0.11 205);
--color-tdl03-good: #18804a;
--color-tdl03-good: oklch(0.52 0.13 155);
--color-tdl03-bad: #c8352f;
--color-tdl03-bad: oklch(0.56 0.19 25);
--color-tdl03-shadow: #4f7f86;
--color-tdl03-shadow: oklch(0.45 0.06 205 / 0.55);
--font-tdl03-sans: 'Instrument Sans', system-ui, sans-serif;
--animate-tdl03-rise: tdl03-rise 0.6s cubic-bezier(0.2,0.7,0.2,1) backwards;
@keyframes tdl03-rise {
from {
scale: 1 0;
}
}
}
.tdl-03 {
width: 100%;
min-height: 100svh;
}
@media (prefers-reduced-motion: reduce) {
.tdl-03 * {
animation: none !important;
transition: none !important;
}
}
```How this works
Why not auto-fit? The CSS original used repeat(auto-fit,minmax(240px,1fr)), which is breakpoint-free but produces a lonely fourth card on a second row whenever three fit. Four cards only look right as 4, 2 or 1 columns, so the port uses named container queries: grid-cols-1 @min-[34rem]/kpi:grid-cols-2 @min-[60rem]/kpi:grid-cols-4.
State in the DOM. The range control is three visually styled radios inside a fieldset. The stage is group/kpi, and every dataset span listens:
<span class="hidden group-has-[#tdl03-28:checked]/kpi:inline">$86,410</span>
<span class="hidden group-has-[#tdl03-7:checked]/kpi:inline">$21,980</span>Arrow keys move between radios natively, focus is drawn with has-focus-visible:outline-2 on the label, and hidden spans are display:none so screen readers only hear the active value.
Make it yours
- Break points:
@min-[34rem]/kpi:and@min-[60rem]/kpi:— container widths, so the grid behaves the same in a sidebar or a modal. - Add a range: one more radio + label, and one more span per value with its own
group-has-[#id:checked]/kpi:utility. - More than four metrics? Switch to
grid-cols-[repeat(auto-fit,minmax(min(15rem,100%),1fr))], where orphan rows matter less. - Good/bad colours live in
--color-tdl03-goodand--color-tdl03-bad.
Gotchas — read before shipping
- Radio ids must be unique on the page; rename
tdl03-*if you paste the block twice. group-has-[…]needs the radios inside the group element — here the whole stage is the group.- Sparkline bars animate on first paint only; switching ranges swaps instantly (re-triggering keyframes would need JS).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Tailwind v4 floor; :has() is inside it.