27 / 39
All 39 in 39 Tailwind CSS Breadcrumbs
- 01Tailwind Accessible Breadcrumb with Schema.org Microdata & ARIA
- 02Tailwind Responsive Collapsing Breadcrumb Bar with Container Queries
- 03Tailwind Adaptive Light & Dark Mode Breadcrumb Bar with Theme Tokens
- 04Tailwind E-Commerce Filter & Facet Breadcrumb with Removable Chips
- 05Tailwind Glassmorphism Floating Breadcrumb Pill with Shimmer Sweep
- 06Tailwind Neumorphic Soft UI Breadcrumb with Dual Box Shadows
- 07Tailwind Sticky Scroll-Shrink Breadcrumb Header with Scroll-Driven Animation
- 08Tailwind Material Design Flat Breadcrumb Bar with Ink Ripple
- 09Tailwind Isometric 3D Layered Breadcrumb with Extruded Tiles
- 10Tailwind Breadcrumb Bar with Built-In Dropdown Sibling Menus
- 11Tailwind Full-Width Block-Style Segmented Breadcrumb Bar
- 12Tailwind Accent-Colour Toggle Breadcrumb with Persisted Theme
- 13Tailwind Seamless Arrow-Cutout Breadcrumb Trail with clip-path
- 14Tailwind Compact Icon-Only Home Node Breadcrumb Bar
- 15Tailwind Segmented Rounded-Outline Pill Breadcrumb
- 16Tailwind Neumorphic Soft Inset-Shadow Breadcrumb Channel
- 17Tailwind CSS Grid Auto-Fitting Responsive Breadcrumb Trail
- 18Tailwind Holographic Shimmer Web3 Breadcrumb with Animated @property
- 19Tailwind SaaS Gradient-Text Breadcrumb Bar with Animated Clip
- 20Tailwind Marketplace Filter-Chip Breadcrumb with Icon Chips
- 21Tailwind Editorial Breadcrumb with Animated SVG Wave Underline
- 22Tailwind Developer Docs Breadcrumb with One-Click Path Copy
- 23Tailwind Modern Arrow Breadcrumb Bar using clip-path Polygons
- 24Tailwind Multi-Line Aligned Breadcrumb Layout with CSS Subgrid
- 25Tailwind Breadcrumb Links with Animated Underline Glow
- 26Tailwind Minimalist Breadcrumb with Swappable Masked SVG Separators
- 27Tailwind Multi-Step Checkout & Wizard Breadcrumb Indicator
- 28Tailwind Ribbon-Style Chevron Breadcrumb Trail
- 29Tailwind Pill-Shape Badge Breadcrumb Bar
- 30Tailwind Clean Line-Separator Minimalist Breadcrumbs
- 31Tailwind Gradient Background Glow Breadcrumb Bar
- 32Tailwind Floating Card Shadow Breadcrumb Navigation
- 33Tailwind Truncated Ellipsis Compact Mobile Breadcrumb
- 34Tailwind Inline Bullet-Dot Separator Breadcrumb
- 35Tailwind Skewed Parallelogram Breadcrumb Bar
- 36Tailwind Outline Border-Only Geometric Breadcrumbs
- 37Tailwind Circular Icon & Numbered Step Breadcrumbs
- 38Tailwind Frosted Glass Dark UI Breadcrumb Component
- 39Tailwind Micro-Interaction Animated Hover Breadcrumbs
Tailwind Multi-Step Checkout & Wizard Breadcrumb Indicator
Breadcrumbs as a checkout progress bar: auto-numbered circular badges, connector lines that fill for completed steps, and three states read straight off a data-state attribute with data-[state=done]: variants. The numbering stays a CSS counter — the one thing utilities genuinely cannot do.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tbc-27 w-full min-h-svh grid place-items-center bg-tbc27-bg font-tbc27-sans px-7 py-9">
<nav aria-label="Checkout progress" class="w-[min(560px,100%)]">
<ol class="flex list-none m-0 p-0 [counter-reset:tbc27step]">
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done first:before:hidden">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Cart</span>
<span class="sr-only">completed</span>
</li>
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Shipping</span>
<span class="sr-only">completed</span>
</li>
<li data-state="current" aria-current="step" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-acc text-tbc27-acc shadow-[0_0_0_4px_color-mix(in_oklab,var(--color-tbc27-acc)_20%,transparent)] before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-bold text-tbc27-strong">Payment</span>
</li>
<li data-state="upcoming" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-mut">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-mut text-tbc27-mutink before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-mutink">Review</span>
</li>
</ol>
</nav>
</section><section class="tbc-27 w-full min-h-svh grid place-items-center bg-tbc27-bg font-tbc27-sans px-7 py-9">
<nav aria-label="Checkout progress" class="w-[min(560px,100%)]">
<ol class="flex list-none m-0 p-0 [counter-reset:tbc27step]">
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done first:before:hidden">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Cart</span>
<span class="sr-only">completed</span>
</li>
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Shipping</span>
<span class="sr-only">completed</span>
</li>
<li data-state="current" aria-current="step" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-acc text-tbc27-acc shadow-[0_0_0_4px_color-mix(in_oklab,var(--color-tbc27-acc)_20%,transparent)] before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-bold text-tbc27-strong">Payment</span>
</li>
<li data-state="upcoming" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-mut">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-mut text-tbc27-mutink before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-mutink">Review</span>
</li>
</ol>
</nav>
</section>@import "tailwindcss";
@theme {
--color-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 1;
}@import "tailwindcss";
@theme {
--color-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 1;
}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-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 1;
}@import "tailwindcss";
@theme {
--color-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 1;
}<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=Space+Grotesk:wght@400;500;600;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The state machine moved onto the markup (
data-state+ variants) while the numbering stayed in CSS. That split is the honest answer for counters: they are content, and utilities describe presentation. - The connector line is a
before:on the step rather than a separate element, andfirst:before:hiddenreplaces theli:first-child::before{display:none}rule — one of the few places a structural variant is clearly worth using. - UI change from the CSS original: the done colour darkened from
oklch(0.65 0.14 160)to0.55so the white checkmark inside the badge clears 4.5:1, and upcoming labels darkened for the same reason.
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 Multi-Step Checkout & Wizard Breadcrumb Indicator
Source: https://codefronts.com/navigation/tailwind-css-breadcrumbs/tailwind-multi-step-checkout-wizard-breadcrumb-indicator/
Breadcrumbs as a checkout progress bar: auto-numbered circular badges, connector lines that fill for completed steps, and three states read straight off a data-state attribute with data-[state=done]: variants. The numbering stays a CSS counter — the one thing utilities genuinely cannot do.
## HTML
```html
<section class="tbc-27 w-full min-h-svh grid place-items-center bg-tbc27-bg font-tbc27-sans px-7 py-9">
<nav aria-label="Checkout progress" class="w-[min(560px,100%)]">
<ol class="flex list-none m-0 p-0 [counter-reset:tbc27step]">
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done first:before:hidden">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Cart</span>
<span class="sr-only">completed</span>
</li>
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Shipping</span>
<span class="sr-only">completed</span>
</li>
<li data-state="current" aria-current="step" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-acc text-tbc27-acc shadow-[0_0_0_4px_color-mix(in_oklab,var(--color-tbc27-acc)_20%,transparent)] before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-bold text-tbc27-strong">Payment</span>
</li>
<li data-state="upcoming" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-mut">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-mut text-tbc27-mutink before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-mutink">Review</span>
</li>
</ol>
</nav>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 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 Multi-Step Checkout & Wizard Breadcrumb Indicator
Source: https://codefronts.com/navigation/tailwind-css-breadcrumbs/tailwind-multi-step-checkout-wizard-breadcrumb-indicator/
Breadcrumbs as a checkout progress bar: auto-numbered circular badges, connector lines that fill for completed steps, and three states read straight off a data-state attribute with data-[state=done]: variants. The numbering stays a CSS counter — the one thing utilities genuinely cannot do.
## HTML
```html
<section class="tbc-27 w-full min-h-svh grid place-items-center bg-tbc27-bg font-tbc27-sans px-7 py-9">
<nav aria-label="Checkout progress" class="w-[min(560px,100%)]">
<ol class="flex list-none m-0 p-0 [counter-reset:tbc27step]">
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done first:before:hidden">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Cart</span>
<span class="sr-only">completed</span>
</li>
<li data-state="done" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-tbc27-done border-2 border-tbc27-done text-white before:content-['\2713']"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-label">Shipping</span>
<span class="sr-only">completed</span>
</li>
<li data-state="current" aria-current="step" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-done">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-acc text-tbc27-acc shadow-[0_0_0_4px_color-mix(in_oklab,var(--color-tbc27-acc)_20%,transparent)] before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-bold text-tbc27-strong">Payment</span>
</li>
<li data-state="upcoming" class="group relative flex-1 flex flex-col items-center gap-2.5 text-center before:content-[''] before:absolute before:top-4 before:-left-1/2 before:w-full before:h-0.75 before:z-0 before:bg-tbc27-mut">
<span aria-hidden="true" class="relative z-1 grid place-items-center w-8.5 h-8.5 rounded-full text-sm font-bold [counter-increment:tbc27step] bg-white border-2 border-tbc27-mut text-tbc27-mutink before:content-[counter(tbc27step)]"></span>
<span class="text-[13px]/[1.2] font-semibold text-tbc27-mutink">Review</span>
</li>
</ol>
</nav>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbc27-bg: #fbfbfd;
--color-tbc27-bg: oklch(0.98 0.004 250);
--color-tbc27-acc: #4a68d4;
--color-tbc27-acc: oklch(0.55 0.16 265);
--color-tbc27-done: #2f8f6b;
--color-tbc27-done: oklch(0.55 0.13 160);
--color-tbc27-mut: #d2d5dd;
--color-tbc27-mut: oklch(0.85 0.01 250);
--color-tbc27-mutink: #7d818c;
--color-tbc27-mutink: oklch(0.56 0.01 250);
--color-tbc27-label: #61656f;
--color-tbc27-label: oklch(0.46 0.02 250);
--color-tbc27-strong: #32353d;
--color-tbc27-strong: oklch(0.28 0.02 250);
--font-tbc27-sans: 'Space Grotesk', system-ui, sans-serif;
}
.tbc-27 {
width: 100%;
min-height: 100svh;
}
/* counter() inside content has no utility form — the numeral itself is the one part
of this pattern that must stay in CSS. */
.tbc-27 li span[aria-hidden]::before {
line-height: 1;
}
```How this works
Three states, one attribute. Each <li> carries data-state and group; the badge and the label read it through group-data-[state=...]:. Adding a fourth state (error, skipped) is a new value and a new variant run — no new selectors.
Counters are the port's hard limit. counter-increment and content:counter(step) have no utility form, and should not: they are document state, not design scale. They stay in the stylesheet, and the done state swaps the numeral for a checkmark the same way.
The accessible layer is separate from the visual one. aria-current="step" marks the active step, and completed steps carry an sr-only "completed" — because a green checkmark is not an announcement.
Make it yours
- Palette:
--color-tbc27-done,--color-tbc27-acc,--color-tbc27-mut. - Badge size:
w-8.5 h-8.5plus the connector'sbefore:top-4— they must agree or the line misses the circle's centre. - For a vertical wizard, swap the list to
flex-coland rotate the connector tobefore:w-0.75 before:h-full.
Gotchas — read before shipping
data-[state=done]:is the value form; the baredata-done:shorthand only matches valueless attributes.- The connector uses
-left-1/2 w-full, which requires each step to be an equalflex-1track — content-sized steps make the lines ragged. - Never rely on colour alone for step state: the checkmark, the numeral and the
sr-onlytext carry it too.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
CSS counters are ancient and universal; color-mix() is the only modern feature and is v4's own floor.