Skip to main content

FilterToolbar

FilterToolbar

Toolbar that folds filter controls behind a Filters button once more than three controls would sit inline

Tags: component layout toolbar filters

View Demo

FilterToolbar is a Toolbar with a built-in rule for busy list pages: when the left side would hold more than three controls (search counts; count text does not), the filters move behind a Filters button. On desktop the button expands a FilterPanel under the strip; on mobile (below lg) it opens a full-screen Dialog. Filters apply live in every mode.

Pair it with ActiveFilterChips under the toolbar so filters hidden behind the button stay visible and removable.

Import​

<script>
  import { FilterToolbar, ActiveFilterChips } from '@classic-homes/theme-svelte';
</script>

Basic Usage​

<script>
  import {
    FilterToolbar,
    ActiveFilterChips,
    Input,
    Label,
    Select,
  } from '@classic-homes/theme-svelte';

  let search = $state('');
  let status = $state('');
  let type = $state('');
  let supplier = $state('');

  const activeCount = $derived([status, type, supplier].filter(Boolean).length);
  const clear = () => (status = type = supplier = '');
</script>

<FilterToolbar label="Sign tools" filterCount={3} {activeCount} onClear={clear}>
  {#snippet search()}
    <Input type="search" aria-label="Search signs" class="w-full sm:w-56" bind:value={search} />
  {/snippet}
  {#snippet filters({ inline })}
    <div class="space-y-1">
      <Label for="status" class={inline ? 'sr-only' : ''}>Status</Label>
      <Select id="status" options={statusOptions} bind:value={status} />
    </div>
    <!-- …type, supplier -->
  {/snippet}
  {#snippet summary()}<span class="text-sm">42 signs</span>{/snippet}
</FilterToolbar>

<ActiveFilterChips
  chips={status
    ? [{ key: 'status', label: `Status: ${status}`, onRemove: () => (status = '') }]
    : []}
  onClearAll={clear}
/>

Props​

Prop Type Default Description
label string required Accessible name of the strip (required)
search Snippet - Search field; always inline
filters Snippet<[{ inline: boolean }]> required Filter controls; inline is false inside the panel/dialog
filterCount number required Controls rendered by filters (drives the collapse rule)
activeCount number 0 Active filters, shown as a badge on the button
summary Snippet - Result count after the filters / button
end Snippet - Right-aligned cluster
onClear () => void - Resets every filter ("Clear all")
maxInline number 3 Most left-side controls (search included) allowed inline
open boolean false Panel/dialog open state (bindable)
mobileQuery string '(max-width: 1023px)' Below this viewport filters always collapse into a dialog
class string - Additional classes on the wrapper

Accessibility​

  • The Filters button carries aria-expanded, and aria-controls pointing at the panel on desktop; its name includes the active count ("Filters, 2 active").
  • The collapsed panel is inert, so hidden controls are not tabbable or announced.
  • Render a label for every control; keep it sr-only when inline is true.
On this page