Diwa Design System

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

TokenValueRole
--diwa-state-focus#10a37fFocus ring colour — brand accent
--diwa-border-focusvar(--diwa-state-focus)Alias for use in border contexts
--diwa-focus-ring-width1pxFocus outline width token (default)
--diwa-focus-ring-offset1pxFocus outline offset token (default)

Focus is only visible when navigating by keyboard (Tab / Shift+Tab):

Some Anchor

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);
}