02 / 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 Pill Radio Toggle (Segmented Control)
The iOS-style billing toggle — Monthly / Yearly with a save badge — where a white thumb glides under the chosen option. The thumb is a before: pseudo-element on the track, moved by one arbitrary variant: has-[label:nth-child(2)_input:checked]:before:translate-x-full. The price below re-renders from the same radio state.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="trb-02 w-full min-h-svh grid place-items-center bg-trb02-bg px-6 py-14 font-trb02-sans text-trb02-ink" aria-label="Pill radio toggle segmented control demo">
<fieldset class="group/bill grid justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Billing period</legend>
<div class="relative isolate grid grid-flow-col auto-cols-fr rounded-full bg-trb02-track p-[5px] shadow-[inset_0_1px_3px_oklch(0.2_0.03_265/0.1)] before:absolute before:top-[5px] before:left-[5px] before:-z-10 before:h-[calc(100%-10px)] before:w-[calc(50%-5px)] before:rounded-full before:bg-white before:shadow-trb02-thumb before:transition-transform before:duration-350 before:ease-trb02-glide before:content-[''] has-[label:nth-child(2)_input:checked]:before:translate-x-full motion-reduce:before:transition-none">
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="monthly" class="sr-only" checked>
<span class="text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Monthly</span>
</label>
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="yearly" class="sr-only">
<span class="flex items-center gap-2 text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Yearly <em class="rounded-full bg-trb02-acc/15 px-2 py-0.5 text-[11px] font-bold not-italic text-trb02-acc-ink">−20%</em></span>
</label>
</div>
<p class="grid justify-items-center text-center">
<span class="text-4xl font-extrabold tracking-[-0.03em] tabular-nums"><span class="group-has-[[value=yearly]:checked]/bill:hidden">$24</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">$19</span><small class="text-base font-semibold text-trb02-mut"> / month</small></span>
<span class="mt-1 text-[13px] text-trb02-mut"><span class="group-has-[[value=yearly]:checked]/bill:hidden">Billed monthly · cancel anytime</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">Billed $228 yearly · you save $60</span></span>
</p>
</fieldset>
</section><section class="trb-02 w-full min-h-svh grid place-items-center bg-trb02-bg px-6 py-14 font-trb02-sans text-trb02-ink" aria-label="Pill radio toggle segmented control demo">
<fieldset class="group/bill grid justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Billing period</legend>
<div class="relative isolate grid grid-flow-col auto-cols-fr rounded-full bg-trb02-track p-[5px] shadow-[inset_0_1px_3px_oklch(0.2_0.03_265/0.1)] before:absolute before:top-[5px] before:left-[5px] before:-z-10 before:h-[calc(100%-10px)] before:w-[calc(50%-5px)] before:rounded-full before:bg-white before:shadow-trb02-thumb before:transition-transform before:duration-350 before:ease-trb02-glide before:content-[''] has-[label:nth-child(2)_input:checked]:before:translate-x-full motion-reduce:before:transition-none">
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="monthly" class="sr-only" checked>
<span class="text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Monthly</span>
</label>
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="yearly" class="sr-only">
<span class="flex items-center gap-2 text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Yearly <em class="rounded-full bg-trb02-acc/15 px-2 py-0.5 text-[11px] font-bold not-italic text-trb02-acc-ink">−20%</em></span>
</label>
</div>
<p class="grid justify-items-center text-center">
<span class="text-4xl font-extrabold tracking-[-0.03em] tabular-nums"><span class="group-has-[[value=yearly]:checked]/bill:hidden">$24</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">$19</span><small class="text-base font-semibold text-trb02-mut"> / month</small></span>
<span class="mt-1 text-[13px] text-trb02-mut"><span class="group-has-[[value=yearly]:checked]/bill:hidden">Billed monthly · cancel anytime</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">Billed $228 yearly · you save $60</span></span>
</p>
</fieldset>
</section>@import "tailwindcss";
@theme {
--color-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
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-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
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.
.track::beforeand its:has(nth-child)rule became ninebefore:utilities plus one arbitraryhas-[…]:variant on the element that owns the thumb.- The badge's
color-mix(in oklab, var(--accent) 16%, white)became the opacity modifierbg-trb02-acc/15. - UI upgrade from the CSS original: a live price readout (
$24→$19, plus the yearly total and saving) now answers "what does this toggle do?" — driven bygroup-has-[[value=yearly]:checked]/bill:. - UI upgrade: the badge text moved to a darker
acc-inktoken (5.8:1 on its tint; the original green-on-green was 3.1:1), and the hint line was replaced by the readout.
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 Pill Radio Toggle (Segmented Control)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-pill-radio-toggle-segmented-control/
The iOS-style billing toggle — Monthly / Yearly with a save badge — where a white thumb glides under the chosen option. The thumb is a before: pseudo-element on the track, moved by one arbitrary variant: has-[label:nth-child(2)_input:checked]:before:translate-x-full. The price below re-renders from the same radio state.
## HTML
```html
<section class="trb-02 w-full min-h-svh grid place-items-center bg-trb02-bg px-6 py-14 font-trb02-sans text-trb02-ink" aria-label="Pill radio toggle segmented control demo">
<fieldset class="group/bill grid justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Billing period</legend>
<div class="relative isolate grid grid-flow-col auto-cols-fr rounded-full bg-trb02-track p-[5px] shadow-[inset_0_1px_3px_oklch(0.2_0.03_265/0.1)] before:absolute before:top-[5px] before:left-[5px] before:-z-10 before:h-[calc(100%-10px)] before:w-[calc(50%-5px)] before:rounded-full before:bg-white before:shadow-trb02-thumb before:transition-transform before:duration-350 before:ease-trb02-glide before:content-[''] has-[label:nth-child(2)_input:checked]:before:translate-x-full motion-reduce:before:transition-none">
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="monthly" class="sr-only" checked>
<span class="text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Monthly</span>
</label>
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="yearly" class="sr-only">
<span class="flex items-center gap-2 text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Yearly <em class="rounded-full bg-trb02-acc/15 px-2 py-0.5 text-[11px] font-bold not-italic text-trb02-acc-ink">−20%</em></span>
</label>
</div>
<p class="grid justify-items-center text-center">
<span class="text-4xl font-extrabold tracking-[-0.03em] tabular-nums"><span class="group-has-[[value=yearly]:checked]/bill:hidden">$24</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">$19</span><small class="text-base font-semibold text-trb02-mut"> / month</small></span>
<span class="mt-1 text-[13px] text-trb02-mut"><span class="group-has-[[value=yearly]:checked]/bill:hidden">Billed monthly · cancel anytime</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">Billed $228 yearly · you save $60</span></span>
</p>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
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 Pill Radio Toggle (Segmented Control)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-pill-radio-toggle-segmented-control/
The iOS-style billing toggle — Monthly / Yearly with a save badge — where a white thumb glides under the chosen option. The thumb is a before: pseudo-element on the track, moved by one arbitrary variant: has-[label:nth-child(2)_input:checked]:before:translate-x-full. The price below re-renders from the same radio state.
## HTML
```html
<section class="trb-02 w-full min-h-svh grid place-items-center bg-trb02-bg px-6 py-14 font-trb02-sans text-trb02-ink" aria-label="Pill radio toggle segmented control demo">
<fieldset class="group/bill grid justify-items-center gap-5">
<legend class="mb-1 text-center text-2xl font-bold tracking-[-0.02em]">Billing period</legend>
<div class="relative isolate grid grid-flow-col auto-cols-fr rounded-full bg-trb02-track p-[5px] shadow-[inset_0_1px_3px_oklch(0.2_0.03_265/0.1)] before:absolute before:top-[5px] before:left-[5px] before:-z-10 before:h-[calc(100%-10px)] before:w-[calc(50%-5px)] before:rounded-full before:bg-white before:shadow-trb02-thumb before:transition-transform before:duration-350 before:ease-trb02-glide before:content-[''] has-[label:nth-child(2)_input:checked]:before:translate-x-full motion-reduce:before:transition-none">
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="monthly" class="sr-only" checked>
<span class="text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Monthly</span>
</label>
<label class="group relative grid min-h-11 min-w-[9.5rem] cursor-pointer place-items-center rounded-full px-5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb02-acc">
<input type="radio" name="trb-02" value="yearly" class="sr-only">
<span class="flex items-center gap-2 text-[15px] font-semibold text-trb02-mut transition-colors duration-300 group-has-checked:text-trb02-ink">Yearly <em class="rounded-full bg-trb02-acc/15 px-2 py-0.5 text-[11px] font-bold not-italic text-trb02-acc-ink">−20%</em></span>
</label>
</div>
<p class="grid justify-items-center text-center">
<span class="text-4xl font-extrabold tracking-[-0.03em] tabular-nums"><span class="group-has-[[value=yearly]:checked]/bill:hidden">$24</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">$19</span><small class="text-base font-semibold text-trb02-mut"> / month</small></span>
<span class="mt-1 text-[13px] text-trb02-mut"><span class="group-has-[[value=yearly]:checked]/bill:hidden">Billed monthly · cancel anytime</span><span class="hidden group-has-[[value=yearly]:checked]/bill:inline">Billed $228 yearly · you save $60</span></span>
</p>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb02-bg: #f7f8fb;
--color-trb02-bg: oklch(0.978 0.005 265);
--color-trb02-track: #e9ebf2;
--color-trb02-track: oklch(0.938 0.01 270);
--color-trb02-ink: #1c2130;
--color-trb02-ink: oklch(0.24 0.03 270);
--color-trb02-mut: #5d6479;
--color-trb02-mut: oklch(0.5 0.03 270);
--color-trb02-acc: #1f9d5c;
--color-trb02-acc: oklch(0.6 0.15 155);
--color-trb02-acc-ink: #11683b;
--color-trb02-acc-ink: oklch(0.45 0.12 155);
--shadow-trb02-thumb: 0 2px 8px oklch(0.2 0.03 265 / 0.16);
--ease-trb02-glide: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb02-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-02 {
width: 100%;
min-height: 100svh;
}
```How this works
One pseudo-element, one arbitrary variant. The CSS original's .track:has(label:nth-child(2) input:checked)::before becomes a single class on the track; the underscore carries the descendant-combinator space.
<div class="relative isolate grid grid-flow-col auto-cols-fr …
before:absolute before:-z-10 before:w-[calc(50%-5px)] …
has-[label:nth-child(2)_input:checked]:before:translate-x-full">Why the price moved in. A toggle whose effect is only a badge makes users hunt for the consequence. The readout under the track shows $24 / month or $19 / month, billed $228 yearly from the same checked radio, still with zero JavaScript.
Make it yours
- Third segment: set the thumb to
w-[calc(33.333%-3.33px)]and addhas-[label:nth-child(3)_input:checked]:before:translate-x-[200%]. - Accent:
--color-trb02-accdrives the badge and the focus ring. - Glide feel: edit
--ease-trb02-glide.
Gotchas — read before shipping
- Translate with
translate-x-full, neverleft-*— the thumb stays on the compositor. - Without
isolateon the track,before:-z-10escapes behind the section background and the thumb vanishes. - Keep segments at
min-h-11(44px) — segmented controls are prime thumb UI.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Without :has() the options still select and the checked label keeps its ink; only the sliding thumb stops.