Layout switch — accessibility

Keyboard

  • [ toggles the menu (full ↔ rail) everywhere in the app, whether or not any other segment is registered.
  • ] toggles the context panel, but only when a screen registered one; otherwise the key is a no-op — there is nothing to toggle.
  • Both keys are ignored with a modifier held (metaKey/ctrlKey/altKey), when the keydown event is already defaultPrevented, while typing in a field (input, textarea, select, or any contenteditable — including a rich editor's body), and while any overlay (dialog, popover, drawer) sits above the shell.
  • Every button in the pill is a real <button>; Tab reaches them in DOM order (menu → list → panel).

ARIA

  • The pill is role="group" with one aria-label for the whole cluster ("Layout") — a screen reader announces it once, not as three unrelated buttons.
  • Every button sets its own aria-label; none relies on the native title fallback for its accessible name. The DS Tooltip (not title) provides the visible hint, wired through aria-describedby on the button it wraps.
  • The list-layout and context-panel buttons carry aria-pressed — genuine two-state toggles. The menu button carries aria-expanded instead, and no aria-pressed: it is an action that switches the whole sidebar, not a state of itself.
  • The divider between segments (.pp-ldiv) is aria-hidden="true" — purely visual, never focusable or announced.

Focus

  • Foundation ring from ds.css applies to every button; the pill declares no focus style of its own.
  • Toggling a segment does not move focus; the button that was activated keeps it, so most screen readers re-announce its new aria-pressed/aria-expanded state on their own.
  • The pill is not a focus trap and never steals focus on mount.

Contrast

Computed from packages/ui/src/tokens/tokens.css:

  • Rest icon colour --fg-secondary on the pill's --surface-100: 3.93:1 light, 6.20:1 dark.
  • Hover/active/pressed icon colour --fg on --active-surface: 15.29:1 light, 11.41:1 dark.
  • Pill border --border on --surface-100: 1.15:1 light, 1.22:1 dark — decorative only, the shape does not depend on the hairline being visible against every background.
  • Focus ring --accent: 4.44:1 light, 3.83:1 dark (same token as the rest of the shell).

Known limits

  • Nothing is announced when a segment appears or disappears (a screen registering or unregistering support) — a screen-reader user discovers the change only by tabbing to where the pill sits.
  • The tooltip's keyboard hint is baked into the same string as the accessible name ("Collapse menu to icons ( [ )"); it is verbose for a screen reader on every focus, but keeps the hint and the name from drifting apart (see usage doc, Content).

Source: docs/app-shell/layout-switch.a11y.md · GitLab