busy-office-ui
Menu

Filters

Filter bar + applied-filter chips. Each chip's remove button names its filter in the aria-label. Filtering itself is your code (form submit or HTMX). Not for one or two fields — a single search input in the table toolbar reads better than a bar and chips; the chips earn their place once a user can have several filters applied at once and lose track of them.

Filtering something — bar + multi-select + matching rows

PO #Cost centerAmount
PO-88210CC-4021$4,208.00
PO-88211CC-4021$18,940.50

The full loop: the multi-select dropdown is where chip values come from, the active chip echoes the applied filter, the rows below match it. Also the base vs --active chip contrast — an inactive chip ("Overdue only") is an available filter, an active one is applied and removable.

Saved views opt-in

Views are just links (server-rendered, zero new CSS) — but which one is "active" and what the filter bar's fields show shouldn't be hand-set per page. initSavedViews() derives both from the URL: it reads location.search, fills matching .bo-filter-bar fields, and marks whichever [data-saved-views] link's own querystring matches the current one aria-current="page" — clearing it from the others. Try it: click a saved view below, watch the filter bar and the active chip update to match.

Markup

<!-- A real <form>, so type="reset" works and role="search" sits on it. -->
<form class="bo-filter-bar" role="search" aria-label="Invoice filters">
  <input class="bo-input" type="search" name="q" aria-label="Search invoices" />
  <select class="bo-select" name="status" aria-label="Status">…</select>
  <span class="bo-chip bo-chip--active">Status: Pending
    <button class="bo-chip__remove" type="button"
            aria-label="Remove filter Status: Pending">✕</button>
  </span>
  <button class="bo-btn bo-btn--ghost" type="reset">Clear all</button>
</form>

Quick reference generated

ClassRole
.bo-chip block (root)
.bo-chip--active variant: active
.bo-chip__remove part: remove
.bo-filter-bar block (root)
.bo-form-field block (root)

State attributes: data-saved-views

API generated from CSS

Blocks bo-chipbo-filter-barbo-form-field
Parts bo-chip__remove
Variants bo-chip--active
data-* hooks data-saved-views
JS required Optional: initSavedViews() — populates a .bo-filter-bar's fields from the URL querystring and marks the matching [data-saved-views] link aria-current.
  • Make the bar a real <form role="search"> so type="reset" works.
  • Chip remove buttons name their filter in aria-label; 24px hit area is built in.
  • Saved views are just links — persistence (naming, saving, deleting a view) is your code; the behavior only keeps the UI in sync with whichever view the URL says is active.
Contrast — 27 AA pairs this component uses (generated, CI-gated)
TextonLightDark
text-primary bg-surface 17.74 16.15
text-primary bg-canvas 16.98 18.08
text-secondary bg-surface 10.31 11.46
text-secondary bg-muted 9.37 9.75
text-muted bg-surface 7.56 6.65
text-muted bg-muted 6.87 5.66
bg-surface text-primary 17.74 16.15
accent-text accent-subtle 7.27 8.38
danger-text bg-surface 6.47 8.89
success-text bg-surface 5.02 12.02
accent bg-surface 5.47 9.07
text-primary bg-hover 16.12 13.75
text-primary bg-selected 17.01 11.86
accent-text bg-selected 7.27 8.38
text-primary bg-surface-raised 17.74 14.51
border-control bg-surface 4.83 6.65
accent bg-selected 5.25 6.66
--bo-state-error-color bg-surface 4.83 6.1
accent accent-subtle 5.25 6.66
border-control bg-muted 4.39 5.66
accent bg-muted 4.97 7.72
warning-strong bg-muted 4.56 6.69
warning-strong bg-surface 5.02 7.86
danger bg-muted 4.39 5.19
text-primary bg-muted 16.12 13.75
success-text bg-muted 4.56 10.23
danger-text bg-muted 5.88 7.57

Maturity generated

Introduced
v0.1.0
CSS floor
Chrome/Edge 99 · Firefox 97 · Safari 15.4
Alignment scored
2026-08-21
Assistive-tech evidence
None recorded

Pin this component's first release with npm i @busy-office/ui@0.1.0. The floor is derived from this component's stylesheet alone — set by @layer — so it sits at or below the whole framework's Chrome/Edge 119 · Firefox 129 · Safari 17.5. No assistive-technology runtime result is recorded for any component: it needs a human listening to VoiceOver and NVDA on owner hardware; no gate in this repo can simulate announcement behaviour. What automation covers instead is test:axe across every page at 1440 and 390, and check:forced-colors — see accessibility.

Design-system alignment generated

100% (18/18) — how closely this component follows the design language. Each rule is scored 0-3 against a checkable citation; rules that don't apply leave the denominator. Scored 2026-08-21.

Rule Score Evidence
Typography 3 / 3 no raw font-size
Colour 3 / 3 zero raw hex
Spacing 3 / 3 0.125rem chip hug commented (94.2); the 1rem remove box already carried its WCAG 2.5.8 hit-area note
Interaction 3 / 3 composes initDropdowns + initSavedViews; applied filters are visible chips, not state hidden behind a dialog
Content 3 / 3 the opener says a bar and chips are wrong for one or two fields, pointing at a search input in the table toolbar, and says what earns the chips (94.8)
Fit 3 / 3 filter bar above tables/lists — the context list-report actually uses
↑↓ move Enter open Esc close