19 / 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 Image Radio Button Selector (Arch Mask)
A destination picker where photography is the control: each option is a photo in a tall arch. Unselected images rest desaturated; picking one blooms it to full colour, relaxes the arch into a soft rounded card and slides the caption up. The mask morph is just rounded-[85px_85px_18px_18px] → group-has-checked:rounded-[26px] — border-radius interpolates natively.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="trb-19 w-full min-h-svh grid place-items-center bg-trb19-bg px-6 py-14 font-trb19-sans text-trb19-ink" aria-label="Image clip mask option selector demo">
<fieldset>
<legend class="text-center font-trb19-serif text-[40px] leading-none italic">Where to next?</legend>
<div class="mt-9 flex flex-wrap justify-center gap-6">
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="alps" class="sr-only" checked>
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=480&q=80&auto=format&fit=crop" alt="Snow-capped alpine peaks at dawn" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Alps</span><span class="text-xs text-white/85">7 nights · from $1,240</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="coast" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=480&q=80&auto=format&fit=crop" alt="Turquoise water on a white sand beach" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Coast</span><span class="text-xs text-white/85">5 nights · from $980</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="forest" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=480&q=80&auto=format&fit=crop" alt="Sunlight through a tall green forest" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Forest</span><span class="text-xs text-white/85">4 nights · from $760</span></span>
</span>
</label>
</div>
</fieldset>
</section><section class="trb-19 w-full min-h-svh grid place-items-center bg-trb19-bg px-6 py-14 font-trb19-sans text-trb19-ink" aria-label="Image clip mask option selector demo">
<fieldset>
<legend class="text-center font-trb19-serif text-[40px] leading-none italic">Where to next?</legend>
<div class="mt-9 flex flex-wrap justify-center gap-6">
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="alps" class="sr-only" checked>
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=480&q=80&auto=format&fit=crop" alt="Snow-capped alpine peaks at dawn" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Alps</span><span class="text-xs text-white/85">7 nights · from $1,240</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="coast" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=480&q=80&auto=format&fit=crop" alt="Turquoise water on a white sand beach" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Coast</span><span class="text-xs text-white/85">5 nights · from $980</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="forest" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=480&q=80&auto=format&fit=crop" alt="Sunlight through a tall green forest" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Forest</span><span class="text-xs text-white/85">4 nights · from $760</span></span>
</span>
</label>
</div>
</fieldset>
</section>@import "tailwindcss";
@theme {
--color-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
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-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
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=Instrument+Serif:ital@0;1&family=Geist:wght@400..700&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=Instrument+Serif:ital@0;1&family=Geist:wght@400..700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
filter:grayscale(1) brightness(.65)became two composable filter utilities; hover and checked each undo just the pieces they need.- The caption's hand-written
linear-gradient(180deg,…)became v4'sbg-linear-to-b from-transparent to-black/75. - UI upgrade from the CSS original: captions carry trip length and a from-price in two lines, and the legend moved to Instrument Serif italic for a travel-magazine voice.
- UI upgrade: the lift is
motion-safe:, and the arch radius (88px) now matches half the card width exactly so the top is a true semicircle.
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 Image Radio Button Selector (Arch Mask)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-image-radio-button-selector-arch-mask/
A destination picker where photography is the control: each option is a photo in a tall arch. Unselected images rest desaturated; picking one blooms it to full colour, relaxes the arch into a soft rounded card and slides the caption up. The mask morph is just rounded-[85px_85px_18px_18px] → group-has-checked:rounded-[26px] — border-radius interpolates natively.
## HTML
```html
<section class="trb-19 w-full min-h-svh grid place-items-center bg-trb19-bg px-6 py-14 font-trb19-sans text-trb19-ink" aria-label="Image clip mask option selector demo">
<fieldset>
<legend class="text-center font-trb19-serif text-[40px] leading-none italic">Where to next?</legend>
<div class="mt-9 flex flex-wrap justify-center gap-6">
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="alps" class="sr-only" checked>
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=480&q=80&auto=format&fit=crop" alt="Snow-capped alpine peaks at dawn" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Alps</span><span class="text-xs text-white/85">7 nights · from $1,240</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="coast" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=480&q=80&auto=format&fit=crop" alt="Turquoise water on a white sand beach" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Coast</span><span class="text-xs text-white/85">5 nights · from $980</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="forest" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=480&q=80&auto=format&fit=crop" alt="Sunlight through a tall green forest" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Forest</span><span class="text-xs text-white/85">4 nights · from $760</span></span>
</span>
</label>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
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 Image Radio Button Selector (Arch Mask)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-image-radio-button-selector-arch-mask/
A destination picker where photography is the control: each option is a photo in a tall arch. Unselected images rest desaturated; picking one blooms it to full colour, relaxes the arch into a soft rounded card and slides the caption up. The mask morph is just rounded-[85px_85px_18px_18px] → group-has-checked:rounded-[26px] — border-radius interpolates natively.
## HTML
```html
<section class="trb-19 w-full min-h-svh grid place-items-center bg-trb19-bg px-6 py-14 font-trb19-sans text-trb19-ink" aria-label="Image clip mask option selector demo">
<fieldset>
<legend class="text-center font-trb19-serif text-[40px] leading-none italic">Where to next?</legend>
<div class="mt-9 flex flex-wrap justify-center gap-6">
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="alps" class="sr-only" checked>
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=480&q=80&auto=format&fit=crop" alt="Snow-capped alpine peaks at dawn" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Alps</span><span class="text-xs text-white/85">7 nights · from $1,240</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="coast" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=480&q=80&auto=format&fit=crop" alt="Turquoise water on a white sand beach" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Coast</span><span class="text-xs text-white/85">5 nights · from $980</span></span>
</span>
</label>
<label class="group relative cursor-pointer">
<input type="radio" name="trb-19" value="forest" class="sr-only">
<span class="relative block h-[16.5rem] w-44 overflow-hidden rounded-[88px_88px_18px_18px] transition-[border-radius,translate,box-shadow] duration-500 ease-trb19-relax group-has-checked:rounded-[26px] group-has-checked:shadow-trb19-lift group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-4 group-has-focus-visible:outline-trb19-ink motion-safe:group-has-checked:-translate-y-1.5">
<img class="size-full scale-108 object-cover brightness-65 grayscale transition-[filter,scale] duration-550 ease-trb19-relax group-hover:brightness-85 group-hover:grayscale-40 group-has-checked:scale-100 group-has-checked:brightness-100 group-has-checked:grayscale-0 motion-reduce:transition-[filter]" src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=480&q=80&auto=format&fit=crop" alt="Sunlight through a tall green forest" loading="lazy">
<span class="absolute inset-x-0 bottom-0 grid translate-y-2.5 bg-linear-to-b from-transparent to-black/75 px-3.5 pt-8 pb-3 text-center opacity-80 transition-[translate,opacity] duration-400 group-has-checked:translate-y-0 group-has-checked:opacity-100"><span class="font-trb19-serif text-[22px] leading-tight text-white">The Forest</span><span class="text-xs text-white/85">4 nights · from $760</span></span>
</span>
</label>
</div>
</fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-trb19-bg: #141210;
--color-trb19-bg: oklch(0.19 0.006 70);
--color-trb19-ink: #f4efe8;
--color-trb19-ink: oklch(0.955 0.01 75);
--shadow-trb19-lift: 0 26px 50px -22px oklch(0 0 0 / 0.85);
--ease-trb19-relax: cubic-bezier(0.22, 1, 0.36, 1);
--font-trb19-serif: 'Instrument Serif', Georgia, serif;
--font-trb19-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-19 {
width: 100%;
min-height: 100svh;
}
```How this works
No clip-path needed. An arch is a border-radius, and border-radius interpolates, so the mask morph is a plain transition on rounded-* values. v4's filter utilities (grayscale, brightness-65) compose into one filter, and their -0/-100 counterparts undo each piece.
Images are the labels. Every photo keeps real alt text inside the label, and the visible caption repeats the destination, so the option announces properly.
Make it yours
- Swap the Unsplash URLs; any portrait crop works.
- Checked shape: try
group-has-checked:rounded-fullfor a pill. - Bloom speed:
duration-550on the image.
Gotchas — read before shipping
- Selectable images need real alt text — an empty alt makes the option unusable by screen readers.
- Filter the
<img>, not the label, or the caption grays out too. - Use
loading="lazy"; three photos above the fold cost LCP.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Border-radius morphing and filters are universal; :has() drives the bloom.