# @spark-web/text-input — AI Context

## What this is

A styled single-line text input. Must always be wrapped in a `Field` from
`@spark-web/field` — `Field` provides the label, description, message, and all
accessibility wiring. Supports leading/trailing adornments (icons, buttons,
secondary selects) via `InputAdornment`.

## What this is NOT

- Not a textarea — for multi-line input, use a different component
- Not usable without `Field` — always wrap in `<Field label="…">`
- Not responsible for its own label or validation message

## Exports

- `TextInput` — the input element
- `InputAdornment` — wrapper for icons/elements placed inside the input

## TextInput props

| Prop               | Type                                                                                  | Notes                                                                |
| ------------------ | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `type`             | `'text' \| 'password' \| 'email' \| 'search' \| 'number' \| 'tel' \| 'url'`           | Defaults to `'text'`                                                 |
| `placeholder`      | `string`                                                                              | —                                                                    |
| `value`            | `string`                                                                              | Controlled value                                                     |
| `onChange`         | `ChangeEventHandler<HTMLInputElement>`                                                | —                                                                    |
| `name`             | `string`                                                                              | —                                                                    |
| `autoComplete`     | `string`                                                                              | —                                                                    |
| `onBlur`           | `FocusEventHandler<HTMLInputElement>`                                                 | —                                                                    |
| `onFocus`          | `FocusEventHandler<HTMLInputElement>`                                                 | —                                                                    |
| `onInput`          | `FormEventHandler<HTMLInputElement>`                                                  | —                                                                    |
| `inputMode`        | `'none' \| 'text' \| 'tel' \| 'url' \| 'email' \| 'numeric' \| 'decimal' \| 'search'` | Virtual keyboard hint for mobile                                     |
| `required`         | `boolean`                                                                             | —                                                                    |
| `children`         | `InputAdornment` elements                                                             | Adornments; must be `InputAdornment` — anything else is filtered out |
| `overflowStrategy` | `'truncate' \| 'nowrap'`                                                              | Defaults to `'truncate'`                                             |
| `data`             | `DataAttributeMap`                                                                    | Test/analytics attributes                                            |

`disabled` and `readOnly` are injected via `Field` context — do not pass them
directly to `TextInput`.

## InputAdornment props

| Prop         | Type               | Notes                                                                        |
| ------------ | ------------------ | ---------------------------------------------------------------------------- |
| `placement`  | `'start' \| 'end'` | required — where to place the adornment                                      |
| `children`   | `ReactElement`     | The adornment element (icon, button, etc.)                                   |
| `raw`        | `boolean`          | Opt out of automatic alignment/spacing wrapper                               |
| `fieldLabel` | `string`           | Override the parent field label for an inner input (e.g. a select adornment) |

Only one adornment per placement is allowed.

## Common patterns

### Search field with icon and clear button

```tsx
<Field label="Search users" labelVisibility="hidden">
  <TextInput
    placeholder="Search users"
    value={search}
    onChange={e => setSearch(e.target.value)}
  >
    <InputAdornment placement="start">
      <SearchIcon size="xsmall" tone="placeholder" />
    </InputAdornment>
    {search && (
      <InputAdornment placement="end">
        <Button
          label="Clear search"
          prominence="none"
          tone="neutral"
          onClick={() => setSearch('')}
        >
          <XIcon />
        </Button>
      </InputAdornment>
    )}
  </TextInput>
</Field>
```

### Plain search (icon only, no clear)

```tsx
<Field label="Search" labelVisibility="hidden">
  <TextInput
    placeholder="Search…"
    value={q}
    onChange={e => setQ(e.target.value)}
  >
    <InputAdornment placement="start">
      <SearchIcon size="xsmall" tone="placeholder" />
    </InputAdornment>
  </TextInput>
</Field>
```

### With react-hook-form (via ui-components)

Prefer `TextInputField` from `@brighte/ui-components` when used with
`react-hook-form`. Pass `placeholder` directly, and use `FieldProps` to pass
`description` (renders as muted hint text below the label):

```tsx
<TextInputField
  control={control}
  name="subject"
  label="Subject"
  placeholder="Type here..."
  FieldProps={{ description: 'Enter a brief summary of the issue' }}
/>
```

## Do NOTs

- NEVER use `TextInput` without a parent `Field`
- NEVER pass non-`InputAdornment` elements as children — they will not render
- NEVER put more than one `InputAdornment` at the same placement
- NEVER manage `disabled` state directly on `TextInput` — set it on `Field`
- NEVER use a raw `<input>` element — always use `TextInput`
