GitHub

Filter Pills

Active filters rendered as removable chips. Each chip shows "label: displayValue" and an x button that dispatches a bubbling esa-filter-remove event carrying { name, value }.

Stable .astro import EsaFilterPills from '@esa/ecology/esa-filter-pills.astro';

Preview

Pass the current active filters; each becomes a removable chip.

Status:OpenStatus:In reviewDepartment:Water Quality
<FilterPills filters={[
  { name: 'status', label: 'Status', value: 'open', displayValue: 'Open' },
  { name: 'status', label: 'Status', value: 'review', displayValue: 'In review' },
  { name: 'dept', label: 'Department', value: 'water', displayValue: 'Water Quality' },
]} />

States

A single chip renders inline; with no filters the component renders nothing at all — matching the Angular @if (activeFilters().length > 0) guard.

Priority:High
<FilterPills filters={[
  { name: 'priority', label: 'Priority', value: 'high', displayValue: 'High' },
]} />

<!-- Empty: renders no DOM -->
<FilterPills filters={[]} />

API

PropTypeDefaultDescription
filters ActiveFilter[] [] Active filters to render. Each is { name, label, value, displayValue }. Empty renders no DOM.

Events

PropTypeDefaultDescription
esa-filter-remove { name, value } — Bubbling/composed. Fires when a chip's remove button is clicked, identifying which filter to drop.

Accessibility

  • Each chip carries an aria-label of "label: displayValue" so the pairing is announced as one unit.
  • Remove buttons are native <button>s with an explicit "Remove {label} {displayValue}" accessible name.
  • Remove buttons show a visible focus ring via --focus-ring-* tokens.

Theming surface

Every public token this component reads, extracted from its source at build time, split by blast radius: the hooks that belong to this component, then the shared and system tokens it consumes alongside everything else. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

Type roles it names

This component does not set a size, weight, face or tracking of its own — it names a composite, and the composite carries all five. A composite is a class rather than a token, so none of it appears in the tables below; re-theme text by re-pointing the token family, not by editing the component.

RoleRe-pointReach
.typography-microcopy-xs
  • --typography-microcopy-xs-font-family 'DM Sans', sans-serif via --typography-font-family-sans → --font-family-dm-sans
  • --typography-microcopy-xs-font-size clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --font-size-100
  • --typography-microcopy-xs-font-weight 500 via --typography-font-weight-medium → --font-weight-500
  • --typography-microcopy-xs-letter-spacing 0.01em via --letter-spacing-default
  • --typography-microcopy-xs-line-height 1 via --line-height-none
+6 more components esa-app-shell, esa-button, esa-button-toggle, esa-kbd, esa-pill, esa-range-slider
.typography-microcopy-xs-subtle
  • --typography-microcopy-xs-subtle-font-family 'DM Sans', sans-serif via --typography-font-family-sans → --font-family-dm-sans
  • --typography-microcopy-xs-subtle-font-size clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --font-size-100
  • --typography-microcopy-xs-subtle-font-weight 350 via --typography-font-weight-regular → --font-weight-350
  • --typography-microcopy-xs-subtle-letter-spacing 0.01em via --letter-spacing-default
  • --typography-microcopy-xs-subtle-line-height 1 via --line-height-none
+7 more components esa-combobox, esa-date-picker, esa-filter-dropdown, esa-input-tag, esa-select, esa-text-field, esa-tooltip

Wired to this component

None. This component has no hooks of its own — it themes entirely through the shared tokens below, so every override here also moves its siblings.

Shared tokens it reads

Owned by a family or by the semantic layer. Still overridable — but scope the override (esa-foo { --form-border-color: … }) unless you mean to move everything that reads it. Reach says what else that is.

Token Reach Resolves to
--focus-ring-color component focus ring group · +34 more #3e9b4f via --color-border-default-focus → --color-grass-10
--focus-ring-width component focus ring group · +35 more 2px via --border-width-focus → --border-width-200
--color-background-brand-muted semantic every component #e9f6e9 via --color-grass-3
--color-background-brand-muted-hover semantic every component #daf1db via --color-grass-4
--color-content-brand semantic every component #2a7e3b via --color-grass-11
--color-content-default-secondary semantic every component #646464 via --color-gray-11
--color-content-utility-danger semantic every component #ce2c31 via --color-red-11
--radius-pill semantic every component 9999px via --radius-full
--transition-fast semantic every component 150ms via --duration-150
--typography-label-xs-font-size semantic every component clamp(0.625rem, 0.56rem + 0.32vw, 0.75rem) via --font-size-100
--spacing-050 primitive every component 0.125rem
--spacing-100 primitive every component 0.25rem
--spacing-200 primitive every component 0.5rem
--spacing-300 primitive every component 0.75rem