Skip to content

Accessibility ​

All @sylonik/macos-ui components are designed with accessibility as a core requirement, targeting WCAG 2.1 AA compliance. This guide documents the accessibility features of each component and provides recommendations for testing.

Per-Component Accessibility ​

Window ​

The Window component is implemented as an accessible dialog:

FeatureImplementation
Rolerole="dialog" on the window container.
Modal indicatoraria-modal="true" signals to assistive technologies that content behind the window is inert.
Labelingaria-labelledby="window-title-{id}" associates the window with its title element.
Traffic light buttonsEach button (Close window, Minimize window, Maximize window) has a descriptive aria-label.
Focus trapWhen a window is focused, Tab/Shift+Tab navigation wraps within the window boundaries, preventing accidental focus escape.
Keyboard dismissalEscape key can be bound to onClose for keyboard-accessible window closing.
Resize handleThe resize handle has aria-label="Resize window".

Dock ​

FeatureImplementation
Item labelingEach dock item has aria-label set to its label prop value.
RoleEach dock item has role="button".
TooltipHovering over a dock item shows a visible tooltip with the item label.
FocusDock items are focusable and activatable via standard button interaction patterns.
FeatureImplementation
Menu roleDropdown containers have role="menu".
Item roleMenu items have role="menuitem".
Keyboard navigationArrowDown/ArrowUp moves focus between items. Escape closes the dropdown.
Disabled itemsDisabled items have the disabled HTML attribute and reduced visual opacity.
Focus managementFocus follows keyboard navigation within the open dropdown.

DesktopIcon ​

FeatureImplementation
Rolerole="button" identifies the icon as an interactive element.
Labelingaria-label is set to the icon's label prop.
Selection statearia-selected communicates whether the icon is selected.
FocustabIndex={0} makes icons focusable via Tab navigation.
Keyboard handlersEnter triggers onDoubleClick (open). Space triggers onClick (select).

Keyboard Navigation Overview ​

ComponentKeyAction
WindowTabMove focus to next focusable element within the window.
WindowShift + TabMove focus to previous focusable element within the window.
WindowEscapeClose window (when bound to onClose).
MenuBarArrowDownMove focus to next menu item in dropdown.
MenuBarArrowUpMove focus to previous menu item in dropdown.
MenuBarEscapeClose the open dropdown menu.
MenuBarEnterActivate the focused menu item.
DesktopIconEnterTrigger onDoubleClick (open the item).
DesktopIconSpaceTrigger onClick (select the item).
DesktopIconTabMove focus to the next icon.
DockTabMove focus between dock items.
DockEnter / SpaceActivate the focused dock item.

Screen Reader Support ​

All components use semantic HTML and ARIA attributes to communicate their state and purpose:

  • Window is announced as a dialog with its title.
  • Dock items are announced as buttons with their labels.
  • MenuBar dropdowns are announced as menus with individual menu items.
  • DesktopIcons are announced as buttons with their labels and selection state.

Focus management ensures that screen reader users always know where they are and can navigate predictably.

Testing Accessibility ​

We recommend the following approach to verify accessibility:

Automated Testing ​

  • axe-core — Use @axe-core/react during development to catch common issues.
  • eslint-plugin-jsx-a11y — Add the ESLint plugin to catch accessibility issues at lint time.
  • Playwright + axe — Use @axe-core/playwright in E2E tests for automated WCAG compliance checks.

Manual Testing ​

  1. Keyboard-only navigation — Verify all interactive elements are reachable and operable without a mouse.
  2. Screen reader testing — Test with VoiceOver (macOS), NVDA (Windows), or JAWS.
  3. Color contrast — Verify text meets minimum contrast ratios (4.5:1 for normal text, 3:1 for large text).
  4. Zoom testing — Verify the UI remains usable at 200% browser zoom.
  5. Reduced motion — Verify animations respect prefers-reduced-motion (consider adding support if needed).

Example: axe-core with Vitest ​

tsx
import { render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'

expect.extend(toHaveNoViolations)

it('Window has no accessibility violations', async () => {
  const { container } = render(
    <WindowManagerProvider>
      <Window id="test" title="Test">
        <p>Content</p>
      </Window>
    </WindowManagerProvider>
  )

  const results = await axe(container)
  expect(results).toHaveNoViolations()
})

See Also ​

  • Window — Window-specific accessibility details.
  • Dock — Dock accessibility features.
  • MenuBar — MenuBar keyboard navigation.
  • DesktopIcon — DesktopIcon ARIA attributes.

Released under the MIT License.