Focus
Every interactive element must have a visible focus indicator when navigated by keyboard. Diwa implements WCAG 2.4.7 (AA) and WCAG 2.4.11 (AA 2.2) out of the box via a global :focus-visible rule and the --diwa-border-focus focus token set.
Example
| Token | Value | Role |
|---|---|---|
| --diwa-state-focus | #10a37f | Focus ring colour — brand accent |
| --diwa-border-focus | var(--diwa-state-focus) | Alias for use in border contexts |
| --diwa-focus-ring-width | 1px | Focus outline width token (default) |
| --diwa-focus-ring-offset | 1px | Focus outline offset token (default) |
Focus is only visible when navigating by keyboard (Tab / Shift+Tab):
Usage
The :focus-visible state allows users to navigate all interactive elements via keyboard. The focus ring is only visible when using keyboard navigation — not on mouse click or touch.
Do
- ✓Use :focus-visible so focus rings are hidden for pointer users and visible for keyboard users.
- ✓Use outline (not box-shadow) — it renders correctly in Windows High Contrast Mode and is never clipped by overflow: hidden.
- ✓Maintain tokenized focus width and sufficient colour contrast (>= 3:1 against adjacent bg).
- ✓Apply :host(:focus-visible) inside Shadow DOM components with the inherited token.
- ✓Use getFocusStyle() for consistent focus styles in JSS or styled-components.
Don't
- ✕Don't use outline: none without :focus-visible — this removes focus for all users including keyboard.
- ✕Don't rely solely on a colour change to communicate focus; always include the outline.
- ✕Don't use box-shadow for focus rings — it is clipped by overflow: hidden and invisible in forced-colors mode.
- ✕Don't suppress the global focus rule in component stylesheets; override only when necessary.
✓2.4.7 (AA)Focus VisibleAll interactive elements have a visible focus indicator.
✓2.4.11 (AA)Focus AppearanceTokenized outline with --diwa-border-focus provides a clearly visible indicator.
✓1.4.11 (AA)Non-text ContrastFocus ring colour vs adjacent background meets 3:1.
✓2.1.1 (A)Keyboard:focus-visible ensures all keyboard navigation paths are visible.
Styles
The global rule is applied automatically by Diwa. For custom components or Shadow DOM elements, use the CSS tokens directly or the JS utility:
/* Global rule (applied by Diwa automatically) */
:focus-visible {
outline: var(--diwa-focus-ring-width) solid var(--diwa-border-focus);
outline-offset: var(--diwa-focus-ring-offset);
}
/* Shadow DOM — inside a Stencil component */
:host(:focus-visible) {
outline: var(--diwa-focus-ring-width) solid var(--diwa-border-focus);
outline-offset: var(--diwa-focus-ring-offset);
}
/* JS — JSS / styled-components / inline styles */
import { getFocusStyle } from '@diwacopilot/components/styles';
// Default: { outline: 'var(--diwa-focus-ring-width) solid var(--diwa-border-focus)', outlineOffset: 'var(--diwa-focus-ring-offset)' }
const style = getFocusStyle();
// Custom offset:
const customStyle = getFocusStyle('4px');@use '@diwacopilot/components/styles' as *;
/* Focus ring is applied globally by Diwa */
/* Override inside a custom Shadow DOM component */
:host(:focus-visible) {
outline: var(--diwa-focus-ring-width) solid var(--diwa-border-focus);
outline-offset: var(--diwa-focus-ring-offset);
}