The bordered text-field primitives. TextInput is the base: a wrapper that
carries all chrome (border, focus, invalid state) around a chromeless input,
sized to the shared toolbar control heights. SearchInput is TextInput
preconfigured for filtering: search-icon prefix, a clear button that appears
with content, and an optional shortcut hint while empty.
<script lang="ts">
import { SearchInput, TextInput } from "@kenn-io/kit-ui";
let name = $state("");
let query = $state("");
</script>
<TextInput bind:value={name} placeholder="Display name" />
<SearchInput bind:value={query} keys={["⌘", "K"]} block />| Prop | Type | Default | Notes |
|---|---|---|---|
value |
string (bindable) |
"" |
|
type |
"text" | "search" | "email" | "url" | "password" | "tel" |
"text" |
Text-like types only; date/checkbox/etc. keep native chrome elsewhere |
placeholder |
string |
— | |
size |
"sm" | "md" | "lg" |
"md" |
24px / 28px toolbar controls; 36px-minimum lg form control |
invalid |
boolean |
false |
Red border + aria-invalid |
disabled / readonly / required |
boolean |
false |
Forwarded to the native input |
block |
boolean |
false |
Stretch to container width (default is a 180px inline field) |
id / name |
string |
— | For <label for> / forms |
ariaLabel |
string |
— | Required in spirit when no <label> is associated |
autofocus |
boolean |
false |
Focus on mount |
autocomplete |
string |
— | |
role / ariaExpanded / ariaControls / ariaActivedescendant / ariaAutocomplete |
combobox wiring | — | For fields that drive a listbox (CommandPalette): role="combobox" plus pointers at the list and highlighted option |
oninput / onchange |
(value: string) => void |
— | Called with the new value |
onkeydown |
(event: KeyboardEvent) => void |
— | |
onblur |
() => void |
— | Called when the control loses focus |
ariaDescribedby |
string |
— | Connects supporting or error text to the native input |
prefix / suffix |
Snippet |
— | Adornments inside the border (icon, unit, kbd, button). Interactive suffix actions must handle disabled themselves — the wrapper only dims |
inputEl |
HTMLInputElement (bindable) |
— | The underlying input, for focus management |
class |
string |
"" |
Focus renders as an --accent-blue border and visible focus ring, while invalid
renders as --accent-red; both follow the FindBar card convention. Browsers
with :has() enhance the ring onto the wrapper, while older engines keep an
input-level or native outline fallback. The native webkit search-cancel button
is suppressed — the wrapper owns the clear affordance.
FormField composes the lg size with a persistent label and accessible
error message. Large inputs and buttons share a 36px minimum height and grow
with the user's type size. Use TextInput directly when the surrounding form
owns those elements.
Forwarded from TextInput: value, placeholder, size, invalid,
disabled, readonly, block, autofocus, id, name, ariaLabel
(default "Search"), the combobox wiring props (role, ariaExpanded,
ariaControls, ariaActivedescendant, ariaAutocomplete), oninput,
onchange, onkeydown. (type, prefix, and suffix are owned by
SearchInput.) Plus:
| Prop | Type | Default | Notes |
|---|---|---|---|
keys |
string[] |
— | Shortcut hint (KbdBadge) shown while empty, e.g. ["⌘", "K"] |
onclear |
() => void |
— | After the clear button or Escape empties the field |
clearLabel |
string |
"Clear search" |
Clear button label |
Clearing behavior: the clear button only renders with content on an
enabled, non-readonly field (disabled fields can't be mutated through it),
and clearing — by click or Escape — returns focus to the input so keyboard
users aren't dropped when the button unmounts. Escape stops propagation
only when it actually clears — a search-in-modal clears before the
modal's Escape-to-close fires and a second Escape reaches the modal, while
a readonly/disabled/empty field passes Escape straight through. inputEl
is bindable for app shortcut handlers that focus the field.
FindBar,Typeahead,SelectDropdownkeep their own inputs — they're comboboxes/cards with their own ARIA wiring and chrome ownership.DateRangePicker's custom tab uses nativetype="date"inputs (native pickers beat styled text fields for dates).FilterDropdown's search box usesSearchInputinternally (size="sm" block).