Skip to main content

Component States & Accessible Interactions

intermediate14 min readLesson 82 of 143

Hover/focus/disabled contract, focus-visible, stacking contexts, accessible modal disclosure, and ARIA only where needed.

A component is its states. Engineering them โ€” visibly and accessibly โ€” is the difference between decoration and UI.

The state contract

Every interactive element owes: hover (affordance), focus (keyboard parity with hover), active (feedback), disabled (clearly non-interactive, still perceivable), aria-disabled when it must stay focusable. Never remove focus outlines without a :focus-visible replacement:

button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

:focus-visible shows rings for keyboard users and skips them for pointer users โ€” the best of both.

Stacking contexts

z-index only ranks within its stacking context โ€” created by position + z-index, opacity < 1, transform, filter, and more. A modal inside a transform: translateY(0) card cannot rise above a sibling with z-index 10 in another context. Debug with DevTools' 3D/layers view; fix by keeping overlays in flat subtrees (often portaled to body).

Disclosure semantics

For toggled UI, wire semantics with states โ€” no heavy ARIA libraries needed:

<button aria-expanded="false" aria-controls="menu">Menu</button>
<div id="menu" hidden>โ€ฆ</div>

Flip aria-expanded and hidden together from one source of truth. Reach for role only when the platform has no native element (dialogs get native <dialog>); native first, ARIA second.

Now practice

Component Systems โ€” PracticeState machines for real widgets: a disclosure, a sortable-table reducer, and a focus-trap plan as pure logic.3 challenges ยท ยท ~20 min