Form pseudo-classes expose control state, so you can style validation and toggles without JavaScript. Each weighs 0-1-0.
| Selector | Matches |
|---|---|
| :checked / :indeterminate / :default | Ticked / mixed / initially selected |
| :enabled / :disabled | Usable / unusable controls |
| :required / :optional | With / without required |
| :valid / :invalid | Passing / failing validation now |
| :user-valid / :user-invalid | Same, after user interaction |
| :in-range / :read-only | Within min-max / not editable |
| :placeholder-shown / :autofill | Showing placeholder / browser-filled |
:invalid flags an empty required field as soon as the page loads. :user-invalid, Baseline widely available since May 2026, waits until the user edits the field or tries to submit, so prefer it for error styling. :autofill is not yet Baseline.
<!doctype html>
<style>
body { font: 15px system-ui; } input { padding: 4px; border: 2px solid #90a4ae; }
input:invalid { border-color: #c62828; } input:valid:read-write { border-color: #2e7d32; }
input:placeholder-shown { background: #fffde7; } input:disabled { opacity: 0.5; }
:checked + span { font-weight: 700; color: #1565c0; }
</style>
<p><input type="email" required placeholder="Empty: invalid">
<input type="email" value="ann@example.com"></p>
<label><input type="checkbox" checked> <span>Subscribed</span></label> <input value="Off" disabled>