Browse all of Kata

Input and Field

Source docs/design/components/input.mdMarkdown

On this page

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.

Light

Empty · placeholder in text.tertiary

Test notesOptional
Add a note…

Filled

Event name
Summer camp 2027

Focus · 2px border.focus

Event name
Summer camp 2027

Error · helper survives

Price
45..00

Per person, in dollars.

Enter a price like 45.00.

Multiline · 88 floor, grows

DescriptionOptional
What should people bring?

Dark

Empty · placeholder in text.tertiary

Test notesOptional
Add a note…

Filled

Event name
Summer camp 2027

Focus · 2px border.focus

Event name
Summer camp 2027

Error · helper survives

Price
45..00

Per person, in dollars.

Enter a price like 45.00.

Multiline · 88 floor, grows

DescriptionOptional
What should people bring?

Source: Input.tsx and Field.tsx. The form these sit in: form.

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. Not for search — that is SearchField, 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.

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

The event

Event name
Summer camp 2027

Do

Inputs sit on the page, grouped by space and an eyebrow.

Event name
Summer camp 2027

Don’t

An input inside a white card is white on white.

WhereOptional
Main dojo

Do

Mark what is optional. Everything else is assumed required.

Where *
Main dojo

Don’t

An asterisk on every field marks nothing.

Code

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>