Pure TailwindMIT licensed

Tailwind Avatar Radio Picker (Profile Selector)

The "who's watching?" screen as a proper form control: profile photos where the active one scales up, gains a bright ring and full colour while the rest rest slightly dimmed. The double ring is ring-3 ring-trb21-acc ring-offset-4 ring-offset-trb21-bg; the sprung lift is motion-safe:. An "Add profile" tile shows how to mix an action into the row without joining the radio group.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="trb-21 w-full min-h-svh grid place-items-center bg-trb21-bg px-6 py-14 font-trb21-sans text-trb21-ink" aria-label="User profile avatar selector demo">
  <fieldset>
    <legend class="text-center text-[30px] font-extrabold tracking-[-0.025em]">Who's watching?</legend>
    <div class="mt-9 flex flex-wrap justify-center gap-8">
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="amara" class="sr-only" checked><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=300&q=80&auto=format&fit=crop" alt="Amara" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Amara</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="jonas" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=300&q=80&auto=format&fit=crop" alt="Jonas" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Jonas</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="sofia" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=300&q=80&auto=format&fit=crop" alt="Sofia" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Sofia</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="mateo" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=300&q=80&auto=format&fit=crop" alt="Mateo" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Mateo</span></label>
      <div class="grid justify-items-center gap-3"><button type="button" class="grid size-[92px] cursor-pointer place-items-center rounded-full border-2 border-dashed border-white/25 text-trb21-mut transition-colors hover:border-white/60 hover:text-white focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white" aria-label="Add profile"><svg class="size-7" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button><span aria-hidden="true" class="text-[15px] font-semibold text-trb21-mut">Add</span></div>
    </div>
  </fieldset>
</section>
@import "tailwindcss";

