<!-- https://getkonjo.com/design/components/input · source: docs/design/components/input.md -->

# Input and Field

`Input` is the typed field. `Field` is the label, helper and error around any control — an
input, a select trigger, a row of chips. Every form is made of the two.

```kata-specimen
input-states
```

> Source: [`Input.tsx`](../../../src/components/ui/Input.tsx) and
> [`Field.tsx`](../../../src/components/ui/Field.tsx). The form these sit in:
> [form](../patterns/form.md).

## When to use it

- `Input` for anything typed: a name, a price, a note. `multiline` for long text,
  `multiline="grow"` for a note that is usually one line and occasionally two.
- `Field` around every control in a form, so the rhythm and the error contract come from one
  place.

Not for choosing from a list — that is a [select trigger](select-trigger.md). Not for search —
that is [SearchField](search-field.md), which is this input with two slots filled. Never inside
a white card: inputs sit on the page.

## Anatomy

| Part | Token |
|---|---|
| Field label | `typography.label` 13/700, sentence case, `text.primary`; "Optional" in `caption` `text.secondary` on the right |
| Label to control | `spacing.s6` |
| Control fill | `surface.card` |
| Control edge | 1px `border.control`, `radius.input` 8 |
| Control padding | `spacing.s12` |
| Height floor | `controlHeight.control` 44; `multiline` 88 (`controlSize.multilineMinHeight`) and grows |
| Value | `typography.body` 15/400, `text.primary` |
| Placeholder | `text.tertiary` on `surface.card` — legal only because that is the control's fill |
| Helper and error | `typography.caption`, `spacing.s6` below the control; helper `text.secondary`, error `feedback.errorText` |
| Field to next field | `spacing.s16`; to the next section `spacing.section` 24 |

## States

- **Empty** shows the placeholder; **filled** shows the value.
- **Focus**: the edge becomes 2px `border.focus`, and the padding gives back the extra pixel so
  nothing moves. Focus wins over the error border — you need to know where you are before you
  need to know what is wrong.
- **Error**: the edge swaps to `feedback.errorBorder` (still 1px) and the message appears below.
  The helper stays: it explains the field, the error explains the failure.
- **Validation timing**: on blur, never on keystroke.
- **In flight**: the form sets `editable={false}` on every input while it submits.
- **Disabled**: none. A field you cannot change is a fact, shown as text or a static
  [Row](row.md).

## Content

- The label names the thing ("Event name"), not an instruction ("Enter the event name").
- Mark what is **optional**. Required is the default and gets no asterisk.
- Placeholders show an example or name the content ("Add a note…"); they never replace the label.
- Errors say what to do in plain words: "Enter a price like 45.00."
- Return moves to the next field. It never submits a form.

## Accessibility

- The field's label does not reach a React Native control on its own. Spread
  `fieldControlProps({ label, value, helper, error })` onto the control: it sets `hasError` and an
  `accessibilityLabel` read as one sentence — label, value, helper, then "Error: …".
- The error text under the field is hidden from screen readers, because it is already in the
  control's label; announcing it twice is worse than once.
- `text.tertiary` on `surface.card` is 4.74:1 in light, which is why the placeholder may use it
  inside the control and never on the page.
- `border.control` on `surface.page` is 3.15:1 in light, so the control's edge is visible
  against the page it sits on.

## Do and don't

```kata-specimen
input-in-card
```

```kata-specimen
field-optional
```

## Code

```tsx
import { Field, Input, fieldControlProps } from '../../components/ui';

<Field label="Price" helper="Per person, in dollars." error={errors.price}>
  <Input
    value={price}
    onChangeText={setPrice}
    onBlur={validatePrice}
    keyboardType="decimal-pad"
    placeholder="45.00"
    testID="event-price"
    {...fieldControlProps({ label: 'Price', value: price, helper: 'Per person, in dollars.', error: errors.price })}
  />
</Field>
```
