03 / 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 Radio Button with Icon and Description
The settings-page staple: a vertical option list where every row carries an icon tile, a title, a one-line description and a custom radio on the right. Three grid columns, one has-checked: on the row, and group-has-checked: on everything that responds — tint, tile, icon and dot.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="trb-03 w-full min-h-svh grid place-items-center bg-trb03-bg px-6 py-14 font-trb03-sans text-trb03-ink" aria-label="Radio options with icon and description demo">
<fieldset class="w-full max-w-[29rem]">
<legend class="text-2xl font-extrabold tracking-[-0.02em]">Notifications</legend>
<p class="mt-1 text-sm text-trb03-mut">Choose what reaches your inbox.</p>
<div class="mt-5 grid gap-2.5">
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="all" class="sr-only" checked>
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10 19a2 2 0 0 0 4 0"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">All activity</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Every comment, mention and follow — delivered instantly.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="mentions" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Mentions only</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Stay focused — only direct @mentions get through.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="muted" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5a3 3 0 0 1 6 0c3 1 3 4 3 6 0 2 1 4 1 4H5s1-2 1-4c0-2 0-5 3-6Z" opacity=".35"/><path d="m4 4 16 16"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Muted</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Nothing until you turn notifications back on.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
</div>
</fieldset>
</section><section class="trb-03 w-full min-h-svh grid place-items-center bg-trb03-bg px-6 py-14 font-trb03-sans text-trb03-ink" aria-label="Radio options with icon and description demo">
<fieldset class="w-full max-w-[29rem]">
<legend class="text-2xl font-extrabold tracking-[-0.02em]">Notifications</legend>
<p class="mt-1 text-sm text-trb03-mut">Choose what reaches your inbox.</p>
<div class="mt-5 grid gap-2.5">
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="all" class="sr-only" checked>
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10 19a2 2 0 0 0 4 0"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">All activity</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Every comment, mention and follow — delivered instantly.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="mentions" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Mentions only</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Stay focused — only direct @mentions get through.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="muted" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5a3 3 0 0 1 6 0c3 1 3 4 3 6 0 2 1 4 1 4H5s1-2 1-4c0-2 0-5 3-6Z" opacity=".35"/><path d="m4 4 16 16"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Muted</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Nothing until you turn notifications back on.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
</div>
</fieldset>
</section>@import "tailwindcss";
@theme {
--color-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
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-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
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..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=Plus+Jakarta+Sans:wght@400..800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Every
color-mix(in oklab, var(--accent) N%, white)became an opacity modifier (/5,/14,/35) — the same derivation, readable in the class list. .tile svgbecame the child variant*:size-[22px]; there is no descendant CSS left.- UI upgrade from the CSS original: the chosen row's icon gets a small
motion-safe:scale so the tile reads as "on", and a legend subline explains the question. - UI upgrade: descriptions darkened from
#6d7389(4.4:1 on the tint) to#5f6579(5.7:1), and the accent was deepened from0.58to0.55lightness so the checked border clears 3:1 on white.
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 Radio Button with Icon and Description
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-radio-button-with-icon-and-description/
The settings-page staple: a vertical option list where every row carries an icon tile, a title, a one-line description and a custom radio on the right. Three grid columns, one has-checked: on the row, and group-has-checked: on everything that responds — tint, tile, icon and dot.
## HTML
```html
<section class="trb-03 w-full min-h-svh grid place-items-center bg-trb03-bg px-6 py-14 font-trb03-sans text-trb03-ink" aria-label="Radio options with icon and description demo">
<fieldset class="w-full max-w-[29rem]">
<legend class="text-2xl font-extrabold tracking-[-0.02em]">Notifications</legend>
<p class="mt-1 text-sm text-trb03-mut">Choose what reaches your inbox.</p>
<div class="mt-5 grid gap-2.5">
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="all" class="sr-only" checked>
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10 19a2 2 0 0 0 4 0"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">All activity</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Every comment, mention and follow — delivered instantly.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="mentions" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Mentions only</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Stay focused — only direct @mentions get through.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="muted" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5a3 3 0 0 1 6 0c3 1 3 4 3 6 0 2 1 4 1 4H5s1-2 1-4c0-2 0-5 3-6Z" opacity=".35"/><path d="m4 4 16 16"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Muted</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Nothing until you turn notifications back on.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
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 Radio Button with Icon and Description
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-radio-button-with-icon-and-description/
The settings-page staple: a vertical option list where every row carries an icon tile, a title, a one-line description and a custom radio on the right. Three grid columns, one has-checked: on the row, and group-has-checked: on everything that responds — tint, tile, icon and dot.
## HTML
```html
<section class="trb-03 w-full min-h-svh grid place-items-center bg-trb03-bg px-6 py-14 font-trb03-sans text-trb03-ink" aria-label="Radio options with icon and description demo">
<fieldset class="w-full max-w-[29rem]">
<legend class="text-2xl font-extrabold tracking-[-0.02em]">Notifications</legend>
<p class="mt-1 text-sm text-trb03-mut">Choose what reaches your inbox.</p>
<div class="mt-5 grid gap-2.5">
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="all" class="sr-only" checked>
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6"/><path d="M10 19a2 2 0 0 0 4 0"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">All activity</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Every comment, mention and follow — delivered instantly.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="mentions" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Mentions only</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Stay focused — only direct @mentions get through.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
<label class="group relative grid cursor-pointer grid-cols-[auto_1fr_auto] items-center gap-3.5 rounded-2xl border-[1.5px] border-trb03-line bg-white p-4 transition duration-250 hover:border-trb03-acc/35 has-checked:border-trb03-acc has-checked:bg-trb03-acc/5 has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-trb03-acc motion-reduce:transition-none">
<input type="radio" name="trb-03" value="muted" class="sr-only">
<span aria-hidden="true" class="grid size-[42px] place-items-center rounded-[11px] bg-trb03-tile text-trb03-icon transition duration-250 *:size-[22px] group-has-checked:bg-trb03-acc/14 group-has-checked:text-trb03-acc motion-safe:group-has-checked:*:scale-110 *:transition-transform"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5a3 3 0 0 1 6 0c3 1 3 4 3 6 0 2 1 4 1 4H5s1-2 1-4c0-2 0-5 3-6Z" opacity=".35"/><path d="m4 4 16 16"/></svg></span>
<span class="grid gap-0.5"><span class="text-[15px] font-bold">Muted</span><span class="text-[13px] leading-[1.45] text-trb03-mut">Nothing until you turn notifications back on.</span></span>
<span aria-hidden="true" class="grid size-[22px] place-items-center rounded-full border-2 border-trb03-ring transition after:size-[9px] after:scale-0 after:rounded-full after:bg-white after:transition-transform after:duration-300 after:ease-trb03-spring after:content-[''] group-has-checked:border-trb03-acc group-has-checked:bg-trb03-acc group-has-checked:after:scale-100"></span>
</label>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb03-bg: #f5f5f9;
--color-trb03-bg: oklch(0.97 0.006 285);
--color-trb03-ink: #1d2233;
--color-trb03-ink: oklch(0.24 0.03 275);
--color-trb03-mut: #5f6579;
--color-trb03-mut: oklch(0.5 0.025 275);
--color-trb03-line: #e5e7f0;
--color-trb03-line: oklch(0.93 0.01 275);
--color-trb03-ring: #c7cddd;
--color-trb03-ring: oklch(0.85 0.02 270);
--color-trb03-tile: #eef0f6;
--color-trb03-tile: oklch(0.955 0.008 275);
--color-trb03-icon: #737b96;
--color-trb03-icon: oklch(0.58 0.035 275);
--color-trb03-acc: #7447e0;
--color-trb03-acc: oklch(0.55 0.21 290);
--ease-trb03-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--font-trb03-sans: 'Plus Jakarta Sans', 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-03 {
width: 100%;
min-height: 100svh;
}
```How this works
A three-column label. Each row is <label class="group grid grid-cols-[auto_1fr_auto] …">; because the description sits inside the label it enlarges the hit area and is read with the option.
Child selectors without a stylesheet. The original's .tile svg{width:22px} became *:size-[22px] on the tile — v4's child variant targets direct children, which is exactly one SVG here.
Make it yours
- Re-theme with
--color-trb03-acc; wash, tile and dot derive from it via opacity modifiers. - Denser rows:
p-3 gap-3and asize-9tile. - Wider settings panes: raise
max-w-[29rem].
Gotchas — read before shipping
- Don't put links or buttons inside the label — nested interactive content inside a label is invalid and splits the click.
- For long descriptions, move the text out and point
aria-describedbyat it so the accessible name stays short. - Never add
tabindexto the labels; the radio already takes focus.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
:has() drives the row tint; without it the options select normally.