Field-size search on the grey frosted recess: clear button, loading state and an optional key hint.
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.
Installation
npx shadcn@latest add KezLahd/rime/search-fieldAlso installs: base, field, icon, spinner.
Usage
import { SearchField } from "@/components/ui";
<SearchField aria-label="Search invoices" value={q} onValueChange={setQ} placeholder="Customer or invoice number" />Examples
Default, loading
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
SearchField
| Prop | Type | Default | Description |
|---|---|---|---|
| valueRequired | string | ||
| onValueChangeRequired | (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.
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
| Token | Default | Live value |
|---|---|---|
| --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
| Token | Default | Live value |
|---|---|---|
| --blur-recess | tokens.css | … |
| --border-w-field | tokens.css | … |
| --danger-base | tokens.css | … |
| --density | tokens.css | … |
| --dur | tokens.css | … |
| --ease | tokens.css | … |
| --field-h-md | tokens.css | … |
| --field-h-sm | tokens.css | … |
| --focus-halo | tokens.css | … |
| --focus-ring | tokens.css | … |
| --font-body | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-muted | tokens.css | … |
| --r-2xs | tokens.css | … |
| --r-md | tokens.css | … |
| --r-sm | tokens.css | … |
| --r-xs | tokens.css | … |
| --recess-fill | tokens.css | … |
| --recess-fill-hover | tokens.css | … |
| --rgb-brand | tokens.css | … |
| --rgb-brand-deep | tokens.css | … |
| --rgb-danger | tokens.css | … |
| --rgb-white | tokens.css | … |
| --shadow-recess | tokens.css | … |
| --text-body | tokens.css | … |
| --text-input | tokens.css | … |
| --text-micro | tokens.css | … |
| --wash-active | tokens.css | … |
| --wash-strong | tokens.css | … |
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 and don't
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.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open search-field.md