Record select — accessibility
Keyboard
- Trigger (
button): Tab focuses it; Enter or Space toggles the panel (native click);disabledremoves it from the tab order. WithanchorRefthere is no trigger — the host's element opens the panel and the panel is mounted only while open. - On open, focus moves to the search input on the next animation frame (after the panel is positioned — a hidden panel cannot be focused).
- Typing filters in memory over
label,subandkeywords, with diacritics folded ("novak" finds "Novák"). - Esc closes the panel and returns focus to the anchor or trigger; the key is consumed (
preventDefault), so page-level Esc shortcuts such as "close record" do not fire and a panel inside a dialog never closes the dialog with it. - Rows are real
buttons: Tab moves from the search input through the rows; Enter or Space picks. Single mode closes on pick; multi keeps the panel open. - Arrow keys, Home and End move focus inside the panel — they come from the canonical
Popoverbehaviour shared by every menu in the system. - Enter in the search fires
onCreatewhen the footer's quick-create row is offered. - Click outside the anchor and the portalled panel closes it (capture-phase
mousedown). - The clear "×" is
tabindex="-1"— mouse only (limit).
ARIA
- Trigger:
aria-haspopup="listbox",aria-expanded,aria-labelfromariaLabel; norole="combobox"(a plain button). - Search input carries an
aria-label(the search placeholder), so it is named without relying on the placeholder alone. - List:
div[role="listbox"]witharia-multiselectablein multi mode; rowsbutton[role="option"]witharia-selected. - Sections are
role="group"witharia-label, so the grouping is announced even where the visible name is not drawn (a single section draws no name). - The indicator is decorative —
aria-selectedcarries the state; the mark is decorative too (the name is in.nm). - Clear:
span[role="button"][aria-label="Vymazat"]. - Panel carries
data-rr-keep-openso the record rail treats clicks inside the portal as inside the editor. - No
aria-controlsfrom trigger or input to the panel.
Focus
- Trigger
:focus-visible= foundation ringoutline: 2px solid var(--accent); outline-offset: 1px; while open the wrapper adds the accent border + 3px 16% halo to the trigger. - Search input:
outline: 0— no ring on the input itself; the open trigger halo is the only indicator (the input is visually "inside" a fresh panel). - Row buttons get the foundation outline when tabbed to.
- Closing (Esc, pick, outside click) returns focus to the anchor or trigger.
Contrast
Computed from packages/ui/src/tokens/tokens.css:
- Trigger filled
--ink-900on--surface-100= 17.59:1 (light), 14.53:1 (dark). - Trigger placeholder / disabled
--fg-muted= 2.85:1 (light), 3.63:1 (dark); disabled on--surface-752.75:1 / 3.82:1. - Search input text
--ink-90014px = 17.59:1 / 14.53:1; placeholder--fg-muted2.85:1 / 3.63:1. - Row name
.nm--ink-900= 17.59:1 / 14.53:1; on hover surface 17.0:1 / 13.09:1. - Row secondary
.em--fg-secondary300 12px on white = 3.93:1 (light) — below AA; 6.2:1 (dark). - Section name
var(--label)in--fg-mutedon the panel = 2.85:1 (light), 3.63:1 (dark) — below AA in both themes. The Art Director asked for the lighter tone on 12 September 2026 (ACRM-227) with these numbers in front of him: the label has to step back behind the rows it separates. It is a decorative group label — assistive technology reads the group fromrole="group"+aria-labelon.acrm-recordselect-grp, not from this text — so nothing depends on reading it. Measured on the panel surface, which is the lighter of the two grounds. - Indicator
--accenton white = 4.44:1 (light), 3.83:1 (dark). - Empty text
--fg-muted13px = 2.85:1 / 3.63:1. - Panel border
--border-strong1.38:1 / 1.55:1; search hairline--border≈ 1.1:1.
Known limits
- The clear "×" is unreachable by keyboard.
- Rows are individual tab stops as well as arrow targets.
- Section names, secondary text and all
--fg-mutedtext are below AA 4.5:1 in the light theme. - Nothing announces that the pinned block at the top is "what you already picked" — the order is a visual convention.
Muted and warning options (acrm !4348)
- A muted option keeps the name at
--fg; the dimming is visual only (mark at 50 %) and the reason is real text in the option's accessible name, so screen readers hear „Bohumil Král, dovolená do 6. 10.“. subTone: 'warn'text is part of the accessible name as well; colour is never the only signal.
Source: docs/select/record-select.a11y.md · GitLab