1---2name: component-states-and-interaction-fidelity3description: Use when a single interactive control needs complete hover, focus-visible, pressed, selected, read-only, and transition fidelity. Do not use for aggregate data-region lifecycle handling or API architecture; route those concerns to system-state design or component-library architecture.4---56# Component States & Interaction Fidelity — the state matrix and the moves between states78Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.910<!-- dual-compat-start -->11## Use When12- Specifying the **full interactive-state matrix** of one component — every reachable cell of13 **default · hover · focus-visible · active/pressed · disabled · loading · selected/checked ·14 error/invalid · read-only** filled with a concrete, token-backed treatment, not left to chance.15- Specifying the **transitions between states** — the move from default→hover→active, the16 focus-in/focus-out behaviour, what happens to the cursor, the duration and easing of each move,17 and the reduced-motion fallback. The *matrix* says what each state looks like; **this skill also18 pins how the component travels between them**, which is where fidelity is usually lost.19- Writing the **focus-visible specification** to the WCAG 2.2 bar: a real, ≥3:1 indicator that is20 never obscured by sticky chrome, never `outline:none` with no replacement.21- Producing a **spec-grade state table** for a component sheet, QA gate, or dev handoff — the22 artifact a developer and a tester can both check off cell by cell.23- Reviewing/auditing a component for **state gaps** (the empty cell that ships as a bug).2425## Do Not Use When26- You are deciding the component's **variant / size / API model** (intent enums, props vs slots,27 atomic level) — that is `09-design-systems-tokens-and-theming/component-library-architecture`.28 That skill owns the *axes*; this skill owns the *state cells and the transitions between them*.29- You are designing **empty / error-page / loading-content** states of a screen or flow (zero-data30 blank slates, 404/500, skeletons vs spinners, offline) — that is31 `14-conversion-and-web-page-patterns/empty-error-and-loading-states`. It owns *content-level* states; this skill32 owns the *control-level* interactive states (hover/focus/active/disabled/selected) of one33 component. (A component's `loading` and `error` cells here link out to that skill for the34 region-level treatment.)35- You are defining the **token tiers/names** the states consume — use `design-tokens-and-naming`.36- You are choosing **motion language** for the whole product (a motion system, choreography) — use37 `08-motion-and-interaction/*`; here motion is scoped to per-state transitions and pulls durations38 from motion tokens.39- You only need **field-level** validation affordances inside a form — `04…/form-ux-design`.4041## Required Inputs4243| Input | Source | Evidence |44|---|---|---|45| Component purpose, variants, and actions | Product/component owner | Existing component or approved anatomy |46| Supported inputs and accessibility targets | Platform policy and WCAG contract | Keyboard, pointer, touch, and assistive paths |47| Tokens and motion rules | Design system | Named colour, focus, duration, and easing tokens |48- The **component** and which states it can actually *reach* (a link can't be `loading`; a trigger49 Button can't be `selected`). Run `references/state-matrix-method.md` to enumerate before designing.50- The **anatomy** of the component (its named parts) so each state names *which part* changes — use51 `references/component-anatomy.md` for the Book-3 per-component anatomy/number baseline.52- A **semantic token layer** to bind every state value to (`design-tokens-and-naming`): color53 per state, border, radius, **focus-ring**, and **motion durations/easing**. No literals.54- The **WCAG 2.2 AA floor** (`doctrine/references/wcag-2.2-criteria.md`) — focus-visible, target55 size, contrast, reduced motion, name/role/value.5657## Workflow581. **Enumerate the reachable states first; mark the rest N/A explicitly.** Run59 `references/state-matrix-method.md`. Fill only the states the component can enter; mark the60 others **N/A with the correct alternative** (e.g. a Button has no `selected` → use a61 ToggleButton with `aria-pressed`). A silently dropped cell is a state gap; an explicit N/A is a62 decision. **An empty cell is a bug, not a default.**632. **Give every reachable state a token-backed treatment.** For each cell name what changes and64 to which token: bg / fg / border / shadow / cursor, on which **anatomy part**. Default, hover,65 active/pressed, disabled, loading, selected/checked, error/invalid, read-only. Bind to tokens66 (`--color-…-hover`, `--radius-control`, `--duration-fast`) — a literal hex/px in a state cell is67 a defect (`09…/component-library-architecture` §5; `doctrine/design-doctrine.md`).683. **Specify focus-visible to the WCAG 2.2 bar — non-negotiable.** A visible indicator with69 **≥3:1 contrast** against *both* the component and the page (1.4.11, 2.4.7), an offset so it70 reads on every variant, and **never obscured** by sticky headers/footers (2.4.11). Distinguish71 `:focus` from `:focus-visible` (don't show the ring on mouse-press where it's noise; do show it72 for keyboard/AT). `outline:none` without a token-driven replacement is **forbidden**. Give73 `destructive`/danger controls a distinct-but-still-≥3:1 ring. See `references/state-matrix-method.md`74 §Focus and `doctrine/references/wcag-2.2-criteria.md`.754. **Specify the transitions, not just the endpoints.** For each adjacent pair, state the76 **trigger** (pointerenter, pointerdown, blur, `aria-busy` set…), the **property animated**77 (bg/opacity/transform), the **duration + easing token** (`--duration-fast` ~120–160 ms for78 hover/press; instant for focus-ring appearance — never fade a focus ring in slowly), and the79 **reduced-motion path** (`prefers-reduced-motion` → drop transform/scale, keep an instant80 color/opacity change; the state must still be perceivable). Define focus-OUT and the81 loading→done / error→recovered return moves, not only the forward moves. Pressed `scale .98` is82 the canonical reduced-motion casualty — keep the state, drop the motion (2.3.3).835. **Hold accessibility across *every* state, not just default.** Contrast holds in hover/active84 (1.4.3); **disabled ≠ invisible** and is communicated by more than color (1.4.1); **loading**85 exposes `aria-busy` + a live status and stays focusable but not activatable; **error/invalid**86 pairs color with text + `aria-invalid` + `aria-describedby`; **selected/checked** exposes87 `aria-pressed`/`aria-checked`/`aria-selected`. Target stays **≥24×24 CSS px** in every state88 (2.5.8), aiming 44×44 touch. Name/role/value correct per state (4.1.2).896. **Emit the spec-grade table + transition table.** Produce the per-component artifact: an90 anatomy line, the **state matrix** (one row per state × the parts that change), the91 **transition table** (one row per adjacent move × trigger/property/duration/easing/reduced-92 motion), the focus-visible clause, and a per-state a11y line. This is the handoff a dev builds93 from and QA checks off. Use the worked `examples/input-state-matrix.md` as the pattern.947. **Make the one authored choice and apply it systematically.** The convergent AI default is a95 flat control with a generic blue ring, no real pressed state, and a hover that just darkens.96 Make a *specific, defensible* move — a considered pressed shift, a brand focus ring, an97 intentional hover — and route it through tokens so it reads as one skilled hand across every98 state and variant (`doctrine/design-doctrine.md` §0).99100## Decision Rules101102| Condition | Choice | Wrong-choice failure |103|---|---|---|104| Action is temporarily unavailable | Disabled only with visible reason or prerequisite | Silent disabled controls strand users |105| Action is processing | Preserve label/width, show busy state, block duplicate activation | Layout shift or repeat submission corrupts intent |106| Motion is reduced | Instant or opacity-safe transition with equivalent state cue | Motion dependence causes discomfort and hides state |107108## Capability Contract109110- Must inspect the component and exercise supported input paths; review is read-only unless remediation is requested.111- May edit in-scope component styles/tests, but must preserve semantics and may not remove focus or accessibility behaviour for visual convenience.112113## Degraded Mode114115- If component anatomy or state events are unknown, stop and request the contract.116- Without interactive rendering, return a provisional state-transition table marked untested. Recover a failed state by restoring semantic control behaviour, then retest keyboard, touch, pointer, and reduced-motion paths.117118## Quality Standards119120- Every reachable state has a truthful cue, trigger, exit, focus rule, and non-colour-only distinction.121- Release evidence records component build, inputs, transitions, accessibility checks, and failures corrected.122123## Anti-Patterns124- **State gaps.** default + hover only; focus-visible, active, disabled, loading, selected, error125 forgotten. The matrix exists so nothing is missed — an empty cell is a bug.126- **`outline:none` with no replacement** — the single most common a11y regression (2.4.7 fail).127- **Focus ring obscured** by a sticky header/footer (2.4.11 fail), or a ring under 3:1 (1.4.11).128- **Designing endpoints, not transitions** — the states look fine in static frames but the129 component snaps or jank-fades between them because no duration/easing/trigger was ever specified.130- **Ignoring reduced-motion** on hover/press/selection transitions (2.3.3).131- **State-by-color-alone** — disabled only paler, error only a red border, selected only a tint.132 Fails 1.4.1; pair with icon/shape/text/`aria-*`.133- **Hard-coded values in a state cell** — can't theme, can't go dark. Token it.134- **`:focus` styled like `:focus-visible`** so a mouse press flashes the keyboard ring (noise), or135 the reverse — suppressing the ring for keyboard users.136- **Loading that loses focus or shifts layout** — reserve label width; keep focus on the control.137138## Outputs139140| Output | Consumer | Evidence and acceptance |141|---|---|---|142| State-transition matrix | Designer and engineer | Trigger, visual, semantic, focus, cursor, duration, and exit are complete |143| Interaction-fidelity gate | QA and accessibility reviewer | Keyboard, touch, pointer, contrast, target, and reduced-motion evidence passes |144- A **per-component state matrix**: every reachable state (default/hover/focus-visible/active/145 disabled/loading/selected/error/read-only) given a token-backed treatment; non-reachable states146 marked N/A with the correct alternative.147- A **transition table**: each adjacent state move with trigger, animated property, duration +148 easing token, and the reduced-motion fallback (incl. focus-out and loading/error returns).149- A **focus-visible specification** meeting 2.4.7 / 2.4.11 / 1.4.11.150- A **per-state a11y line** (contrast, target size, name/role/value, live regions, color-not-alone).151- A handoff-ready, cell-by-cell checkable spec for dev and QA.152153## Examples154- See `examples/input-state-matrix.md` — a complete worked state-fidelity spec for one real155 component (a text Input / form field): anatomy, all nine interactive states each given a156 token-backed treatment, the **transition table** (focus-in/out, hover, error-appears, loading),157 the focus-visible clause to WCAG 2.4.7/2.4.11/1.4.11, and the per-state a11y notes. Never lorem.158159## References160- `doctrine/design-doctrine.md` — the Anti-Slop Charter; §0 "looks human-made" applied to states161 and transitions (one authored move — pressed shift, brand focus ring — applied via tokens).162- `doctrine/references/wcag-2.2-criteria.md` — the a11y floor cited throughout: focus-visible &163 contrast (2.4.7, 1.4.11), focus-not-obscured (2.4.11), target size 24px (2.5.8), reduced motion164 (2.3.3), color-not-alone (1.4.1), name/role/value (4.1.2), live regions (4.1.3).165- `references/state-matrix-method.md` — the method: enumerate reachable states, the nine-state166 set, the focus-visible spec, and the transition-specification model.167- `references/component-anatomy.md` — the Book-3 (*How to Design Better UI Components*) per-component168 anatomy + numbers (button/input/dropdown/checkbox/card heights, paddings, hit areas) so each state169 names a real part with a real measure.170- Pair: `09-design-systems-tokens-and-theming/component-library-architecture` — owns the variant/171 size/API model; **this skill owns the per-component state fidelity** that fills its state column.172- Pair: `14-conversion-and-web-page-patterns/empty-error-and-loading-states` — owns content-level empty/error/173 loading (skeletons, 404, offline); this skill owns control-level interactive states and links to174 it for the region-level loading/error treatment.175- Consumes: `design-tokens-and-naming` (state + motion tokens); aligns with `08-motion-and-interaction`176 for transition durations/easing.177<!-- dual-compat-end -->