@theme {
  --color-trb21-bg: #101014;
  --color-trb21-bg: oklch(0.18 0.006 285);
  --color-trb21-ink: #f2f2f5;
  --color-trb21-ink: oklch(0.96 0.004 285);
  --color-trb21-mut: #9c9ca8;
  --color-trb21-mut: oklch(0.7 0.012 285);
  --color-trb21-acc: #f36f5c;
  --color-trb21-acc: oklch(0.7 0.16 30);
  --shadow-trb21-lift: 0 16px 34px -12px oklch(0 0 0 / 0.8);
  --ease-trb21-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --font-trb21-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-21 {
  width: 100%;
  min-height: 100svh;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-trb21-bg: #101014;
  --color-trb21-bg: oklch(0.18 0.006 285);
  --color-trb21-ink: #f2f2f5;
  --color-trb21-ink: oklch(0.96 0.004 285);
  --color-trb21-mut: #9c9ca8;
  --color-trb21-mut: oklch(0.7 0.012 285);
  --color-trb21-acc: #f36f5c;
  --color-trb21-acc: oklch(0.7 0.16 30);
  --shadow-trb21-lift: 0 16px 34px -12px oklch(0 0 0 / 0.8);
  --ease-trb21-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --font-trb21-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-21 {
  width: 100%;
  min-height: 100svh;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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.

  • The double box-shadow ring became ring-3 + ring-offset-4; the depth shadow composes alongside it as shadow-trb21-lift.
  • filter:saturate(.7) brightness(.8) became two filter utilities, each undone independently on hover and check.
  • UI upgrade from the CSS original: the "+ Add" tile the original only described is now built — a real <button> outside the radio group, with its own focus ring.
  • UI upgrade: names rose from #84848f (5.2:1) to 7.3:1, and focus rings clear the accent ring with a 10px offset instead of colliding with it.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 Avatar Radio Picker (Profile Selector)
Source: https://codefronts.com/components/tailwind-css-radio-buttons/tailwind-avatar-radio-picker-profile-selector/

The "who's watching?" screen as a proper form control: profile photos where the active one scales up, gains a bright ring and full colour while the rest rest slightly dimmed. The double ring is ring-3 ring-trb21-acc ring-offset-4 ring-offset-trb21-bg; the sprung lift is motion-safe:. An "Add profile" tile shows how to mix an action into the row without joining the radio group.
## HTML
```html
<section class="trb-21 w-full min-h-svh grid place-items-center bg-trb21-bg px-6 py-14 font-trb21-sans text-trb21-ink" aria-label="User profile avatar selector demo">
  <fieldset>
    <legend class="text-center text-[30px] font-extrabold tracking-[-0.025em]">Who's watching?</legend>
    <div class="mt-9 flex flex-wrap justify-center gap-8">
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="amara" class="sr-only" checked><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=300&q=80&auto=format&fit=crop" alt="Amara" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Amara</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="jonas" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=300&q=80&auto=format&fit=crop" alt="Jonas" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Jonas</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="sofia" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=300&q=80&auto=format&fit=crop" alt="Sofia" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Sofia</span></label>
      <label class="group relative grid cursor-pointer justify-items-center gap-3"><input type="radio" name="trb-21" value="mateo" class="sr-only"><img class="size-[92px] rounded-full object-cover brightness-80 saturate-70 ring-trb21-acc ring-offset-trb21-bg transition-[scale,filter,box-shadow] duration-350 ease-trb21-spring group-hover:brightness-100 group-hover:saturate-100 group-has-checked:shadow-trb21-lift group-has-checked:ring-3 group-has-checked:ring-offset-4 group-has-checked:brightness-100 group-has-checked:saturate-100 group-has-focus-visible:outline-3 group-has-focus-visible:outline-offset-[10px] group-has-focus-visible:outline-white motion-safe:group-has-checked:scale-112" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=300&q=80&auto=format&fit=crop" alt="Mateo" loading="lazy"><span class="text-[15px] font-semibold text-trb21-mut transition-colors group-has-checked:text-white">Mateo</span></label>
      <div class="grid justify-items-center gap-3"><button type="button" class="grid size-[92px] cursor-pointer place-items-center rounded-full border-2 border-dashed border-white/25 text-trb21-mut transition-colors hover:border-white/60 hover:text-white focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-white" aria-label="Add profile"><svg class="size-7" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button><span aria-hidden="true" class="text-[15px] font-semibold text-trb21-mut">Add</span></div>
    </div>
  </fieldset>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-trb21-bg: #101014;
  --color-trb21-bg: oklch(0.18 0.006 285);
  --color-trb21-ink: #f2f2f5;
  --color-trb21-ink: oklch(0.96 0.004 285);
  --color-trb21-mut: #9c9ca8;
  --color-trb21-mut: oklch(0.7 0.012 285);
  --color-trb21-acc: #f36f5c;
  --color-trb21-acc: oklch(0.7 0.16 30);
  --shadow-trb21-lift: 0 16px 34px -12px oklch(0 0 0 / 0.8);
  --ease-trb21-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --font-trb21-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-21 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

Rings, not shadows. The original built a gap ring and an accent ring from two box-shadow spreads; ring-offset-4 paints the gap in the stage colour and ring-3 the accent, still composable with shadow-* for depth.

Faces are content. Every photo's alt is the person's name, and the visible caption matches, so the option announces as "Amara, radio button, checked". The Add tile is a real button — arrow keys never land on it.

Make it yours

  • Accent: --color-trb21-acc; keep ring-offset-trb21-bg matched to the stage.
  • Calmer: drop the scale-112 lift.
  • Size: size-[92px] on each image and the Add tile.

Gotchas — read before shipping

  • ring-offset-* is a solid colour — change the stage colour and the offset must follow.
  • Filter the image, not the label, or captions dim too.
  • Keep scale deltas ≤1.15 to avoid mid-animation blur on low-DPI screens.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Photos hotlink from Unsplash's free CDN. :has() drives state; degrades to plain radios.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…