Filter bar
canonRow of filter chips with add, reset and save controls.
React implementation lives in the app (apps/web/src/components/FilterBar.tsx), not in @acrm/ui. Phone (owner 2026-09-28, acrm !4088): with onOpenChange the bar is the bottom sheet „Filtry" (search, conditions with ⋮ Odebrat, Přidat filtr, Vymazat vše, Hotovo); the Filtr toggle shows a neutral CountBadge, Řadit (SegmentScreen) a sort sheet; „Uložit pro všechny" moves to the bar's ⋮ (phoneOnly). „Vymazat filtry" = Button ghost tone muted. Desktop unchanged. 14 Oct 2026 (owner "24c approved", proto/lista-vypisu@a16fe17a7, acrm !5045): no word labels — Filtr = icon (+ dot), Seskupit/Řadit/Zobrazení = icon + chosen value, field name only in aria-label/tooltip; open row: chip = field icon + value, „+" and „×" icons; closed by default, „Jen moje" is a chip inside Filtr. Dev check 14 Oct on /tasks /pipeline /objednavky /setkani /lide, 1440 + 390: 0 label words, no overflow. Registry repaired 17 Oct 2026 (review F2): removed classes with no CSS definition and no TSX use on main: .fb-add, .fb-reset, .fb-save.
Used in 23 files · 21 areas · 48× — components/communication, components/datamart, pages/AccountingInvoicesPage, pages/AccountingReceivedInvoicesPage, pages/AsistentAuditPage, pages/DokumentyPage …
apps/web <FilterBar> <FilterToggleChip>.fb-clear .fb-conj .fb-menu .fb-sheet +3SourceStoryLegacy card filter-bar.htmlKeyboard
- Every control in the bar is a real
<button>: Tab reaches the chips in reading order, then+ Filtr, thenVymazat filtryandUložit pro všechny. Enter and Space activate them. - A chip carries
aria-haspopup="menu"andaria-expanded; Enter opens its menu, Escape closes it (handled byPopover). - The inline search
.fb-searchis a plain<input>inside a<label>: Tab reaches it, typing filters, there is no shortcut to jump to it. - The conjunction is a button — Enter switches
a zároveň↔nebo. - Missing: the menu does not take focus when it opens, and it is portalled to the end of
<body>, so Tab from the chip walks the rest of the page instead of entering the menu. Arrow-key movement between menu rows does not exist either (see Popover — known limits; the same defect, one implementation).
ARIA
- Chip:
aria-haspopup="menu"+aria-expanded; the accessible name is the field name and the value read together ("Fáze Jednání"). - Toggle chip (
+ Filtrin the Section top bar) usesaria-pressed, notaria-expanded— it switches a region on and off rather than opening a layer. - Conjunction:
aria-labelsays what the click will do ("Spojka a zároveň, změnit na nebo") — the bare word "nebo" would be read as text, not as a control. - The bar itself is a plain
<div>with no landmark role; it is announced through the controls it contains. - The group
.fb-groupis decorative: the tint carries no semantics, the conjunction buttons do. - Phone (below 720 px, with
onOpenChange; acrm !4088, 28 Sep 2026): the toggle's visible label is hidden and only the neutral count stays, so its accessible name carries both: „Filtr" without active conditions, „Filtr, aktivní podmínky: N" with them. The Řadit icon button is named „Řadit". - The Filtry sheet is the canonical BottomSheet:
role="dialog"titled „Filtry", grip, scrim that closes on tap, and one full-width „Hotovo" (<Button touch>) — Escape or a click outside is never the only way out on a phone. Each condition's ⋮ is named „Podmínka <pole>" and opens a menu with „Odebrat filtr" (danger).
Focus
- No focus style is declared in
filter-bar.css. All the controls are<button>, so they take the foundation ring fromds.css:outline: 2px solid var(--accent); outline-offset: 1pxon:focus-visible. - The only element that opts out of the ring is the
<input>inside.fb-search(outline: 0); the wrapper answers with:focus-within— accent border plus--surface-100fill. The field is therefore visibly focused, but through the box, not a ring. - Opening and closing the bar does not move focus; the toggle chip keeps it.
Contrast
Computed from packages/ui/src/tokens/tokens.css (WCAG 2.x relative luminance):
- Chip value
.v--ink-900on--surface-100= 17.59:1 light, 14.53:1 dark. - Chip field name
.k--fg-mutedon--surface-100= 2.85:1 light, 3.63:1 dark — below AA 4.5:1 for text. It is the muted half of a pair whose value carries the meaning, but it is a genuine limit. - Chip border
--border-strongon--surface-100= 1.38:1 light, 1.55:1 dark — hairline, below the 3:1 non-text minimum; the chip is discoverable by its fill and shadow. - Ghost actions
.fb-add/.fb-reset--fg-mutedon--bg-canvas= 2.85:1 light, 3.63:1 dark — below AA; on hover the text goes to--ink-700(12.99:1). - Save
.fb-save--accent-inkon--accent-tint= 4.58:1 light, 5.37:1 dark — passes AA. - Conjunction
--fg-mutedon canvas = 2.85:1 light; activenebo--ink-900on--surface-100= 17.59:1. - Focus ring
--accenton--surface-100= 4.44:1 light, 3.83:1 dark — passes the 3:1 non-text minimum in both themes.
Known limits
- Muted text (
.k,.fb-add,.fb-reset,.fb-conj) sits at 2.85:1 in light theme — below AA for text. It is the DS-wide--fg-mutedlimit, not a bar-specific choice. - Keyboard users cannot reach a chip menu by Tab (portal + no focus move) and cannot move through its rows with arrows.
- The
disabledlook of a toggle chip comes from a different sheet (.lst-toggle:disabledinds/list-view.css); a chip used outside a listing has no disabled style of its own. - Nothing announces that the listing has been re-filtered — there is no live region; the change is only visible.
Source: docs/filter/filter-bar.a11y.md · GitLab