Interaction States
Why interaction states matter
Every interactive element must communicate its current state to the user — visually and programmatically. DIR Web Modernization standards and WCAG 2.2 SC 1.4.11 (Non-text Contrast) require a ≥ 3:1 contrast ratio between an element's default state and its interactive states.
State definitions
| State | Trigger | Visual treatment |
|---|---|---|
| Default | No interaction | Base component appearance |
| Hover | Pointer over element | Slight background shift or underline; cursor changes to pointer |
| Focus | Keyboard tab or programmatic focus | 2px solid outline using --txds-focus-color with --txds-focus-offset gap |
| Active / Pressed | Mouse-down or Enter key | Darkened background, slight scale down (transform: scale(0.98)) |
| Disabled | disabled or aria-disabled="true" |
Reduced opacity (0.4), cursor: not-allowed, no pointer events |
| Selected / Checked | Radio, checkbox, toggle in "on" state | Primary-color fill or border |
| Error / Invalid | Failed validation | Red border, associated error message via aria-describedby |
Component state matrix
The table below shows which states each component supports. "—" means the state is not applicable.
| Component | Hover | Focus | Active | Disabled | Selected | Error |
|---|---|---|---|---|---|---|
| Button | ✓ | ✓ | ✓ | ✓ | — | — |
| Text input | ✓ | ✓ | — | ✓ | — | ✓ |
| Checkbox | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Radio | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Select | ✓ | ✓ | — | ✓ | — | ✓ |
| Link | ✓ | ✓ | ✓ | — | — | — |
| Accordion | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Card | ✓ | ✓ | — | — | — | — |
| Chip | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Pagination | ✓ | ✓ | ✓ | ✓ | ✓ | — |
| Modal close | ✓ | ✓ | ✓ | — | — | — |
| Tab | ✓ | ✓ | ✓ | ✓ | ✓ | — |
Token-driven states
All interactive states use shared design tokens so they stay consistent across components:
:root {
/* Focus ring */
--txds-focus-color: #2563eb; /* Blue-600 */
--txds-focus-width: 2px;
--txds-focus-offset: 2px;
/* Disabled */
--txds-disabled-opacity: 0.4;
}
/* Global focus ring applied to all focusable elements */
*:focus-visible {
outline: var(--txds-focus-width) solid var(--txds-focus-color);
outline-offset: var(--txds-focus-offset);
}
Implementation checklist
- [ ] Every
<button type="button">,<a>, and<input>has visible hover, focus, and active states. - [ ] Focus indicators meet 3:1 contrast against adjacent colors (WCAG 2.2 SC 1.4.11).
- [ ] Disabled elements use
opacity+pointer-events: noneandaria-disabled="true". - [ ] No state relies solely on color change — pair with border, underline, or icon change.
- [ ] Interactive state contrast verified with browser DevTools forced-state tool.
- [ ] Keyboard-only testing confirms no "focus trap" except inside open modals.
Testing states in the browser
Use Chrome DevTools to force element states:
- Right-click the element → Inspect.
- In the Styles panel, click :hov to toggle
:hover,:focus,:active, etc. - Verify contrast with the built-in Contrast ratio picker in the color swatch.