Skip to content

Tailwind Checkbox and Radio Styling

Checkboxes and radio buttons have historically been difficult to style consistently across browsers. This lesson shows how the Tailwind forms plugin makes them stylable with ordinary color and sizing utilities.

What Makes Checkboxes and Radios Different?

Unlike text inputs, checkboxes and radio buttons render a native browser-drawn control (a checkmark or dot inside a box or circle) that's historically been very difficult to restyle with plain CSS. The @tailwindcss/forms plugin solves this by applying a consistent baseline that responds to standard utility classes.

With the plugin installed, text-{color} on a checkbox or radio actually controls the checked-state fill color, a non-obvious but consistent mapping worth remembering.

<label class="flex items-center gap-2">
  <input type="checkbox" class="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500" />
  <span class="text-sm text-slate-700">Subscribe to updates</span>
</label>

text-blue-600 sets the checked-state fill color, while h-4 w-4 controls the checkbox's physical size.

Checkbox and Radio Class Patterns

class="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"  /* checkbox */
class="h-4 w-4 border-slate-300 text-blue-600 focus:ring-blue-500"          /* radio */
  • h-4 w-4 (or similar) sets the physical control size.
  • rounded gives checkboxes soft corners; radios are circular by default via the forms plugin.
  • text-{color} sets the checked-state fill color, not the surrounding text color.
  • focus:ring-{color} adds an accessible focus ring, matching the pattern from the Ring lesson.

Checkbox and Radio Cheatsheet

Common styling patterns for checkboxes, radios, and their labels.

Element Example Classes Purpose
Checkbox h-4 w-4 rounded text-blue-600 Standard square checkbox with rounded corners
Radio h-4 w-4 text-blue-600 Circular radio button (default shape via forms plugin)
Checked color text-blue-600 Controls the checked-state fill color
Focus ring focus:ring-2 focus:ring-blue-500 Accessible focus indicator
Label wrapper flex items-center gap-2 Aligns the control with its label text
Disabled disabled:opacity-50 Muted appearance when disabled
Group spacing space-y-2 Consistent spacing between multiple options
Larger touch target h-5 w-5 Easier to tap accurately on mobile

Building a Checkbox Group

Grouping related checkboxes with consistent spacing and a shared <fieldset>/<legend> improves both visual clarity and accessibility for screen reader users navigating the group.

<fieldset class="space-y-2">
  <legend class="text-sm font-medium text-slate-700">Notification preferences</legend>
  <label class="flex items-center gap-2">
    <input type="checkbox" class="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500" />
    <span class="text-sm text-slate-700">Email notifications</span>
  </label>
  <label class="flex items-center gap-2">
    <input type="checkbox" class="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500" />
    <span class="text-sm text-slate-700">SMS notifications</span>
  </label>
</fieldset>

Building a Radio Group

Radio buttons in the same group must share the same name attribute so the browser treats them as mutually exclusive options, a common bug source when copy-pasting radio markup.

<fieldset class="space-y-2">
  <legend class="text-sm font-medium text-slate-700">Choose a plan</legend>
  <label class="flex items-center gap-2">
    <input type="radio" name="plan" class="h-4 w-4 border-slate-300 text-blue-600 focus:ring-blue-500" />
    <span class="text-sm text-slate-700">Monthly</span>
  </label>
  <label class="flex items-center gap-2">
    <input type="radio" name="plan" class="h-4 w-4 border-slate-300 text-blue-600 focus:ring-blue-500" />
    <span class="text-sm text-slate-700">Annual</span>
  </label>
</fieldset>

Building a Toggle Switch From a Checkbox

A common UI pattern hides the native checkbox visually (with sr-only or peer) and uses a styled sibling element to represent a toggle switch, while keeping the underlying checkbox fully functional and accessible.

<label class="relative inline-flex cursor-pointer items-center">
  <input type="checkbox" class="peer sr-only" />
  <div class="h-6 w-11 rounded-full bg-slate-300 peer-checked:bg-blue-600 transition-colors"></div>
  <div class="absolute left-1 h-4 w-4 rounded-full bg-white transition-transform peer-checked:translate-x-5"></div>
</label>

sr-only keeps the real checkbox accessible to screen readers and keyboard users while visually hiding it in favor of the styled switch.

Accessible Checkboxes and Radios

Every checkbox and radio needs a properly associated label and, for grouped options, a <fieldset> with a <legend> describing the group as a whole.

  • Wrap each checkbox/radio and its text in a <label> so clicking the text also toggles the control.
  • Group related options in a <fieldset> with a descriptive <legend>.
  • Never rely on sr-only tricks without verifying the control is still keyboard-operable and announced correctly.

Common Mistakes

  • Forgetting to install @tailwindcss/forms, then fighting inconsistent native checkbox/radio rendering across browsers.
  • Using different name attributes for radio buttons that should belong to the same mutually exclusive group.
  • Not wrapping the control and its label text together, so clicking the text doesn't toggle the input.
  • Hiding a checkbox visually for a custom toggle switch without using sr-only and peer correctly, breaking accessibility.
  • Making checkboxes too small to tap accurately on mobile devices.

Key Takeaways

  • The @tailwindcss/forms plugin makes checkboxes and radios stylable with ordinary utility classes.
  • text-{color} on a checkbox or radio controls its checked-state fill color, not text color.
  • Radio buttons in the same group must share an identical name attribute.
  • <fieldset> and <legend> improve accessibility for grouped checkbox and radio options.
  • Custom toggle switches should use sr-only and peer to stay accessible while looking custom-styled.

Pro Tip

When building a custom-styled toggle or checkbox replacement, always keep the real <input> in the DOM and accessible via sr-only, never build a purely visual fake control with a <div> and JavaScript click handler alone.