Top bar
canonThe application frame bar: workspace switch, global search (⌘K), utilities and account — never changes with the screen.
React implementation lives in the app (AppShell.tsx — TopBar). .as-ai-trigger is a reserved, unused variant — decision A4.
Used in no file in the app (main)
Keyboard
- Everything in the bar is a real
<button>: workspace switch, search, utilities, account. Tab reaches them left to right. - Global search answers to ⌘K / Ctrl+K through a window listener, and the button advertises it with
aria-keyshortcuts="Meta+K"as well as the visible⌘Kchip. - The assistant answers to ⌘J and says so with
aria-keyshortcuts="Meta+J"; it has no visible chip because the trigger already carries a name. - Menus opened from the bar are canonical popovers: Escape closes them, and the same keyboard limits apply as for Popover (no arrow-key movement inside the menu).
- The mobile hamburger toggles the off-canvas sidebar and is the first tab stop on small screens.
ARIA
- The workspace switch is
aria-haspopup="menu"+aria-expanded; the account button carries the user's name asaria-labelandtitle. - The hamburger has
aria-expandedandaria-controls="as-side-nav", so the sidebar it opens is named. - The assistant trigger sets
aria-busywhile a run is going — the pulse is not the only signal. - The
.dotindicator is decorative; what is waiting must also be readable in the panel that opens. Nothing announces its appearance (no live region). - The bar is a
<header>; the sidebar is<aside id="as-side-nav">, so both are landmarks.
Focus
- No focus rule in
app-shell.cssand nooutline: none: all controls are buttons, so the foundation ring fromds.cssapplies (outline: 2px solid var(--accent); outline-offset: 1px). - Opening the search palette moves focus into it; closing returns focus to the search button.
- The bar is not a focus trap and never steals focus on load.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Workspace name and account initials sit on
--surface-100in ink tones — above 12:1 in both themes. - Search placeholder
.phuses--fg-muted= 2.85:1 light, 3.63:1 dark — below AA for text; it is a placeholder, and the button's accessible name carries the meaning. - The
⌘Kchip is.acrm-kbd— the canonical shortcut chip, muted by design. - The waiting dot is accent on the button surface; it is a non-text indicator above the 3:1 minimum, but it is small — the panel behind it is the real carrier.
- Focus ring
--accent= 4.44:1 light, 3.83:1 dark.
Known limits
- The search placeholder and the utility icons sit at
--fg-muted(2.85:1 in light theme). - Nothing is announced when the dot appears — a screen-reader user learns about a new notification only by opening the bell.
- The assistant's busy pulse is visual;
aria-busycovers assistive technology, but there is no text status. - The bar has no skeleton state: while the workspace list loads it renders the current workspace only.
Source: docs/app-shell/top-bar.a11y.md · GitLab