08 / 32
All 32 in 32 Tailwind CSS Tabs
- 01Tailwind CSS Tabs with Sliding Underline Indicator
- 02Neon Glow Tailwind Tabs with Per-Tab Accent Colors
- 03Neo-Brutalist Tailwind Tabs with Press Effect
- 04Color-Coded Tailwind CSS Tabs Component
- 05VS Code Style File Tabs in Tailwind CSS
- 06iOS Segmented Control in Tailwind CSS
- 07Bento Grid Card Tabs in Tailwind CSS
- 08Vertical Tabs with Timeline Progress in Tailwind CSS
- 09Terminal Window Tabs in Tailwind CSS (Retro CRT)
- 10Expanding Icon Tabs in Tailwind CSS
- 11Typewriter Animation Tabs in Tailwind CSS
- 12Tailwind Tabs with Particle Burst Animation
- 13Animated Counter Tabs in Tailwind CSS (Type Specimen)
- 14Oversized Cropped Text Tabs in Tailwind CSS
- 15Tailwind Sidebar Navigation with Sliding Indicator
- 16Tailwind Bottom Navigation Bar with Expanding Labels
- 17Variable Font Weight Tabs in Tailwind CSS
- 18Uppercase Tabs with Thick Sliding Bar in Tailwind CSS
- 19Drop Cap Initial Letter Tabs in Tailwind CSS
- 20Industrial Stencil Tabs in Tailwind CSS
- 21Bracket Selection Tabs in Tailwind CSS
- 22Bauhaus Color Block Tabs in Tailwind CSS
- 23Inverted Rubber Stamp Tabs in Tailwind CSS
- 24Outlined Tabs with Corner Markers in Tailwind CSS
- 25Tailwind Pill Tab Bar with Icons
- 26Risograph Print Effect Tabs in Tailwind CSS
- 27Animated SVG Underline Tabs in Tailwind CSS
- 28Font Specimen Glyph Tabs in Tailwind CSS
- 29Sliding Pill Tabs with Icons in Tailwind CSS
- 30Folded Corner Tabs in Tailwind CSS (clip-path)
- 31Bookmark Ribbon Navigation in Tailwind CSS
- 32Neumorphic Inset Tabs in Tailwind CSS
Vertical Tabs with Timeline Progress in Tailwind CSS
A settings sidebar on a timeline rail: hollow dots for each section, a solid glowing dot for the active one, and — new in the port — a progress fill that runs down the rail to the active step. Beside it, the content pane switches; on phones the two stack.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttb-08 group/tabs grid min-h-svh w-full place-items-center bg-ttb08-bg px-5 py-14 font-ttb08-sans text-ttb08-ink" aria-label="Vertical timeline tabs demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="grid w-full gap-4 rounded-2xl bg-ttb08-card p-4 ring-1 ring-white/8 sm:grid-cols-[210px_1fr]">
<div role="radiogroup" aria-label="Settings sections" class="relative flex flex-col [--i:0] has-[#ttb08-r2:checked]:[--i:1] has-[#ttb08-r3:checked]:[--i:2] has-[#ttb08-r4:checked]:[--i:3]">
<span aria-hidden="true" class="absolute top-6 bottom-6 left-[15px] w-0.5 rounded-full bg-ttb08-acc/20"></span>
<span aria-hidden="true" class="absolute top-6 left-[15px] h-[calc((100%-48px)*var(--i)/3)] w-0.5 rounded-full bg-ttb08-acc transition-[height] duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r1" checked><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Profile</span><span class="block text-[11px] text-ttb08-dim">Name, photo, bio</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r2"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Account</span><span class="block text-[11px] text-ttb08-dim">Email, password, 2FA</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r3"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Billing</span><span class="block text-[11px] text-ttb08-dim">Plan, invoices</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r4"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Logs</span><span class="block text-[11px] text-ttb08-dim">Sessions, activity</span></span></label>
</div>
<div class="rounded-xl bg-white/[0.03] p-5 ring-1 ring-white/6">
<div class="hidden group-has-[#ttb08-r1:checked]/tabs:block"><h3 class="text-lg font-bold">Profile</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">How you appear to teammates: display name, avatar and a short bio.</p></div>
<div class="hidden group-has-[#ttb08-r2:checked]/tabs:block"><h3 class="text-lg font-bold">Account</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Sign-in email, password and two-factor authentication.</p></div>
<div class="hidden group-has-[#ttb08-r3:checked]/tabs:block"><h3 class="text-lg font-bold">Billing</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Your current plan, payment method and downloadable invoices.</p></div>
<div class="hidden group-has-[#ttb08-r4:checked]/tabs:block"><h3 class="text-lg font-bold">Logs</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Active sessions and a 90-day record of account activity.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 font-ttb08-mono text-[11.5px] text-ttb08-mut">rail fill h-[calc((100%-48px)*var(--i)/3)] · group-has-checked/tab: dot</p>
</div>
</section><section class="ttb-08 group/tabs grid min-h-svh w-full place-items-center bg-ttb08-bg px-5 py-14 font-ttb08-sans text-ttb08-ink" aria-label="Vertical timeline tabs demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="grid w-full gap-4 rounded-2xl bg-ttb08-card p-4 ring-1 ring-white/8 sm:grid-cols-[210px_1fr]">
<div role="radiogroup" aria-label="Settings sections" class="relative flex flex-col [--i:0] has-[#ttb08-r2:checked]:[--i:1] has-[#ttb08-r3:checked]:[--i:2] has-[#ttb08-r4:checked]:[--i:3]">
<span aria-hidden="true" class="absolute top-6 bottom-6 left-[15px] w-0.5 rounded-full bg-ttb08-acc/20"></span>
<span aria-hidden="true" class="absolute top-6 left-[15px] h-[calc((100%-48px)*var(--i)/3)] w-0.5 rounded-full bg-ttb08-acc transition-[height] duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r1" checked><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Profile</span><span class="block text-[11px] text-ttb08-dim">Name, photo, bio</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r2"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Account</span><span class="block text-[11px] text-ttb08-dim">Email, password, 2FA</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r3"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Billing</span><span class="block text-[11px] text-ttb08-dim">Plan, invoices</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r4"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Logs</span><span class="block text-[11px] text-ttb08-dim">Sessions, activity</span></span></label>
</div>
<div class="rounded-xl bg-white/[0.03] p-5 ring-1 ring-white/6">
<div class="hidden group-has-[#ttb08-r1:checked]/tabs:block"><h3 class="text-lg font-bold">Profile</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">How you appear to teammates: display name, avatar and a short bio.</p></div>
<div class="hidden group-has-[#ttb08-r2:checked]/tabs:block"><h3 class="text-lg font-bold">Account</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Sign-in email, password and two-factor authentication.</p></div>
<div class="hidden group-has-[#ttb08-r3:checked]/tabs:block"><h3 class="text-lg font-bold">Billing</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Your current plan, payment method and downloadable invoices.</p></div>
<div class="hidden group-has-[#ttb08-r4:checked]/tabs:block"><h3 class="text-lg font-bold">Logs</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Active sessions and a 90-day record of account activity.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 font-ttb08-mono text-[11.5px] text-ttb08-mut">rail fill h-[calc((100%-48px)*var(--i)/3)] · group-has-checked/tab: dot</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}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-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}<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=Geist:wght@400..800&family=Geist+Mono:wght@400..600&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=Geist:wght@400..800&family=Geist+Mono:wght@400..600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The
::beforerail and the dot'smargin-left:-29pxarithmetic were replaced by two absolutely placed spans under a dot at a known x — no negative-margin geometry to recompute. input:checked + .tt28b .tt28dotbecamegroup-has-checked/tab:on the dot.- UI upgrade from the CSS original: a progress fill runs down the rail to the active step (
h-[calc((100%-48px)*var(--i)/3)]), each step gained a sub-label, and a content pane sits beside the nav. - UI upgrade: rows are 48px tall (were ~24px), inactive labels moved from 55% white to a 7.6:1 token, and the layout stacks on phones.
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: Vertical Tabs with Timeline Progress in Tailwind CSS
Source: https://codefronts.com/navigation/tailwind-css-tabs/vertical-tabs-timeline-progress-tailwind-css/
A settings sidebar on a timeline rail: hollow dots for each section, a solid glowing dot for the active one, and — new in the port — a progress fill that runs down the rail to the active step. Beside it, the content pane switches; on phones the two stack.
## HTML
```html
<section class="ttb-08 group/tabs grid min-h-svh w-full place-items-center bg-ttb08-bg px-5 py-14 font-ttb08-sans text-ttb08-ink" aria-label="Vertical timeline tabs demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="grid w-full gap-4 rounded-2xl bg-ttb08-card p-4 ring-1 ring-white/8 sm:grid-cols-[210px_1fr]">
<div role="radiogroup" aria-label="Settings sections" class="relative flex flex-col [--i:0] has-[#ttb08-r2:checked]:[--i:1] has-[#ttb08-r3:checked]:[--i:2] has-[#ttb08-r4:checked]:[--i:3]">
<span aria-hidden="true" class="absolute top-6 bottom-6 left-[15px] w-0.5 rounded-full bg-ttb08-acc/20"></span>
<span aria-hidden="true" class="absolute top-6 left-[15px] h-[calc((100%-48px)*var(--i)/3)] w-0.5 rounded-full bg-ttb08-acc transition-[height] duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r1" checked><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Profile</span><span class="block text-[11px] text-ttb08-dim">Name, photo, bio</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r2"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Account</span><span class="block text-[11px] text-ttb08-dim">Email, password, 2FA</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r3"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Billing</span><span class="block text-[11px] text-ttb08-dim">Plan, invoices</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r4"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Logs</span><span class="block text-[11px] text-ttb08-dim">Sessions, activity</span></span></label>
</div>
<div class="rounded-xl bg-white/[0.03] p-5 ring-1 ring-white/6">
<div class="hidden group-has-[#ttb08-r1:checked]/tabs:block"><h3 class="text-lg font-bold">Profile</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">How you appear to teammates: display name, avatar and a short bio.</p></div>
<div class="hidden group-has-[#ttb08-r2:checked]/tabs:block"><h3 class="text-lg font-bold">Account</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Sign-in email, password and two-factor authentication.</p></div>
<div class="hidden group-has-[#ttb08-r3:checked]/tabs:block"><h3 class="text-lg font-bold">Billing</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Your current plan, payment method and downloadable invoices.</p></div>
<div class="hidden group-has-[#ttb08-r4:checked]/tabs:block"><h3 class="text-lg font-bold">Logs</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Active sessions and a 90-day record of account activity.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 font-ttb08-mono text-[11.5px] text-ttb08-mut">rail fill h-[calc((100%-48px)*var(--i)/3)] · group-has-checked/tab: dot</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}
```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: Vertical Tabs with Timeline Progress in Tailwind CSS
Source: https://codefronts.com/navigation/tailwind-css-tabs/vertical-tabs-timeline-progress-tailwind-css/
A settings sidebar on a timeline rail: hollow dots for each section, a solid glowing dot for the active one, and — new in the port — a progress fill that runs down the rail to the active step. Beside it, the content pane switches; on phones the two stack.
## HTML
```html
<section class="ttb-08 group/tabs grid min-h-svh w-full place-items-center bg-ttb08-bg px-5 py-14 font-ttb08-sans text-ttb08-ink" aria-label="Vertical timeline tabs demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="grid w-full gap-4 rounded-2xl bg-ttb08-card p-4 ring-1 ring-white/8 sm:grid-cols-[210px_1fr]">
<div role="radiogroup" aria-label="Settings sections" class="relative flex flex-col [--i:0] has-[#ttb08-r2:checked]:[--i:1] has-[#ttb08-r3:checked]:[--i:2] has-[#ttb08-r4:checked]:[--i:3]">
<span aria-hidden="true" class="absolute top-6 bottom-6 left-[15px] w-0.5 rounded-full bg-ttb08-acc/20"></span>
<span aria-hidden="true" class="absolute top-6 left-[15px] h-[calc((100%-48px)*var(--i)/3)] w-0.5 rounded-full bg-ttb08-acc transition-[height] duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r1" checked><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Profile</span><span class="block text-[11px] text-ttb08-dim">Name, photo, bio</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r2"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Account</span><span class="block text-[11px] text-ttb08-dim">Email, password, 2FA</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r3"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Billing</span><span class="block text-[11px] text-ttb08-dim">Plan, invoices</span></span></label>
<label class="group/tab relative flex h-12 cursor-pointer items-center gap-3 rounded-lg px-2.5 transition-colors hover:bg-ttb08-acc/6 has-focus-visible:outline-1 has-focus-visible:outline-offset-1 has-focus-visible:outline-dashed has-focus-visible:outline-ttb08-acc"><input class="sr-only" type="radio" name="ttb08" id="ttb08-r4"><span aria-hidden="true" class="relative z-10 size-3 flex-none rounded-full border-[1.5px] border-ttb08-acc/50 bg-ttb08-card transition-[background-color,border-color,box-shadow] group-has-checked/tab:border-ttb08-acc group-has-checked/tab:bg-ttb08-acc group-has-checked/tab:shadow-[0_0_10px_color-mix(in_oklab,var(--color-ttb08-acc)_60%,transparent)]"></span><span class="leading-tight"><span class="block text-[13px] font-semibold text-ttb08-mut transition-colors group-has-checked/tab:text-ttb08-acc-ink">Logs</span><span class="block text-[11px] text-ttb08-dim">Sessions, activity</span></span></label>
</div>
<div class="rounded-xl bg-white/[0.03] p-5 ring-1 ring-white/6">
<div class="hidden group-has-[#ttb08-r1:checked]/tabs:block"><h3 class="text-lg font-bold">Profile</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">How you appear to teammates: display name, avatar and a short bio.</p></div>
<div class="hidden group-has-[#ttb08-r2:checked]/tabs:block"><h3 class="text-lg font-bold">Account</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Sign-in email, password and two-factor authentication.</p></div>
<div class="hidden group-has-[#ttb08-r3:checked]/tabs:block"><h3 class="text-lg font-bold">Billing</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Your current plan, payment method and downloadable invoices.</p></div>
<div class="hidden group-has-[#ttb08-r4:checked]/tabs:block"><h3 class="text-lg font-bold">Logs</h3><p class="mt-1.5 text-sm leading-relaxed text-ttb08-mut">Active sessions and a 90-day record of account activity.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-white/10 px-3.5 py-2 font-ttb08-mono text-[11.5px] text-ttb08-mut">rail fill h-[calc((100%-48px)*var(--i)/3)] · group-has-checked/tab: dot</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb08-bg: #0b0f15;
--color-ttb08-bg: oklch(0.167 0.014 258.4);
--color-ttb08-card: #111821;
--color-ttb08-card: oklch(0.206 0.021 254.9);
--color-ttb08-ink: #eef4fa;
--color-ttb08-ink: oklch(0.964 0.010 247.9);
--color-ttb08-mut: #a7b3c2;
--color-ttb08-mut: oklch(0.762 0.025 254.0);
--color-ttb08-dim: #8794a5;
--color-ttb08-dim: oklch(0.662 0.029 255.1);
--color-ttb08-acc: #38bdf8;
--color-ttb08-acc: oklch(0.754 0.139 232.7);
--color-ttb08-acc-ink: #7dd3fc;
--color-ttb08-acc-ink: oklch(0.828 0.101 230.3);
--font-ttb08-sans: 'Geist', system-ui, sans-serif;
--font-ttb08-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.ttb-08 {
width: 100%;
min-height: 100svh;
}
```How this works
Rail and fill share one geometry. Rows are 48px tall with no gap, so dot centres sit 24px from the top of the first row and the bottom of the last. The rail spans that distance; the fill covers i/3 of it:
<span class="absolute top-6 bottom-6 left-[15px] w-0.5 bg-ttb08-acc/20"></span>
<span class="absolute top-6 left-[15px] w-0.5 bg-ttb08-acc
h-[calc((100%-48px)*var(--i)/3)] transition-[height]"></span>No negative-margin arithmetic. The original pulled each dot onto the rail with margin-left:-29px; here the dot is the first flex child at a known x, so the rail is placed under it instead.
Make it yours
- More steps: change the divisor from 3 to steps − 1 and add
[--i]entries. - Light mode: stage
#f8fafc, dot fill the stage colour so hollow dots punch the rail. - Turn the fill off for a pure nav (not a progress) by deleting the fill span.
Gotchas — read before shipping
- The fill math assumes equal row heights — keep sub-labels to one line.
- Hollow dots need a fill equal to the background or the rail shows through them.
- For a true stepper (steps completed), style earlier dots with a counter or extra variants; this demo marks only the active one.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() drives the index; height transitions are universal.