Component States
Date: 2026-08-16
The set of appearances a component can take, most of which get designed after launch when someone finds one missing. The list is knowable up front, and writing it down is the cheapest quality improvement available to a design system.
Component states are the distinct visual and behavioural conditions a component can be in — interaction states it enters from user input, and content states it enters from data.
Two different lists, both routinely incomplete.
Interaction states
default resting
hover pointer over it
focus keyboard focus — MUST be visible
focus-visible focus from keyboard, not mouse
active being pressed
disabled present, not operable
loading operating, awaiting a result
selected chosen, in a set
focus is the one that gets removed — usually by a global outline: none early in a project, and usually never replaced. It’s the only state with a hard accessibility requirement, because without it a keyboard user cannot tell where they are — Keyboard Navigation, WCAG.
:focus-visible is the right selector. It shows the indicator for keyboard focus and suppresses it for mouse clicks, which is what people were trying to achieve when they removed focus styling altogether.
Content states
The list that gets forgotten entirely, because the design was made with realistic data:
empty no data yet
loading fetching
partial some data, more coming
error the request failed
too much 200 items in a 5-item list
too long a 90-character product name
too short a one-character name
missing no image, no description
Every one of these ships to production whether or not it was designed. Undesigned, they appear as a blank rectangle, a stretched layout, or a broken image icon.
DESIGNED FOR WHAT SHIPS
"Blue Cotton Shirt" "Professional
Anti-Ageing Serum
With Hyaluronic
Acid 50ml Twin Pack"
↑ breaks the card
Disabled deserves its own argument
Disabled controls are worse than they look:
- Low contrast by convention, so they often fail contrast requirements
- Not focusable, so keyboard and screen-reader users may not know they exist
- No explanation. A disabled button doesn’t say why, and the user can’t ask it
Prefer an enabled control that explains the problem to a disabled one that doesn’t. A submit button that stays active and reveals validation messages on click is more usable than one that greys out silently — Inclusive Design.
Combinations, which is where it gets real
States aren’t independent, and the matrix is bigger than the list:
default hover focus disabled
primary ✓ ✓ ✓ ✓
secondary ✓ ✓ ✓ ✓
danger ✓ ✓ ✓ ✓
loading ✓ – ✓ –
Multiply by theme and by variant and a button has dozens of appearances. You do not design each one — you define how each state modifies the base, and the combinations follow. That’s what makes states a token problem rather than a design problem.
Making the list stick
- Put every state in the component’s documentation page, rendered, not described — Component Documentation
- Test with hostile content. The longest realistic string, the missing image, the empty array. A component that only works with tidy data is untested
- Loading needs a shape. A spinner that replaces content causes layout shift; a skeleton matching the eventual dimensions doesn’t — Cumulative Layout Shift
- Error states need a way out. “Something went wrong” with no retry is a dead end
The honest position
The set is always incomplete. Something will present a state nobody anticipated — a currency with four decimal places, a right-to-left language, a name in a script your font lacks.
The goal isn’t completeness; it’s that the known list is covered, so the surprises are genuinely surprising rather than the ones everyone could have predicted.