Accessibility

Built-in accessibility features: reduced motion, system theme detection, high contrast, and keyboard focus indicators

Reduced Motion

// Motion tokens (config/tokens/motion) :root { --ui-duration-fast: 100ms; --ui-duration-base: 150ms; --ui-duration-slow: 250ms; } @media (prefers-reduced-motion: reduce) { :root { --ui-duration-fast: 0ms; --ui-duration-base: 0ms; --ui-duration-slow: 0ms; } } // Example: spinner @media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
<!-- All animations and transitions respect prefers-reduced-motion. Duration tokens are set to 0ms, and component animations are disabled with animation: none. -->
// Motion tokens (config/tokens/motion)
:root {
  --ui-duration-fast: 100ms;
  --ui-duration-base: 150ms;
  --ui-duration-slow: 250ms;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --ui-duration-fast: 0ms;
    --ui-duration-base: 0ms;
    --ui-duration-slow: 0ms;
  }
}
// Example: spinner
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
  }
}

System Theme Detection

Follows system preference when no data-theme is set

Forced dark regardless of system preference

// Explicit toggle (highest priority) [data-theme="dark"] { --ui-color-bg: var(--ui-color-neutral-900); ... } // Auto-detect (only when no data-theme is set) @media (prefers-color-scheme: dark) { :root:not([data-theme]) { --ui-color-bg: var(--ui-color-neutral-900); ... } }
<!-- Dark mode activates automatically when the OS is set to dark and no explicit data-theme attribute is present. Set data-theme to override. -->
<div class="ui-card">
  <p>Follows system preference when no data-theme is set</p>
</div>
<div class="ui-card" data-theme="dark">
  <p>Forced dark regardless of system preference</p>
</div>
// Explicit toggle (highest priority)
[data-theme="dark"] {
  --ui-color-bg: var(--ui-color-neutral-900);
  ...
}

// Auto-detect (only when no data-theme is set)
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --ui-color-bg: var(--ui-color-neutral-900);
    ...
  }
}

Focus Indicators

<!-- All interactive elements use :focus-visible for keyboard-only focus rings. Mouse clicks do not show focus outlines. -->
<div class="ui-row ui-row--sm">
  <button class="ui-button ui-button--focus">Button</button>
  <input class="ui-input ui-input--focus" placeholder="Input"></input>
  <select class="ui-select ui-select--focus">
    <option>Select</option>
  </select>
</div>

Contrast Preference

@media (prefers-contrast: more) { :root { --ui-color-border: var(--ui-color-neutral-400); --ui-color-border-strong: var(--ui-color-neutral-600); --ui-color-text-muted: var(--ui-color-neutral-600); --ui-color-bg-subtle: var(--ui-color-neutral-200); } } @media (prefers-contrast: less) { :root { --ui-color-border: var(--ui-color-neutral-150); --ui-color-border-strong: var(--ui-color-neutral-200); } } // Tokens affected by prefers-contrast: // --ui-color-border (default: neutral-200 -> more: neutral-400) // --ui-color-border-strong (default: neutral-300 -> more: neutral-600) // --ui-color-text-muted (default: neutral-500 -> more: neutral-600) // --ui-color-bg-subtle (default: neutral-100 -> more: neutral-200)
<!-- Adapts border visibility, muted text, and subtle backgrounds when users request more or less contrast via OS settings. -->
@media (prefers-contrast: more) {
  :root {
    --ui-color-border: var(--ui-color-neutral-400);
    --ui-color-border-strong: var(--ui-color-neutral-600);
    --ui-color-text-muted: var(--ui-color-neutral-600);
    --ui-color-bg-subtle: var(--ui-color-neutral-200);
  }
}
@media (prefers-contrast: less) {
  :root {
    --ui-color-border: var(--ui-color-neutral-150);
    --ui-color-border-strong: var(--ui-color-neutral-200);
  }
}
// Tokens affected by prefers-contrast:
// --ui-color-border (default: neutral-200 -> more: neutral-400)
// --ui-color-border-strong (default: neutral-300 -> more: neutral-600)
// --ui-color-text-muted (default: neutral-500 -> more: neutral-600)
// --ui-color-bg-subtle (default: neutral-100 -> more: neutral-200)

Forced Colors High Contrast

// Normal mode: box-shadow visible, outline transparent .input:focus-visible { outline: var(--ui-border-width-md) solid transparent; outline-offset: var(--ui-border-width-sm); box-shadow: 0 0 0 var(--ui-border-width-md) var(--ui-color-focus); } // Forced colors: browser removes box-shadow, // transparent outline becomes system-colored @media (forced-colors: active) { :root { --ui-color-focus: Highlight; } } // Components with forced-colors support: // Forms: input, select, checkbox, radio, slider // Actions: button, close-button // Navigation: tabs, breadcrumb, pagination // Overlays: modal, drawer, dialog
<!-- Windows High Contrast Mode support. Focus rings use transparent outlines that become visible in forced-colors mode, and the focus color maps to the system Highlight color. -->
// Normal mode: box-shadow visible, outline transparent
.input:focus-visible {
  outline: var(--ui-border-width-md) solid transparent;
  outline-offset: var(--ui-border-width-sm);
  box-shadow: 0 0 0 var(--ui-border-width-md)
    var(--ui-color-focus);
}

// Forced colors: browser removes box-shadow,
// transparent outline becomes system-colored
@media (forced-colors: active) {
  :root {
    --ui-color-focus: Highlight;
  }
}
// Components with forced-colors support:
// Forms: input, select, checkbox, radio, slider
// Actions: button, close-button
// Navigation: tabs, breadcrumb, pagination
// Overlays: modal, drawer, dialog