# SearchField

> Field-size search on the grey frosted recess: clear button, loading state and an optional key hint.

Category: Forms. Docs page: https://rime.mjsons.net/components/search-field

## Installation

```bash
npx shadcn@latest add KezLahd/rime/search-field
```

SearchField is the search box on a page (not in a filter row, where FilterSearch lives). It lifts toward white while focused, shows a clear button once there is a query, and can show a shortcut hint ("/") that focuses it from anywhere.

## Usage

```tsx
import { SearchField } from "@/components/ui";

<SearchField aria-label="Search invoices" value={q} onValueChange={setQ} placeholder="Customer or invoice number" />
```

## Props: SearchField

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `string` |  |  |
| `onValueChange` (required) | `(value: string) => void` |  |  |
| `size` | `"sm" \| "md"` | `"md"` | md sits level with TextInput and Select; sm with the 36px controls. |
| `loading` | `boolean` | `false` | Results are being fetched. The magnifier turns into a spinner and the field reports busy. |
| `loadingLabel` | `string` | `"Searching…"` | Announced while loading. |
| `shortcut` | `string` |  | A letter that focuses the field with Ctrl/⌘. Shows as a key badge while the field is empty and unfocused. Use once per page. |
| `onSearch` | `(value: string) => void` |  | Enter runs this (and never submits an enclosing form). Omit for live filtering. |
| `invalid` | `boolean` |  |  |
| `boxClassName` | `string` |  | Class for the outer box rather than the <input>. |

Also accepts the native attributes of: AriaAttributes, DOMAttributes, HTMLAttributes, InputHTMLAttributes.

## Examples

### Default, loading

```tsx
<SearchField value={q} onValueChange={setQ} loading={pending} shortcut="/" aria-label="Search" />
```

## Theme tokens

Per-component tokens. Undeclared by default: the default below is the fallback the CSS uses. Set any of them at :root or on a wrapper to restyle every instance.

| Token | Default |
| --- | --- |
| `--field-focus-border` | `rgba(var(--rgb-brand), 0.7)` |
| `--field-focus-ring` | `var(--focus-halo)` |
| `--field-focus-ring-invalid` | `0 0 0 4px rgba(var(--rgb-danger), 0.14)` |
| `--input-border-width` | `var(--border-w-field)` |
| `--input-font-size` | `var(--text-input)` |
| `--input-height-md` | `var(--field-h-md)` |
| `--input-height-sm` | `var(--field-h-sm)` |
| `--input-radius` | `var(--r-md)` / `var(--r-sm)` |

System tokens it reads: `--blur-recess`, `--border-w-field`, `--danger-base`, `--density`, `--dur`, `--ease`, `--field-h-md`, `--field-h-sm`, `--focus-halo`, `--focus-ring`, `--font-body`, `--ink-body`, `--ink-brand`, `--ink-disabled`, `--ink-muted`, `--r-2xs`, `--r-md`, `--r-sm`, `--r-xs`, `--recess-fill`, `--recess-fill-hover`, `--rgb-brand`, `--rgb-brand-deep`, `--rgb-danger`, `--rgb-white`, `--shadow-recess`, `--text-body`, `--text-input`, `--text-micro`, `--wash-active`, `--wash-strong`.

## Accessibility

- Renders input type="search". Esc clears the query and stops there, so an enclosing modal stays open; a second Esc on an empty field bubbles. The clear button returns focus to the input.
- Give it an aria-label or a Field label.

## Do

- Debounce the query yourself; the field reports every keystroke.

## Don't

- Don't use it inside a FilterBar; use FilterSearch there so heights match.
