# Filter Chip

Shows one applied filter, its field and value, with an optional button to remove it.

```tsx
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

function FilterChipDemo() {
  return <FilterChip label="Severity" value="Critical" onRemove={() => {}} />
}
```

## Usage

```tsx
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

export function SeverityFilter({ onRemove }: { onRemove: () => void }) {
  return <FilterChip label="Severity" value="Critical" onRemove={onRemove} />
}
```

Use filter chips above a table or list to show which filters are applied, one chip per field.
`label` is the field and renders in secondary text followed by a colon; `value` renders in
medium weight. Pass `onRemove` and the chip gets a remove button that calls it.

The chip is a `<span>` and is not clickable itself. To pick filters, use a
[Select](/components/select), a [Dropdown Menu](/components/dropdown-menu) or a
[Combobox](/components/combobox); to switch a quick filter on and off, use a
[Toggle](/components/toggle). For a status or a category that is not a filter, use a
[Badge](/components/badge).

When a field has several values, join them in one chip ("Critical, High") rather than
rendering a chip per value.

## Examples

### Variants

```tsx
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

function FilterChipVariants() {
  return (
    <>
      <FilterChip label="Status" value="Open" />
      <FilterChip variant="active" label="Severity" value="High" />
      <FilterChip variant="ghost" label="Tenant" value="Acme Corp" />
    </>
  )
}
```

| Variant   | Use for                                                                 |
| --------- | ----------------------------------------------------------------------- |
| `default` | Applied filters in a filter bar.                                        |
| `active`  | A filter that differs from the view's default, to draw attention to it. |
| `ghost`   | Filters inherited from context, such as the current tenant.             |

### Sizes

`sm` (20px) fits inside table cells and card headers, `default` (24px) is for filter bars, and
`lg` (28px) sits next to 28px buttons. `default` and `lg` use the 12.5px body text, `sm` the
10px caption size; the remove icon is 12px at `sm` and 14px otherwise.

```tsx
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

function FilterChipSizes() {
  return (
    <>
      <FilterChip
        size="sm"
        label="Source"
        value="CT logs"
        onRemove={() => {}}
      />
      <FilterChip label="Source" value="CT logs" onRemove={() => {}} />
      <FilterChip
        size="lg"
        label="Source"
        value="CT logs"
        onRemove={() => {}}
      />
    </>
  )
}
```

### Filter bar

The applied filters of a list, each removable, with a link to clear them all. Keep the chips in
the same order as the filter controls.

```tsx
import * as React from "react"
import { Button } from "@zenoxai/ui/components/button"
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

function FilterChipFilterBar() {
  const [filters, setFilters] = React.useState([
    { id: "severity", label: "Severity", value: "Critical, High" },
    { id: "status", label: "Status", value: "Open" },
    { id: "asset", label: "Asset type", value: "Domain" },
    { id: "age", label: "First seen", value: "Last 7 days" },
  ])

  if (filters.length === 0) {
    return (
      <p className="text-sm text-text-muted">No filters. Showing all alerts.</p>
    )
  }

  return (
    <div className="flex w-full max-w-lg flex-wrap items-center gap-2">
      {filters.map((filter) => (
        <FilterChip
          key={filter.id}
          label={filter.label}
          value={filter.value}
          onRemove={() =>
            setFilters((current) => current.filter((f) => f.id !== filter.id))
          }
        />
      ))}
      <Button variant="link" size="xs" onClick={() => setFilters([])}>
        Clear all
      </Button>
    </div>
  )
}
```

### Custom content

Children render after the value, before the remove button. Use them for an icon, a count of
hidden values, or a chip with no field name. Size icons yourself; the chip does not.

```tsx
import { GlobeIcon } from "lucide-react"
import { FilterChip } from "@zenoxai/ui/components/filter-chip"

function FilterChipCustomContent() {
  return (
    <>
      <FilterChip
        variant="active"
        onRemove={() => {}}
        removeAriaLabel="Remove domain filter"
      >
        <GlobeIcon className="size-3" />
        acme-corp.com
      </FilterChip>
      <FilterChip
        label="Tags"
        value="phishing"
        removeAriaLabel="Remove tags filter"
        onRemove={() => {}}
      >
        <span className="text-text-muted">+2</span>
      </FilterChip>
    </>
  )
}
```

## Accessibility

- The remove button is labelled "Remove filter `label`" when `label` is a string, and "Remove
  filter" otherwise. Set `removeAriaLabel` whenever `label` is not a string, so every button on
  the page has a distinct name.
- Removing a chip removes the focused button with it. Move focus somewhere predictable after
  `onRemove`: the next chip's button, or the filter control when none are left.
- The remove button is 16px square, under the 24px target size of WCAG 2.2. It meets the
  spacing exception only while no other control sits within 4px of it, so keep at least `gap-2`
  between chips and the controls around them.

## API reference

`import { ... } from "@zenoxai/ui/components/filter-chip"`

### FilterChip

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| label | `ReactNode` |  | Field/dimension being filtered (e.g. "Severity"). Renders before the colon. |
| onRemove | `(() => void)` |  | When present, renders an inline X button that calls this on click. |
| removeAriaLabel | `string` |  | Accessible name of the remove button. Defaults to the English "Remove filter <label>" when `label` is a string, else "Remove filter"; pass a translated one. |
| size | `"default" \| "sm" \| "lg" \| null` |  | Height: `sm` 20px, `default` 24px, `lg` 28px. Text is the 12.5px body size, 10px at `sm`; the remove icon is 12px at `sm`, 14px otherwise. |
| value | `ReactNode` |  | Filter value (e.g. "Critical"). Renders bold after the colon. |
| variant | `"default" \| "ghost" \| "active" \| null` |  | `default` is outlined; `active` is tinted with the primary color, to mark the chip in focus; `ghost` has a muted fill and no border. |

