Form State Variants

checked, disabled, required, invalid

Form State Variants

Style inputs based on their validation and interaction state declaratively, with no JavaScript needed.

4 min read Level 2/5 #tailwind#variants#forms
What you'll learn
  • Use checked, disabled, required, and read-only
  • Use invalid, valid, and the friendlier user-invalid
  • Build accessible field states with sensible base styling

The browser already tracks whether a field is checked, disabled, required, or valid. Form variants let you style those states without wiring up change handlers.

Interaction States

disabled:, checked:, and read-only: are the everyday ones:

<input type="checkbox"
       class="accent-blue-600 checked:ring-2 checked:ring-blue-500">

<button class="rounded bg-blue-600 px-4 py-2 text-white
               disabled:cursor-not-allowed disabled:opacity-50"
        disabled>
  Save
</button>

disabled:opacity-50 and disabled:cursor-not-allowed communicate an inert control clearly, and they update automatically when the disabled attribute changes.

Required and Validation

Mark required fields and react to validity. required: on the label can append an asterisk:

<label class="required:after:content-['*'] required:after:text-red-500">
  Email
</label>
<input type="email" required
       class="border invalid:border-red-500 valid:border-green-500">

Plain invalid: styles a field red the instant it loads while still empty, which feels hostile. Prefer user-invalid:, which only applies after the user has interacted with and left the field:

<input type="email" required
       class="border user-invalid:border-red-500
              user-invalid:ring-1 user-invalid:ring-red-500">

A Sensible Baseline

Native form controls render inconsistently across browsers. The official forms plugin gives them a clean, restyle-friendly base so your variants have something predictable to build on:

@import "tailwindcss";
@plugin "@tailwindcss/forms";

With that in place, checked:, disabled:, and user-invalid: produce consistent, accessible field states across the board.

Transitions →