# Filter

> The filter panel above a catalog: one row of selects on wide screens, a Filters disclosure on phones, one GET form.

- Block class: `.hs-filter`
- CSS: `components/filter.css` (also inside https://brand.honorsociety.org/brand.css)
- JS: `components/filter.js`. Load it only on a page that has this component.
- Status: decided
- Decision record: https://brand.honorsociety.org/decisions/directories-round.html
- Variants: Filter panel, With active filters
- HTML page: https://brand.honorsociety.org/components/filter/

## What it is

A GET form that narrows a catalog by two to four fixed dimensions. The fields sit in a `details` panel. Below 48rem the panel is a closed "Filters" disclosure with the active count. From 48rem `filter.js` opens it, CSS hides the summary, and the selects sit in one row above the results. A select submits the form on change; the Apply button stays for the no-JS page.

## When to use

- A catalog with two or more dimensions: the scholarship directory (who it is for, type, offered by), the benefits list (category, membership needed).
- Any list long enough to need a pager.

## When not to use

- One dimension over a short list: use [Filter chips](/components/chips/), as the article topics.
- A keyword alone: use [Search](/components/search/).
- Sibling pages at their own URLs: use [Tabs](/components/tabs/).

## Rules

- Markup: `form.hs-filter[method="get"]` > `details.hs-filter__panel[data-hs-filter]` > `summary.hs-filter__summary` + `div.hs-filter__fields`.
- The summary reads `Filters`, then `span.hs-filter__count` with the number of active filters. Leave the count out when no filter is set.
- Each field is `div.hs-field.hs-filter__field` with `label.hs-label` and `select.hs-control`. The first option is the empty value: "Any type", "Anyone".
- The last item in the fields is `div.hs-filter__actions`: `button.hs-button.hs-button--navy.hs-button--sm` "Apply filters", then `a.hs-form-link.hs-filter__clear` "Clear all" to the clean URL. Print Clear all only when a filter is set. It shows only while the panel is a drawer (under 48rem, or without `filter.js`): `filter.js` adds `is-inline` to the open panel on wide screens and `filter.css` hides the link there, because the [Results bar](/components/results-bar/) carries Clear all.
- Two to four selects. A dimension with more than about 12 values needs a curated map in code, not every tag.
- Option labels are visitor words ("Graduate", "Study abroad"), never internal tags or school names.
- The form keeps the keyword (`q`) as a hidden field, so a filter change does not drop the search.
- Filtering runs on the server: the URL holds the state and every option is a real link target. A short list (about 40 items) may filter in the page on the same markup.
- Load `filter.js` on the page. The sort select in the [Results bar](/components/results-bar/) joins this form with `form="FORM-ID"`.

## Accessibility

- Each select has a visible `label for`. The summary is the native disclosure button, 48px tall.
- The count is part of the summary text, so a screen reader hears "Filters 2".
- Without `filter.js` the summary stays visible at every width, so the panel always opens; the Apply button submits.
- Before an auto-submit, `filter.js` disables every empty field of the form and of its joined controls, so the URL holds set filters only (`?for=undergraduate&sort=name`). The no-JS Apply button still submits all fields.
- Auto-submit on change reloads the page with the new list; the [Results bar](/components/results-bar/) count, a `role="status"` line, states the new total.
- The chevron turns with a 200ms transition; reduced motion removes it.
- Summary navy 12.4:1, count white on navy 12.4:1, labels ink 13.0:1 on white.

## Variants

### Filter panel

Narrow the window under 768px to see the closed disclosure. Needs `filter.js`.

```html
<form class="hs-filter" id="catalog-filter" method="get" action="/scholarship-directory">
  <details class="hs-filter__panel" data-hs-filter>
    <summary class="hs-filter__summary">Filters</summary>
    <div class="hs-filter__fields">
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f-for">Who it&rsquo;s for</label>
        <select class="hs-control" id="f-for" name="for">
          <option value="">Anyone</option>
          <option value="undergraduate">Undergraduate</option>
          <option value="graduate">Graduate</option>
          <option value="alumni">Alumni</option>
          <option value="international">International</option>
          <option value="multicultural">Multicultural</option>
        </select>
      </div>
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f-type">Type</label>
        <select class="hs-control" id="f-type" name="type">
          <option value="">Any type</option>
          <option value="scholarship">Scholarship</option>
          <option value="grant">Grant</option>
          <option value="fellowship">Fellowship</option>
          <option value="internship">Internship</option>
          <option value="study-abroad">Study abroad</option>
        </select>
      </div>
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f-by">Offered by</label>
        <select class="hs-control" id="f-by" name="by">
          <option value="">Any provider</option>
          <option value="honor-society">Honor Society&reg;</option>
          <option value="other">Other providers</option>
        </select>
      </div>
      <div class="hs-filter__actions">
        <button class="hs-button hs-button--navy hs-button--sm" type="submit">Apply filters</button>
      </div>
    </div>
  </details>
</form>
```

### With active filters

Two filters set: the summary shows the count, the selects keep their values, and Clear all returns to the clean URL.

```html
<form class="hs-filter" id="catalog-filter-2" method="get" action="/scholarship-directory">
  <input type="hidden" name="q" value="leadership">
  <details class="hs-filter__panel" data-hs-filter>
    <summary class="hs-filter__summary">Filters <span class="hs-filter__count">2</span></summary>
    <div class="hs-filter__fields">
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f2-for">Who it&rsquo;s for</label>
        <select class="hs-control" id="f2-for" name="for">
          <option value="">Anyone</option>
          <option value="undergraduate">Undergraduate</option>
          <option value="graduate" selected>Graduate</option>
        </select>
      </div>
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f2-type">Type</label>
        <select class="hs-control" id="f2-type" name="type">
          <option value="">Any type</option>
          <option value="scholarship">Scholarship</option>
          <option value="fellowship" selected>Fellowship</option>
        </select>
      </div>
      <div class="hs-field hs-filter__field">
        <label class="hs-label" for="f2-by">Offered by</label>
        <select class="hs-control" id="f2-by" name="by">
          <option value="">Any provider</option>
          <option value="honor-society">Honor Society&reg;</option>
          <option value="other">Other providers</option>
        </select>
      </div>
      <div class="hs-filter__actions">
        <button class="hs-button hs-button--navy hs-button--sm" type="submit">Apply filters</button>
        <a class="hs-form-link hs-filter__clear" href="/scholarship-directory">Clear all</a>
      </div>
    </div>
  </details>
</form>
```
