15 / 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
Tailwind Sidebar Navigation with Sliding Indicator
An app-shell sidebar on warm stone: icon rows, an olive accent bar that slides vertically to the active row, and a content pane beside it. The original measured offsetTop; the port slides the bar with translate-y-[calc(var(--i)*(100%+4px))] — each step is one 44px row plus its 4px gap.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttb-15 group/tabs grid min-h-svh w-full place-items-center bg-ttb15-bg px-5 py-14 font-ttb15-sans text-ttb15-ink" aria-label="Sidebar navigation with sliding indicator demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="flex w-full flex-wrap gap-3 rounded-2xl bg-ttb15-panel p-3 shadow-[0_18px_40px_-20px_rgb(58_46_30/0.35)] ring-1 ring-ttb15-ink/8">
<div class="flex-[1_1_200px]">
<p class="flex items-center gap-2.5 px-3 pt-2 pb-4 text-sm font-bold"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-ttb15-acc text-[11px] font-extrabold text-white">NB</span>Northbound</p>
<div role="radiogroup" aria-label="Workspace" class="relative flex flex-col gap-1 pl-3 [--i:0] has-[#ttb15-r2:checked]:[--i:1] has-[#ttb15-r3:checked]:[--i:2] has-[#ttb15-r4:checked]:[--i:3]">
<span aria-hidden="true" class="pointer-events-none absolute top-0 left-0 h-11 w-1 translate-y-[calc(var(--i)*(100%+4px))] rounded-r-full bg-ttb15-acc transition-transform duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r1" checked><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8v10h-6v-7H9v7H3z"/></svg>Home</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r2"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg>Activity</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r3"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0M16 4.5a3.5 3.5 0 0 1 0 7M18 14a6 6 0 0 1 3.5 6"/></svg>People</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r4"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M4.9 19.1L7 17M17 7l2.1-2.1"/></svg>Settings</label>
</div>
</div>
<div class="min-w-0 flex-[999_1_300px] rounded-xl bg-ttb15-card p-6 ring-1 ring-ttb15-ink/6">
<div class="hidden group-has-[#ttb15-r1:checked]/tabs:block"><h3 class="text-xl font-bold">Home</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Pinned projects, recent files and what changed since your last visit.</p></div>
<div class="hidden group-has-[#ttb15-r2:checked]/tabs:block"><h3 class="text-xl font-bold">Activity</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Comments, mentions and edits across every project you follow.</p></div>
<div class="hidden group-has-[#ttb15-r3:checked]/tabs:block"><h3 class="text-xl font-bold">People</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Members, guests and pending invitations for this workspace.</p></div>
<div class="hidden group-has-[#ttb15-r4:checked]/tabs:block"><h3 class="text-xl font-bold">Settings</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Workspace name, permissions, integrations and billing.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-ttb15-ink/10 bg-ttb15-card/70 px-3.5 py-2 font-ttb15-mono text-[11.5px] text-ttb15-mut">translate-y-[calc(var(--i)*(100%+4px))] · spring row shift</p>
</div>
</section><section class="ttb-15 group/tabs grid min-h-svh w-full place-items-center bg-ttb15-bg px-5 py-14 font-ttb15-sans text-ttb15-ink" aria-label="Sidebar navigation with sliding indicator demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="flex w-full flex-wrap gap-3 rounded-2xl bg-ttb15-panel p-3 shadow-[0_18px_40px_-20px_rgb(58_46_30/0.35)] ring-1 ring-ttb15-ink/8">
<div class="flex-[1_1_200px]">
<p class="flex items-center gap-2.5 px-3 pt-2 pb-4 text-sm font-bold"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-ttb15-acc text-[11px] font-extrabold text-white">NB</span>Northbound</p>
<div role="radiogroup" aria-label="Workspace" class="relative flex flex-col gap-1 pl-3 [--i:0] has-[#ttb15-r2:checked]:[--i:1] has-[#ttb15-r3:checked]:[--i:2] has-[#ttb15-r4:checked]:[--i:3]">
<span aria-hidden="true" class="pointer-events-none absolute top-0 left-0 h-11 w-1 translate-y-[calc(var(--i)*(100%+4px))] rounded-r-full bg-ttb15-acc transition-transform duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r1" checked><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8v10h-6v-7H9v7H3z"/></svg>Home</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r2"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg>Activity</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r3"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0M16 4.5a3.5 3.5 0 0 1 0 7M18 14a6 6 0 0 1 3.5 6"/></svg>People</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r4"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M4.9 19.1L7 17M17 7l2.1-2.1"/></svg>Settings</label>
</div>
</div>
<div class="min-w-0 flex-[999_1_300px] rounded-xl bg-ttb15-card p-6 ring-1 ring-ttb15-ink/6">
<div class="hidden group-has-[#ttb15-r1:checked]/tabs:block"><h3 class="text-xl font-bold">Home</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Pinned projects, recent files and what changed since your last visit.</p></div>
<div class="hidden group-has-[#ttb15-r2:checked]/tabs:block"><h3 class="text-xl font-bold">Activity</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Comments, mentions and edits across every project you follow.</p></div>
<div class="hidden group-has-[#ttb15-r3:checked]/tabs:block"><h3 class="text-xl font-bold">People</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Members, guests and pending invitations for this workspace.</p></div>
<div class="hidden group-has-[#ttb15-r4:checked]/tabs:block"><h3 class="text-xl font-bold">Settings</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Workspace name, permissions, integrations and billing.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-ttb15-ink/10 bg-ttb15-card/70 px-3.5 py-2 font-ttb15-mono text-[11.5px] text-ttb15-mut">translate-y-[calc(var(--i)*(100%+4px))] · spring row shift</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
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-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
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=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The vertical measuring script (
offsetTop,offsetHeight, resize listener) becameh-11 translate-y-[calc(var(--i)*(100%+4px))]. .tt03b.active .tt03i { transform: scale(1.12) }becamegroup-has-checked/tab:scale-110; the springcubic-bezier(0.34,1.56,0.64,1)carried over as an arbitrary ease.- UI upgrade from the CSS original: the sidebar now lives in an app shell with a workspace header and a content pane, and the shell stacks on phones via
flex-wrap. - UI upgrade: inactive labels moved from 55% ink (≈3.6:1) to a 5.3:1 token, the active label to a 6.4:1 deep olive, and a visible focus ring was added.
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 Sidebar Navigation with Sliding Indicator
Source: https://codefronts.com/navigation/tailwind-css-tabs/tailwind-sidebar-navigation-sliding-indicator/
An app-shell sidebar on warm stone: icon rows, an olive accent bar that slides vertically to the active row, and a content pane beside it. The original measured offsetTop; the port slides the bar with translate-y-[calc(var(--i)*(100%+4px))] — each step is one 44px row plus its 4px gap.
## HTML
```html
<section class="ttb-15 group/tabs grid min-h-svh w-full place-items-center bg-ttb15-bg px-5 py-14 font-ttb15-sans text-ttb15-ink" aria-label="Sidebar navigation with sliding indicator demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="flex w-full flex-wrap gap-3 rounded-2xl bg-ttb15-panel p-3 shadow-[0_18px_40px_-20px_rgb(58_46_30/0.35)] ring-1 ring-ttb15-ink/8">
<div class="flex-[1_1_200px]">
<p class="flex items-center gap-2.5 px-3 pt-2 pb-4 text-sm font-bold"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-ttb15-acc text-[11px] font-extrabold text-white">NB</span>Northbound</p>
<div role="radiogroup" aria-label="Workspace" class="relative flex flex-col gap-1 pl-3 [--i:0] has-[#ttb15-r2:checked]:[--i:1] has-[#ttb15-r3:checked]:[--i:2] has-[#ttb15-r4:checked]:[--i:3]">
<span aria-hidden="true" class="pointer-events-none absolute top-0 left-0 h-11 w-1 translate-y-[calc(var(--i)*(100%+4px))] rounded-r-full bg-ttb15-acc transition-transform duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r1" checked><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8v10h-6v-7H9v7H3z"/></svg>Home</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r2"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg>Activity</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r3"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0M16 4.5a3.5 3.5 0 0 1 0 7M18 14a6 6 0 0 1 3.5 6"/></svg>People</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r4"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M4.9 19.1L7 17M17 7l2.1-2.1"/></svg>Settings</label>
</div>
</div>
<div class="min-w-0 flex-[999_1_300px] rounded-xl bg-ttb15-card p-6 ring-1 ring-ttb15-ink/6">
<div class="hidden group-has-[#ttb15-r1:checked]/tabs:block"><h3 class="text-xl font-bold">Home</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Pinned projects, recent files and what changed since your last visit.</p></div>
<div class="hidden group-has-[#ttb15-r2:checked]/tabs:block"><h3 class="text-xl font-bold">Activity</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Comments, mentions and edits across every project you follow.</p></div>
<div class="hidden group-has-[#ttb15-r3:checked]/tabs:block"><h3 class="text-xl font-bold">People</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Members, guests and pending invitations for this workspace.</p></div>
<div class="hidden group-has-[#ttb15-r4:checked]/tabs:block"><h3 class="text-xl font-bold">Settings</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Workspace name, permissions, integrations and billing.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-ttb15-ink/10 bg-ttb15-card/70 px-3.5 py-2 font-ttb15-mono text-[11.5px] text-ttb15-mut">translate-y-[calc(var(--i)*(100%+4px))] · spring row shift</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
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: Tailwind Sidebar Navigation with Sliding Indicator
Source: https://codefronts.com/navigation/tailwind-css-tabs/tailwind-sidebar-navigation-sliding-indicator/
An app-shell sidebar on warm stone: icon rows, an olive accent bar that slides vertically to the active row, and a content pane beside it. The original measured offsetTop; the port slides the bar with translate-y-[calc(var(--i)*(100%+4px))] — each step is one 44px row plus its 4px gap.
## HTML
```html
<section class="ttb-15 group/tabs grid min-h-svh w-full place-items-center bg-ttb15-bg px-5 py-14 font-ttb15-sans text-ttb15-ink" aria-label="Sidebar navigation with sliding indicator demo">
<div class="grid w-full max-w-2xl justify-items-center gap-7">
<div class="flex w-full flex-wrap gap-3 rounded-2xl bg-ttb15-panel p-3 shadow-[0_18px_40px_-20px_rgb(58_46_30/0.35)] ring-1 ring-ttb15-ink/8">
<div class="flex-[1_1_200px]">
<p class="flex items-center gap-2.5 px-3 pt-2 pb-4 text-sm font-bold"><span aria-hidden="true" class="grid size-7 place-items-center rounded-lg bg-ttb15-acc text-[11px] font-extrabold text-white">NB</span>Northbound</p>
<div role="radiogroup" aria-label="Workspace" class="relative flex flex-col gap-1 pl-3 [--i:0] has-[#ttb15-r2:checked]:[--i:1] has-[#ttb15-r3:checked]:[--i:2] has-[#ttb15-r4:checked]:[--i:3]">
<span aria-hidden="true" class="pointer-events-none absolute top-0 left-0 h-11 w-1 translate-y-[calc(var(--i)*(100%+4px))] rounded-r-full bg-ttb15-acc transition-transform duration-500 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none"></span>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r1" checked><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11l9-8 9 8v10h-6v-7H9v7H3z"/></svg>Home</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r2"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-9 4 18 3-9h6"/></svg>Activity</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r3"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0M16 4.5a3.5 3.5 0 0 1 0 7M18 14a6 6 0 0 1 3.5 6"/></svg>People</label>
<label class="group/tab flex h-11 cursor-pointer items-center gap-3 rounded-lg px-3 text-sm font-semibold text-ttb15-mut transition-[color,background-color,translate] duration-300 ease-[cubic-bezier(0.34,1.56,0.64,1)] hover:bg-ttb15-acc/8 hover:text-ttb15-ink has-checked:translate-x-1 has-checked:bg-ttb15-acc/10 has-checked:text-ttb15-acc-ink has-focus-visible:outline-2 has-focus-visible:outline-ttb15-acc motion-reduce:transition-none"><input class="sr-only" type="radio" name="ttb15" id="ttb15-r4"><svg aria-hidden="true" class="size-[18px] transition-transform duration-400 group-has-checked/tab:scale-110" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9l2.1 2.1M17 17l2.1 2.1M4.9 19.1L7 17M17 7l2.1-2.1"/></svg>Settings</label>
</div>
</div>
<div class="min-w-0 flex-[999_1_300px] rounded-xl bg-ttb15-card p-6 ring-1 ring-ttb15-ink/6">
<div class="hidden group-has-[#ttb15-r1:checked]/tabs:block"><h3 class="text-xl font-bold">Home</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Pinned projects, recent files and what changed since your last visit.</p></div>
<div class="hidden group-has-[#ttb15-r2:checked]/tabs:block"><h3 class="text-xl font-bold">Activity</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Comments, mentions and edits across every project you follow.</p></div>
<div class="hidden group-has-[#ttb15-r3:checked]/tabs:block"><h3 class="text-xl font-bold">People</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Members, guests and pending invitations for this workspace.</p></div>
<div class="hidden group-has-[#ttb15-r4:checked]/tabs:block"><h3 class="text-xl font-bold">Settings</h3><p class="mt-2 text-sm leading-relaxed text-ttb15-mut">Workspace name, permissions, integrations and billing.</p></div>
</div>
</div>
<p aria-hidden="true" class="rounded-full border border-ttb15-ink/10 bg-ttb15-card/70 px-3.5 py-2 font-ttb15-mono text-[11.5px] text-ttb15-mut">translate-y-[calc(var(--i)*(100%+4px))] · spring row shift</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttb15-bg: #e8dac5;
--color-ttb15-bg: oklch(0.894 0.032 78.1);
--color-ttb15-panel: #f1e7d7;
--color-ttb15-panel: oklch(0.932 0.024 79.7);
--color-ttb15-card: #fbf7f0;
--color-ttb15-card: oklch(0.977 0.010 81.8);
--color-ttb15-ink: #2e2416;
--color-ttb15-ink: oklch(0.268 0.028 75.4);
--color-ttb15-mut: #6a5b46;
--color-ttb15-mut: oklch(0.480 0.037 76.0);
--color-ttb15-acc: #5f8248;
--color-ttb15-acc: oklch(0.565 0.093 134.0);
--color-ttb15-acc-ink: #3f5c2f;
--color-ttb15-acc-ink: oklch(0.440 0.078 135.3);
--font-ttb15-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttb15-mono: 'JetBrains 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-15 {
width: 100%;
min-height: 100svh;
}
```How this works
Vertical index trick. Rows are 44px tall with a 4px gap. The bar is 44px tall, so 100% of its own height plus 4px is exactly one step:
<span class="absolute top-0 -left-3 h-11 w-1 rounded-r
translate-y-[calc(var(--i)*(100%+4px))] transition-transform"></span>The row itself shifts 4px right with a spring overshoot and its icon scales up, the same choreography as the original, now with no measuring.
Make it yours
- Dark sidebar: stone →
#1a1815, labels cream at 75%. - Collapse to icons: hide labels below
mdwithmax-md:sr-only. - Change the gap and the
+4pxtogether.
Gotchas — read before shipping
- The bar math assumes uniform row heights — no two-line labels.
- For routing, use real links (
<a aria-current="page">) instead of radios; style witharia-[current=page]:. - The bar sits in the nav's left padding; keep
pl-3on the nav or it clips.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() drives the index; inside Tailwind v4's floor.