Section top bar — accessibility

Keyboard

  • Every control in the bar is a real <button> or <input>; Tab walks identity → view controls → search → actions → primary action, which is the reading order of the bar.
  • Chips that open a menu carry aria-haspopup and aria-expanded through the control that opens them; Escape closes the menu (Popover's contract).
  • The search is a normal input: typing filters, Escape is left to the screen.
  • Inline rename: Enter commits, Escape cancels, and both stop propagating so the record's own Enter/Escape handlers do not fire as well. Blur commits only if Enter has not already done it (a latch prevents double-saving).
  • The bar has no shortcut of its own; a shortcut belongs to the action that answers to it.

ARIA

  • The bar is a plain container; its accessible structure comes from the controls inside. The identity is a heading-level text, not an <h1> — the page supplies its own heading semantics.
  • SectionTopBarChip sets aria-haspopup="menu" and aria-expanded only when it is a menu trigger (chevron / open), so a plain toggle chip is not announced as a menu.
  • The search input is named by ariaLabel; the placeholder is not a name.
  • Rename mode swaps the text for an input with an accessible name from renameAriaLabel; while renaming the input is disabled and cannot be reopened.
  • Dev-only warnings guard the semantics that matter: identity missing, onRename combined with a dropdown, icon combined with a click handler.
  • Phone ⋮: IconButton named „Další akce" with aria-haspopup="menu" / aria-expanded; its bottom sheet is titled „Další akce".
  • Filtr icon button on a phone: its visible label is hidden, so the name carries both — „Filtr, aktivní podmínky: N".

Focus

  • .tbar-pos.is-link and .tbar-mark declare their own :focus-visible ring in ds.css; everything else in the bar is a <button> and takes the foundation ring (outline: 2px solid var(--accent); outline-offset: 1px).
  • The search box shows focus through :focus-within on the wrapper (accent border) rather than a ring on the input.
  • Entering rename mode moves focus into the input and selects nothing — the caret lands where the user clicked.

Contrast

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

  • Identity name: ink on --surface-100 = 17.59:1 light, 14.53:1 dark.
  • Chip value .v ink = 17.59:1 light; chip key .k --fg-muted = 2.85:1 light, 3.63:1 dark — below AA for text, the value carries the meaning.
  • Chip border --border-strong on --surface-100 = 1.38:1 light — hairline, below the 3:1 non-text minimum.
  • Search placeholder --fg-muted = 2.85:1 light.
  • Focus ring --accent = 4.44:1 light, 3.83:1 dark — passes 3:1.

Phone view row (acrm !4114)

  • Icon-only tools in row 2 (Seskupit, Řadit, search) have an aria-label; when Seskupit or Řadit is off its default, the label and the title carry the chosen option („Řadit: Termín") — the accent dot is decorative and never the only signal.
  • The tools open a bottom sheet (role="menu", rows menuitemradio with aria-checked); the sheet is closed by „Hotovo", the scrim or Escape.
  • Expanded search has its own aria-label and a labelled × („Zavřít"); closing clears the query.

Known limits

  • Muted keys and placeholders sit at 2.85:1 in light theme.
  • No disabled style exists for a chip or the search; a control that cannot be used has to be left out instead.
  • On a narrow screen the bar scrolls sideways: a control can be off-screen with no visual hint other than the cut edge.
  • Screens that hand-roll the identity (status star, logo trigger, colour swatch) bypass the component's guardrails, so their semantics depend on the screen.

Source: docs/section-top-bar/section-top-bar.a11y.md · GitLab