15 / 24
All 24 in 24 Tailwind CSS Radio Buttons
- 01Tailwind Radio Button Cards (Selectable Cards)
- 02Tailwind Pill Radio Toggle (Segmented Control)
- 03Tailwind Radio Button with Icon and Description
- 04Tailwind Dark Mode Glowing Radio Button
- 05Accessible Tailwind Radio Button (appearance-none)
- 06Tailwind Radio Group with Badges
- 07Tailwind Animated Radio Button (Concentric Ripple)
- 08Tailwind Sliding Pill Radio Switch
- 09Tailwind 3D Sphere Radio Buttons
- 10Tailwind Ink Splash Radio Animation
- 11Tailwind Star Rating Radio Buttons
- 12Tailwind 3D Tilt Card Radio Selector
- 13Minimal Checkmark Radio Button in Tailwind
- 14Tailwind Color Swatch Radio Picker
- 15Tailwind Segmented Control Bar (View Switcher)
- 16Tailwind Neon Cyberpunk Radio Buttons
- 17Tailwind Pricing Plan Radio Cards
- 18Tailwind Liquid Wave Fill Radio Button
- 19Tailwind Image Radio Button Selector (Arch Mask)
- 20Tailwind Animated Gradient Border Radio Cards
- 21Tailwind Avatar Radio Picker (Profile Selector)
- 22Tailwind Stacked Radio List (Payment Method)
- 23Tailwind 3D Push Button Radio (Tactile Keys)
- 24Tailwind Pulsing Glow Radio Button (Status Picker)
Tailwind Segmented Control Bar (View Switcher)
List / Grid / Board switching done as a radio group: a flat toolbar of icon + label segments with a crisp underline that slides beneath the active view — and, new in the port, a live preview that re-lays itself out as you switch. The underline is an after: on the bar moved by has-[label:nth-child(n)_input:checked]:after:translate-x-*.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="trb-15 w-full min-h-svh grid place-items-center bg-trb15-bg px-6 py-14 font-trb15-sans text-trb15-ink" aria-label="Segmented control bar radio demo">
<fieldset class="group/view grid w-full max-w-[26rem] justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Project view</legend>
<div class="relative grid w-full grid-cols-3 overflow-hidden rounded-xl border border-trb15-line bg-white shadow-trb15-bar after:absolute after:bottom-0 after:left-0 after:h-[3px] after:w-1/3 after:rounded-t-[3px] after:bg-trb15-acc after:transition-transform after:duration-300 after:ease-trb15-glide after:content-[''] has-[label:nth-child(2)_input:checked]:after:translate-x-full has-[label:nth-child(3)_input:checked]:after:translate-x-[200%] motion-reduce:after:transition-none [&>label+label]:border-l [&>label+label]:border-trb15-line">
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="list" class="sr-only" checked><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">List</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="grid" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.5"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Grid</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="board" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="5" height="17" rx="1.5"/><rect x="11" y="3.5" width="5" height="11" rx="1.5"/><rect x="18.5" y="3.5" width="2" height="7" rx="1"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Board</span></label>
</div>
<div aria-hidden="true" class="grid w-full grid-cols-1 items-start gap-2 rounded-2xl border border-dashed border-trb15-line bg-white/60 p-3 group-has-[[value=grid]:checked]/view:grid-cols-3 group-has-[[value=board]:checked]/view:grid-cols-3 *:rounded-lg *:bg-trb15-block *:transition-[height] *:duration-300 *:h-7 group-has-[[value=grid]:checked]/view:*:h-16 motion-reduce:*:transition-none">
<i></i><i></i><i></i><i class="group-has-[[value=board]:checked]/view:h-16"></i><i class="group-has-[[value=board]:checked]/view:h-10"></i><i class="group-has-[[value=board]:checked]/view:h-4"></i>
</div>
</fieldset>
</section><section class="trb-15 w-full min-h-svh grid place-items-center bg-trb15-bg px-6 py-14 font-trb15-sans text-trb15-ink" aria-label="Segmented control bar radio demo">
<fieldset class="group/view grid w-full max-w-[26rem] justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Project view</legend>
<div class="relative grid w-full grid-cols-3 overflow-hidden rounded-xl border border-trb15-line bg-white shadow-trb15-bar after:absolute after:bottom-0 after:left-0 after:h-[3px] after:w-1/3 after:rounded-t-[3px] after:bg-trb15-acc after:transition-transform after:duration-300 after:ease-trb15-glide after:content-[''] has-[label:nth-child(2)_input:checked]:after:translate-x-full has-[label:nth-child(3)_input:checked]:after:translate-x-[200%] motion-reduce:after:transition-none [&>label+label]:border-l [&>label+label]:border-trb15-line">
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="list" class="sr-only" checked><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">List</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="grid" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.5"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Grid</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="board" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="5" height="17" rx="1.5"/><rect x="11" y="3.5" width="5" height="11" rx="1.5"/><rect x="18.5" y="3.5" width="2" height="7" rx="1"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Board</span></label>
</div>
<div aria-hidden="true" class="grid w-full grid-cols-1 items-start gap-2 rounded-2xl border border-dashed border-trb15-line bg-white/60 p-3 group-has-[[value=grid]:checked]/view:grid-cols-3 group-has-[[value=board]:checked]/view:grid-cols-3 *:rounded-lg *:bg-trb15-block *:transition-[height] *:duration-300 *:h-7 group-has-[[value=grid]:checked]/view:*:h-16 motion-reduce:*:transition-none">
<i></i><i></i><i></i><i class="group-has-[[value=board]:checked]/view:h-16"></i><i class="group-has-[[value=board]:checked]/view:h-10"></i><i class="group-has-[[value=board]:checked]/view:h-4"></i>
</div>
</fieldset>
</section>@import "tailwindcss";
@theme {
--color-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-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-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-15 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-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=Geist:wght@400..800&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&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
.seg + .seg { border-left }became a parent arbitrary variant,[&>label+label]:border-l, so the segments themselves are identical.- The underline's three rules became
after:utilities plus twohas-[…]:after:translate-x-*variants. - UI upgrade from the CSS original: a decorative preview under the bar re-lays itself out (rows → tiles → uneven board columns) — users see the preference before they commit to it.
- UI upgrade: the bar stretches to its container instead of fixed 118px segments, and inactive labels rose from
#69708a(4.6:1) to 5.9: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 Segmented Control Bar (View Switcher)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-segmented-control-bar-view-switcher/
List / Grid / Board switching done as a radio group: a flat toolbar of icon + label segments with a crisp underline that slides beneath the active view — and, new in the port, a live preview that re-lays itself out as you switch. The underline is an after: on the bar moved by has-[label:nth-child(n)_input:checked]:after:translate-x-*.
## HTML
```html
<section class="trb-15 w-full min-h-svh grid place-items-center bg-trb15-bg px-6 py-14 font-trb15-sans text-trb15-ink" aria-label="Segmented control bar radio demo">
<fieldset class="group/view grid w-full max-w-[26rem] justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Project view</legend>
<div class="relative grid w-full grid-cols-3 overflow-hidden rounded-xl border border-trb15-line bg-white shadow-trb15-bar after:absolute after:bottom-0 after:left-0 after:h-[3px] after:w-1/3 after:rounded-t-[3px] after:bg-trb15-acc after:transition-transform after:duration-300 after:ease-trb15-glide after:content-[''] has-[label:nth-child(2)_input:checked]:after:translate-x-full has-[label:nth-child(3)_input:checked]:after:translate-x-[200%] motion-reduce:after:transition-none [&>label+label]:border-l [&>label+label]:border-trb15-line">
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="list" class="sr-only" checked><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">List</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="grid" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.5"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Grid</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="board" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="5" height="17" rx="1.5"/><rect x="11" y="3.5" width="5" height="11" rx="1.5"/><rect x="18.5" y="3.5" width="2" height="7" rx="1"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Board</span></label>
</div>
<div aria-hidden="true" class="grid w-full grid-cols-1 items-start gap-2 rounded-2xl border border-dashed border-trb15-line bg-white/60 p-3 group-has-[[value=grid]:checked]/view:grid-cols-3 group-has-[[value=board]:checked]/view:grid-cols-3 *:rounded-lg *:bg-trb15-block *:transition-[height] *:duration-300 *:h-7 group-has-[[value=grid]:checked]/view:*:h-16 motion-reduce:*:transition-none">
<i></i><i></i><i></i><i class="group-has-[[value=board]:checked]/view:h-16"></i><i class="group-has-[[value=board]:checked]/view:h-10"></i><i class="group-has-[[value=board]:checked]/view:h-4"></i>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-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 Segmented Control Bar (View Switcher)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-segmented-control-bar-view-switcher/
List / Grid / Board switching done as a radio group: a flat toolbar of icon + label segments with a crisp underline that slides beneath the active view — and, new in the port, a live preview that re-lays itself out as you switch. The underline is an after: on the bar moved by has-[label:nth-child(n)_input:checked]:after:translate-x-*.
## HTML
```html
<section class="trb-15 w-full min-h-svh grid place-items-center bg-trb15-bg px-6 py-14 font-trb15-sans text-trb15-ink" aria-label="Segmented control bar radio demo">
<fieldset class="group/view grid w-full max-w-[26rem] justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Project view</legend>
<div class="relative grid w-full grid-cols-3 overflow-hidden rounded-xl border border-trb15-line bg-white shadow-trb15-bar after:absolute after:bottom-0 after:left-0 after:h-[3px] after:w-1/3 after:rounded-t-[3px] after:bg-trb15-acc after:transition-transform after:duration-300 after:ease-trb15-glide after:content-[''] has-[label:nth-child(2)_input:checked]:after:translate-x-full has-[label:nth-child(3)_input:checked]:after:translate-x-[200%] motion-reduce:after:transition-none [&>label+label]:border-l [&>label+label]:border-trb15-line">
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="list" class="sr-only" checked><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">List</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="grid" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.5"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.5"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.5"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Grid</span></label>
<label class="group relative flex min-h-12 cursor-pointer items-center justify-center gap-2 px-4 transition-colors hover:bg-trb15-hover has-focus-visible:outline-3 has-focus-visible:-outline-offset-3 has-focus-visible:outline-trb15-acc"><input type="radio" name="trb-15" value="board" class="sr-only"><svg class="size-[18px] text-trb15-icon transition-colors group-has-checked:text-trb15-acc" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><rect x="3.5" y="3.5" width="5" height="17" rx="1.5"/><rect x="11" y="3.5" width="5" height="11" rx="1.5"/><rect x="18.5" y="3.5" width="2" height="7" rx="1"/></svg><span class="text-sm font-semibold text-trb15-mut transition-colors group-has-checked:text-trb15-acc">Board</span></label>
</div>
<div aria-hidden="true" class="grid w-full grid-cols-1 items-start gap-2 rounded-2xl border border-dashed border-trb15-line bg-white/60 p-3 group-has-[[value=grid]:checked]/view:grid-cols-3 group-has-[[value=board]:checked]/view:grid-cols-3 *:rounded-lg *:bg-trb15-block *:transition-[height] *:duration-300 *:h-7 group-has-[[value=grid]:checked]/view:*:h-16 motion-reduce:*:transition-none">
<i></i><i></i><i></i><i class="group-has-[[value=board]:checked]/view:h-16"></i><i class="group-has-[[value=board]:checked]/view:h-10"></i><i class="group-has-[[value=board]:checked]/view:h-4"></i>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb15-bg: #f7f8fa;
--color-trb15-bg: oklch(0.978 0.004 265);
--color-trb15-ink: #1c2130;
--color-trb15-ink: oklch(0.24 0.03 270);
--color-trb15-mut: #5d6479;
--color-trb15-mut: oklch(0.5 0.03 270);
--color-trb15-icon: #7a8197;
--color-trb15-icon: oklch(0.6 0.03 270);
--color-trb15-line: #e4e6ee;
--color-trb15-line: oklch(0.925 0.01 270);
--color-trb15-hover: #f4f5f9;
--color-trb15-hover: oklch(0.968 0.005 270);
--color-trb15-block: #e6e9f4;
--color-trb15-block: oklch(0.93 0.018 270);
--color-trb15-acc: #3450d4;
--color-trb15-acc: oklch(0.5 0.19 265);
--shadow-trb15-bar: 0 1px 3px oklch(0.25 0.03 270 / 0.06);
--ease-trb15-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb15-sans: 'Geist', system-ui, sans-serif;
}
/* 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. */
.trb-15 {
width: 100%;
min-height: 100svh;
}
```How this works
Same trick, different language. Like the pill slider, the indicator is a pseudo-element translated by nth-child state — here flat, squared and under the bar. Dividers come from a parent arbitrary variant, [&>label+label]:border-l, so segments carry no position logic.
Radios or tabs? Radios are right when the choice is a value — a saved view preference, a filter. If the control swaps whole content panels, use the ARIA tabs pattern instead. The preview here is a decorative illustration of the preference.
Make it yours
- Underline weight:
after:h-[3px]. - Top indicator for bottom toolbars:
after:top-0instead ofafter:bottom-0. - Add a segment:
grid-cols-4,after:w-1/4, one more translate variant.
Gotchas — read before shipping
- Segments must be equal width (
grid-cols-3) for percentage translation to line up. - Use an inset focus ring (
-outline-offset-3) — the bar'soverflow-hiddenclips an outset one. - Mark the preview
aria-hidden; it's a picture of the setting, not content.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Without :has() the active segment still recolours; the underline and preview stay put.