05 / 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)
Accessible Tailwind Radio Button (appearance-none)
The reference implementation every custom radio should start from: style the native <input type="radio"> itself with appearance-none. No hidden input, no wrapper spans, no ARIA — checked:, disabled: and focus-visible: sit directly on the element that owns those states, and forced-colors:appearance-auto hands the control back to Windows High Contrast.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="trb-05 w-full min-h-svh grid place-items-center bg-trb05-bg px-6 py-14 font-trb05-sans text-trb05-ink" aria-label="Accessible Tailwind radio buttons demo">
<fieldset class="w-full max-w-[24rem] rounded-2xl border border-trb05-line bg-white p-6 shadow-trb05-card">
<legend class="sr-only">How did you hear about us?</legend>
<p aria-hidden="true" class="text-xl font-bold tracking-[-0.02em]">How did you hear about us?</p>
<div class="mt-3 grid">
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="search" checked class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Search engine</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="social" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Social media</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="friend" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Friend or colleague</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover has-disabled:cursor-not-allowed has-disabled:text-trb05-off has-disabled:hover:bg-transparent"><input type="radio" name="trb-05" value="event" disabled aria-describedby="trb-05-note" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:content-[''] disabled:cursor-not-allowed disabled:border-trb05-line disabled:bg-trb05-hover forced-colors:appearance-auto"><span class="text-[15.5px]">Event</span><em id="trb-05-note" class="ml-auto rounded-full bg-trb05-hover px-2.5 py-1 text-xs font-medium not-italic text-trb05-mut">Unavailable</em></label>
</div>
<p class="mt-4 border-t border-trb05-line pt-4 text-[13px] text-trb05-mut">Use <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↑</kbd> <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↓</kbd> to move between options.</p>
</fieldset>
</section><section class="trb-05 w-full min-h-svh grid place-items-center bg-trb05-bg px-6 py-14 font-trb05-sans text-trb05-ink" aria-label="Accessible Tailwind radio buttons demo">
<fieldset class="w-full max-w-[24rem] rounded-2xl border border-trb05-line bg-white p-6 shadow-trb05-card">
<legend class="sr-only">How did you hear about us?</legend>
<p aria-hidden="true" class="text-xl font-bold tracking-[-0.02em]">How did you hear about us?</p>
<div class="mt-3 grid">
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="search" checked class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Search engine</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="social" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Social media</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="friend" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Friend or colleague</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover has-disabled:cursor-not-allowed has-disabled:text-trb05-off has-disabled:hover:bg-transparent"><input type="radio" name="trb-05" value="event" disabled aria-describedby="trb-05-note" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:content-[''] disabled:cursor-not-allowed disabled:border-trb05-line disabled:bg-trb05-hover forced-colors:appearance-auto"><span class="text-[15.5px]">Event</span><em id="trb-05-note" class="ml-auto rounded-full bg-trb05-hover px-2.5 py-1 text-xs font-medium not-italic text-trb05-mut">Unavailable</em></label>
</div>
<p class="mt-4 border-t border-trb05-line pt-4 text-[13px] text-trb05-mut">Use <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↑</kbd> <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↓</kbd> to move between options.</p>
</fieldset>
</section>@import "tailwindcss";
@theme {
--color-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
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-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
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.
- The CSS original's four input rules collapsed into one class list on the input itself:
checked:,checked:before:,focus-visible:anddisabled:are all native state variants. - The
.row--disabledmodifier class is gone — the label reads its input withhas-disabled:, so a CMS only toggles thedisabledattribute. - UI upgrade from the CSS original:
forced-colors:appearance-autorestores the native radio in Windows High Contrast, where the custom dot would otherwise vanish. - UI upgrade: the group sits on a card with a keyboard hint; "(unavailable)" became a chip linked with
aria-describedby, and its text rose from#9aa0b2(2.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: Accessible Tailwind Radio Button (appearance-none)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/accessible-tailwind-radio-button-appearance-none/
The reference implementation every custom radio should start from: style the native <input type="radio"> itself with appearance-none. No hidden input, no wrapper spans, no ARIA — checked:, disabled: and focus-visible: sit directly on the element that owns those states, and forced-colors:appearance-auto hands the control back to Windows High Contrast.
## HTML
```html
<section class="trb-05 w-full min-h-svh grid place-items-center bg-trb05-bg px-6 py-14 font-trb05-sans text-trb05-ink" aria-label="Accessible Tailwind radio buttons demo">
<fieldset class="w-full max-w-[24rem] rounded-2xl border border-trb05-line bg-white p-6 shadow-trb05-card">
<legend class="sr-only">How did you hear about us?</legend>
<p aria-hidden="true" class="text-xl font-bold tracking-[-0.02em]">How did you hear about us?</p>
<div class="mt-3 grid">
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="search" checked class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Search engine</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="social" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Social media</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="friend" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Friend or colleague</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover has-disabled:cursor-not-allowed has-disabled:text-trb05-off has-disabled:hover:bg-transparent"><input type="radio" name="trb-05" value="event" disabled aria-describedby="trb-05-note" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:content-[''] disabled:cursor-not-allowed disabled:border-trb05-line disabled:bg-trb05-hover forced-colors:appearance-auto"><span class="text-[15.5px]">Event</span><em id="trb-05-note" class="ml-auto rounded-full bg-trb05-hover px-2.5 py-1 text-xs font-medium not-italic text-trb05-mut">Unavailable</em></label>
</div>
<p class="mt-4 border-t border-trb05-line pt-4 text-[13px] text-trb05-mut">Use <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↑</kbd> <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↓</kbd> to move between options.</p>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
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: Accessible Tailwind Radio Button (appearance-none)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/accessible-tailwind-radio-button-appearance-none/
The reference implementation every custom radio should start from: style the native <input type="radio"> itself with appearance-none. No hidden input, no wrapper spans, no ARIA — checked:, disabled: and focus-visible: sit directly on the element that owns those states, and forced-colors:appearance-auto hands the control back to Windows High Contrast.
## HTML
```html
<section class="trb-05 w-full min-h-svh grid place-items-center bg-trb05-bg px-6 py-14 font-trb05-sans text-trb05-ink" aria-label="Accessible Tailwind radio buttons demo">
<fieldset class="w-full max-w-[24rem] rounded-2xl border border-trb05-line bg-white p-6 shadow-trb05-card">
<legend class="sr-only">How did you hear about us?</legend>
<p aria-hidden="true" class="text-xl font-bold tracking-[-0.02em]">How did you hear about us?</p>
<div class="mt-3 grid">
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="search" checked class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Search engine</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="social" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Social media</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover"><input type="radio" name="trb-05" value="friend" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:transition-transform before:duration-250 before:ease-trb05-spring before:content-[''] checked:border-trb05-acc checked:before:scale-100 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-trb05-acc motion-reduce:before:transition-none forced-colors:appearance-auto"><span class="text-[15.5px]">Friend or colleague</span></label>
<label class="flex min-h-12 cursor-pointer items-center gap-3 rounded-[10px] px-2 transition-colors hover:bg-trb05-hover has-disabled:cursor-not-allowed has-disabled:text-trb05-off has-disabled:hover:bg-transparent"><input type="radio" name="trb-05" value="event" disabled aria-describedby="trb-05-note" class="grid size-[22px] flex-none cursor-pointer appearance-none place-items-center rounded-full border-2 border-trb05-ring bg-white transition-colors before:size-[11px] before:scale-0 before:rounded-full before:bg-trb05-acc before:content-[''] disabled:cursor-not-allowed disabled:border-trb05-line disabled:bg-trb05-hover forced-colors:appearance-auto"><span class="text-[15.5px]">Event</span><em id="trb-05-note" class="ml-auto rounded-full bg-trb05-hover px-2.5 py-1 text-xs font-medium not-italic text-trb05-mut">Unavailable</em></label>
</div>
<p class="mt-4 border-t border-trb05-line pt-4 text-[13px] text-trb05-mut">Use <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↑</kbd> <kbd class="rounded border border-trb05-line px-1.5 py-0.5 text-xs font-medium">↓</kbd> to move between options.</p>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb05-bg: #f4f4f7;
--color-trb05-bg: oklch(0.965 0.004 285);
--color-trb05-ink: #20242f;
--color-trb05-ink: oklch(0.25 0.02 270);
--color-trb05-mut: #5e6475;
--color-trb05-mut: oklch(0.5 0.02 270);
--color-trb05-off: #767c8e;
--color-trb05-off: oklch(0.58 0.02 270);
--color-trb05-line: #e4e6ec;
--color-trb05-line: oklch(0.925 0.008 270);
--color-trb05-hover: #f1f2f6;
--color-trb05-hover: oklch(0.96 0.005 270);
--color-trb05-ring: #a9afc0;
--color-trb05-ring: oklch(0.76 0.02 270);
--color-trb05-acc: #2860d8;
--color-trb05-acc: oklch(0.52 0.18 260);
--shadow-trb05-card: 0 1px 2px oklch(0.2 0.02 270 / 0.05), 0 12px 32px -16px oklch(0.2 0.02 270 / 0.18);
--ease-trb05-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb05-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-05 {
width: 100%;
min-height: 100svh;
}
```How this works
The element is the radio. Because nothing is hidden, label association, arrow-key navigation, form submission and every pseudo-class work untouched. The dot is the input's own before::
<input type="radio" class="appearance-none grid place-items-center
size-[22px] rounded-full border-2 …
before:size-[11px] before:scale-0 checked:before:scale-100
focus-visible:outline-3 forced-colors:appearance-auto">Forced colours. Windows High Contrast strips backgrounds, so a custom dot can disappear. One variant restores the native control there — the cheapest accessibility win in the collection.
Make it yours
- One token,
--color-trb05-acc, recolours border, dot and focus ring. - Size: change
size-[22px]; set the dot to half the box. - Stiffer check: replace
ease-trb05-springwithease-out.
Gotchas — read before shipping
- Never remove the outline without a replacement —
outline-nonealone fails WCAG 2.4.7. - Pseudo-elements on inputs need Safari 15.4+; Tailwind v4's floor is already above that.
- Keep each row
min-h-12— the 22px circle is the visual, the label is the target.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
No :has() needed for the control itself — the most compatible demo here. has-disabled: on the label is enhancement.