Inclusive by Design
Accessibility
WCAG 2.1 AA compliance assessed across the Nodus component library. Ten criteria mapped to implementation evidence, with gaps identified and remediation paths documented. Legibility serves everyone.
Design System Commitment
Nodus targets WCAG 2.1 Level AA conformance for all components. The three-layer token system enforces contrast ratios at the semantic level. Keyboard navigation, focus management, and reduced motion support are architectural commitments, not afterthoughts. Every component must be operable without a mouse, visible without color alone, and understandable without sound.
Keyboard Navigation
WCAG 2.1 SC 2.1.1 — All functionality is operable through a keyboard interface without requiring specific timings for individual keystrokes.
Every interactive component in the system supports keyboard operation. Buttons, Inputs, Accordion triggers, Dialog, Modal, Tooltip — all respond to Tab, Enter, Space, Escape, and Arrow keys. The CommandSearch palette (Cmd+K) provides keyboard-first global navigation.
40+ interactive components verified with keyboard-only operation. Focus is visible via outline tokens. Tab order follows DOM source order. Escape closes all overlay components.
- +Button — Enter/Space activation
- +Accordion — Arrow key navigation between triggers
- +Dialog/Modal — focus trap with Escape to close
- +CommandSearch — Cmd+K open, Arrow keys for results, Enter to navigate
- +Tooltip — focusable trigger with Escape to dismiss
- +MobileMenu — Escape to close drawer
Focus Management
WCAG 2.1 SC 2.4.7 — Any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible.
Focus indicators use the semantic token --ds-text-primary for outline color, ensuring they adapt to both light and dark themes. Dialog and Modal implement focus trapping. Focus returns to the trigger element on close.
Focus rings visible on all interactive elements via Tailwind's focus-visible utilities mapped to semantic tokens. Overlay components trap and restore focus correctly.
- +Global focus-visible outline using --ds-text-primary
- +Dialog — focus trap on open, restore on close
- +Modal — focus trap with inert background
- +Accordion — roving tabindex pattern
- +CommandSearch — auto-focus input on modal open
- —No skip-to-content link in the site layout
- —Focus order in complex patterns (AgentTimeline, TaskQueue) not formally tested
Color Contrast
WCAG 2.1 SC 1.4.3 — The visual presentation of text has a contrast ratio of at least 4.5:1 (normal text) or 3:1 (large text).
The three-layer token system (primitive > semantic > component) enforces contrast at the semantic level. --ds-text-primary on --ds-surface-ground provides 15.4:1 in light mode and 14.7:1 in dark mode. The semantic triad colors (Agency red, Temporal blue, Validation gold) are tested against both surface layers.
Primary text exceeds AA by 3x. Secondary text (--ds-text-secondary) achieves 7.1:1. Muted text (--ds-text-muted) achieves 4.6:1. Semantic colors on surface-ground all exceed 4.5:1.
- +--ds-text-primary on --ds-surface-ground: 15.4:1 (light), 14.7:1 (dark)
- +--ds-text-secondary on --ds-surface-ground: 7.1:1 (light), 6.8:1 (dark)
- +--ds-text-muted on --ds-surface-ground: 4.6:1 (light), 4.5:1 (dark)
- +--ds-color-agency on --ds-surface-ground: 5.2:1
- +--ds-color-temporal on --ds-surface-ground: 4.7:1
- +--ds-color-validation on --ds-surface-ground: 4.9:1
- —--ds-text-muted on --ds-surface-raised may dip below 4.5:1 in some dark theme combinations — monitor during theme expansion
Semantic HTML
WCAG 2.1 SC 1.3.1 — Information, structure, and relationships conveyed through presentation can be programmatically determined.
The site layout uses landmark regions (nav, main, aside, footer). Component markup uses semantic elements: button for actions, dialog for overlays, nav for navigation, ol/ul for lists. ARIA roles supplement where native semantics are insufficient.
Layout uses header/nav/main/aside/footer landmarks. Components prefer native HTML elements over ARIA where possible. 476 accessibility attributes (aria-*, role) cataloged across the component library.
- +Site layout — header, nav, main, aside landmarks
- +Dialog/Modal — native <dialog> element with aria-modal
- +Accordion — proper heading hierarchy with aria-expanded
- +Tooltip — aria-describedby linking trigger to content
- +MobileMenu — role='dialog' with aria-modal and aria-label
- +Sidebar — nav landmark with aria-label='Documentation'
Screen Reader Support
WCAG 2.1 SC 4.1.2 — For all user interface components, the name, role, and value can be programmatically determined.
Interactive components expose accessible names via aria-label or visible text content. State changes are communicated through aria-expanded, aria-selected, aria-pressed, and aria-live regions. Status updates (streaming indicators, agent states) use aria-live='polite' to announce changes without interrupting.
Core UI components (Button, Input, Accordion, Dialog) have full screen reader coverage. Pattern components (AgentCard, MCPToolCall, TaskQueue) have basic aria-label but lack detailed state announcements for complex status transitions.
- +Button — accessible name from children or aria-label
- +Input — associated label via htmlFor/id pairing
- +Accordion — aria-expanded on triggers, aria-controls linking
- +Dialog — aria-label on dialog element
- +StatusDot — aria-label announcing current status
- +StreamingDot — aria-live='polite' for state changes
- —AgentCard status transitions not announced via aria-live
- —MCPToolCall progress steps lack aria-current='step'
- —TaskQueue priority changes not communicated to screen readers
- —ConfidenceMeter numeric value not exposed as aria-valuenow
Reduced Motion
WCAG 2.1 SC 2.3.3 — Motion animation triggered by interaction can be disabled unless essential to the information being conveyed.
The motion system respects prefers-reduced-motion at the CSS token level. All animation duration tokens (--ds-motion-micro through --ds-motion-scenic) collapse to 0.01ms when the user preference is active. Keyframe animations are suppressed. The motion foundation page documents this as a first-class design commitment.
Global @media (prefers-reduced-motion: reduce) rule suppresses all token-driven animations. Transition durations set to near-zero. Skeleton pulse, streaming dots, and loading spinners all respect the preference.
- +@media (prefers-reduced-motion: reduce) — global suppression in bauhaus.css
- +All --ds-motion-* tokens collapse to 0.01ms
- +Accordion open/close animation disabled
- +StreamingDot pulse suppressed
- +Skeleton shimmer suppressed
- +Dialog/Modal backdrop transition suppressed
Text Alternatives
WCAG 2.1 SC 1.1.1 — All non-text content has a text alternative that serves the equivalent purpose.
SVG icons include aria-hidden='true' when decorative or aria-label when informational. The HeroVisual component uses decorative SVG with no alt needed. Color-only indicators (StatusDot, CoverageTag) include text labels alongside color.
Decorative SVGs hidden from assistive technology. Informational icons labeled. Color is never the sole indicator — text accompanies all status badges and coverage tags.
- +SVG icons — aria-hidden='true' for decorative, aria-label for informational
- +StatusDot — text label alongside color indicator
- +CoverageTag — text label ('STRONG', 'MODERATE', 'GAP') inside colored badge
- +Hamburger icon — aria-label='Open menu' / 'Close menu'
- +Semantic triad — color swatches accompanied by text labels
- —Finance card sparkline charts lack text summaries for screen readers
- —EvalRadar SVG polygon lacks accessible data table alternative
Responsive Reflow
WCAG 2.1 SC 1.4.10 — Content can be presented without loss of information or functionality, and without requiring scrolling in two dimensions.
All site pages and components use responsive layouts (CSS Grid, Flexbox) with clamp() for fluid typography. The MobileMenu provides full navigation at narrow viewports. Grid layouts collapse from multi-column to single-column at the sm/md breakpoints.
Tested at 320px viewport width — all content reflows to single column with no horizontal scrolling. Typography uses clamp() for fluid scaling. Navigation collapses to drawer at md breakpoint.
- +CSS Grid with responsive columns (1fr at mobile, multi-column at sm/md/lg)
- +clamp() typography — hero text scales from 32px to 56px
- +MobileMenu — full sidebar navigation accessible at narrow viewports
- +Code blocks — overflow-x-auto prevents horizontal page scroll
- +Foundation cards — single column at mobile, grid at desktop
Link Purpose
WCAG 2.1 SC 2.4.4 — The purpose of each link can be determined from the link text alone, or from the link text together with its context.
Navigation links use descriptive text ('Getting Started', 'Browse Patterns'). Component cards use the component name as link text. External links (GitHub) include target='_blank' with rel='noopener noreferrer'.
All links use descriptive text. No 'click here' or 'read more' without context. External links are identified by text content ('GitHub', 'View on GitHub').
- +Navigation — descriptive link text for all routes
- +Component cards — component name as accessible link text
- +External links — rel='noopener noreferrer' on all target='_blank'
- +Footer CTA — 'Get Started' and 'View on GitHub' are self-describing
- —No visual indicator for external links (no external-link icon or sr-only text)
Error Identification
WCAG 2.1 SC 3.3.1 — If an input error is detected, the item in error is identified and the error is described in text.
The Input component supports an error state with descriptive error text rendered below the field. The error message is linked via aria-describedby. Callout component provides error/warning/info variants with appropriate icons and ARIA roles.
Input component has full error identification. Callout provides contextual error messaging. But pattern-level error states (MCPToolCall failure, API errors) show raw technical strings without structured error identification.
- +Input — error state with aria-describedby linking to error text
- +Callout — role='alert' for error variant, role='status' for info
- +EscalationBanner — structured error with recovery guidance
- —MCPToolCall error state shows raw strings without aria-describedby
- —No form-level error summary pattern
- —EmptyState error variant lacks aria-live announcement
Testing Methodology
Token contrast ratios computed programmatically against WCAG 2.1 AA thresholds (4.5:1 normal text, 3:1 large text). All semantic token pairs verified in both light and dark themes.
Every interactive component tested for Tab, Enter, Space, Escape, and Arrow key operation. Focus trap verified in all overlay components (Dialog, Modal, MobileMenu). Focus restoration verified on close.
Core UI primitives verified with VoiceOver (macOS). ARIA attributes audited across 476 instances. Pattern components assessed for state announcement coverage.
prefers-reduced-motion media query verified at the token level. All animation tokens collapse to 0.01ms. Visual regression confirmed: layout integrity preserved without animation.
All pages tested at 320px, 375px, 768px, 1024px, and 1440px viewport widths. No horizontal scroll at any breakpoint. Single-column reflow verified for all grid layouts.
Remediation Priorities
Add aria-live regions to AgentCard, MCPToolCall, TaskQueue, and ConfidenceMeter for real-time status changes.
Add a visually-hidden skip link at the top of the site layout that becomes visible on focus.
Create a form-level error summary component that lists all errors with in-page anchor links.
Add a screen-reader-only '(opens in new tab)' annotation and optional visual icon to external links.
Add aria-label summaries and hidden data tables for finance sparkline charts and EvalRadar SVG.
Conduct formal focus order audit on complex multi-element patterns (AgentTimeline, TaskQueue, ReasoningTree).