Filter bar

The one way to filter a table listing. A row of chips under the Section top bar: every chip is a field that already has a value, + Filtr adds another field, and on the right sit Vymazat filtry and Uložit pro všechny (which turns the current filter into a segment). React: apps/web/src/components/FilterBar.tsx; CSS apps/web/src/ds/filter-bar.css (prefix .fb).

The model is Notion, taken literally: a chip without a value does not exist. Choosing a field and choosing its value is one act — the chip appears already filled in, and clearing the value removes the chip.

When to use

  • Filtering a table listing (deals, companies, contacts, invoices, tasks) — the bar sits directly under the Section top bar of that listing.
  • A listing whose filter must be readable without clicking: the bar states the whole query in one line, including the logic between chips.
  • Turning a filter into a saved segment — that is what Uložit pro všechny is for.

When not to use

  • A form. The bar filters what is already on screen; it does not collect input.
  • One switch over a listing ("only mine", "include inactive") — that is a toggle chip in the Section top bar (.tchip), not a bar of its own.
  • Building a segment definition with nested conditions — that is the segment builder (SegmentBuilder, .acrm-fchip), a separate screen with its own grammar.
  • A search that should always be visible: use the bar's own inline search (.fb-search), not a chip.

Anatomy

  • Bar .fb: sticky at left: 0, z-index: 5, flex with gap: var(--sp-3), padding var(--sp-3) var(--fb-gutter, 20px), hairline border-bottom, background --bg-canvas. The 20px gutter holds the content line of the listing; a host with a different line overrides --fb-gutter.
  • Chip .fb-chip: 30px tall, padding: 0 10px, gap: 7px, border-radius: var(--radius-md), 1px solid var(--border-strong), --surface-100, 500 12px, box-shadow: var(--shadow-xs) — metrics 1:1 with the Section top bar chip .tchip.
  • Spacer .fb-sp (flex: 1) separates the field chips from the bar actions.
  • Add / reset .fb-add, .fb-reset: ghost, 30px, no border, --fg-muted, hover --hover-surface.
  • Save .fb-save: the only emphasised action in the bar — --accent-tint fill, --accent-ink text, 600 12px var(--font-display), hover adds the accent border.
  • Conjunction .fb-conj: pill with a dashed border, --sp-6 tall, text a zároveň; the active nebo (.or) switches to a solid border and --ink-900 — neutral, never accent (ACRM-50: the conjunction records logic, it is not an action).
  • Group .fb-group: tinted --active-surface block that shows how far a nebo group reaches. Vertical padding is cancelled by a negative margin, so the bar is 47px high with or without a group.
  • Inline search .fb-search: 30px boxed field in the row of chips (min 220px), --surface-75, focus-within paints the border accent and the fill --surface-100.

Phone (below 720 px) — Filtry sheet (2026-09-28, acrm !4088)

With onOpenChange the bar is a bottom sheet „Filtry" on a phone (without it, it stays an inline bar — lists are switched over one by one):

  • Trigger: the bar's Filtr icon button with a neutral CountBadge (number of active conditions); next to it Řadit (SegmentScreen, phone only) with its count — a sheet of the sortable columns, a second tap flips the direction, a third clears it.
  • Sheet, top to bottom: the inline search field, the active conditions one per row (chip over the width + ⋮ „Odebrat filtr", red), „+ Přidat filtr", „Vymazat vše", and Hotovo (<Button touch>).
  • „Uložit pro všechny" is not in the sheet — the page gives it to the bar's ⋮ as a phoneOnly action.
  • „Vymazat filtry" / „Vymazat vše" is <Button variant="ghost" tone="muted"> (owner 2026-09-28): --fg-secondary, --fg on hover.
  • Desktop unchanged.

Best practices

Do

  • Keep the bar to one row of the query: field chips left, actions right, nothing else in between.
  • Let the toggle chip in the Section top bar open and close the bar in both directions — closing it must not switch the filters off; the accent dot says they are still on.
  • Show the conjunction between chips always, not on hover: the reader must be able to read the logic without clicking.
  • Give the host its own --fb-gutter when its content line differs from the 20px of a table listing.

Don't

  • An empty chip ("Fáze: —") waiting for a value; pick the field in + Filtr and set the value in one go.
  • A count of results in the bar ("12 z 340") — the listing says that, not the filter.
  • A second emphasised action next to Uložit pro všechny; the rest are ghost actions.
  • A .set dot on a field chip in the bar. The dot belongs to toggle chips (+ Filtr, modifiers) where the value is not written out; on a bar chip the value is right there, so the dot would be noise.

Content

  • Chip = field name (.k, muted) + value (.v, 600, clipped at 148px). No operator word in the chip; the operator lives in the menu.
  • Bar actions are verbs: Filtr, Vymazat filtry, Uložit pro všechny.
  • Conjunction is lower case, two states only: a zároveň / nebo.
  • Sentence case everywhere, no capitals, no trailing period.
  • filter-chip — the chip and the menu it opens.
  • section-top-bar — hosts the toggle chip that shows and hides this bar.
  • record-list, table — the listings this bar filters.
  • Stories: components--filter-bar--default, --variants, --states, --with-icon-kbd, --composition.

Source: docs/filter/filter-bar.usage.md · GitLab