input
An input is a box for one line of text that people type, such as a name, a host or a search term. Put it in a field to give it a label, a hint and an error message.
when to use
- For one line of text that people type: a name, a host, an ID, a number, a search term.
- Inside a field, which gives it a visible label and ties its hint and its error to it.
- With an icon or a prompt at its start, when that says what kind of value goes in, such as a search icon or a “>” prompt. Or with a unit at its end, such as “ms” or “GiB”.
when to use something else
- For more than one line. Use a textarea. An input can't wrap, and it hides whatever scrolls out of view.
- To pick from a known list. Use a select, so people can't type a value that doesn't exist.
- For on or off. Use a checkbox in a form that is saved, or a switch when the change applies at once.
- To filter a list or a table. Use the filter bar, which lays out the search, the filters and their reset.
- To lay out a whole form or a settings page. Start from forms and settings.
anatomy
- Box. A square with a thin border, in one of three heights. It fills the width of its container.
- Leading icon or prompt (optional). An icon or a prompt before the text, in a dim colour.
- Text. What people type, in monospace. While the box is empty, a placeholder can show in the same dim colour.
- Trailing unit or icon (optional). A unit or an icon after the text, in the same dim colour and with the same spacing.
variants and states
sizes
Inputs come in three heights, the same three as buttons. Give an input the height of the buttons beside it, so the row lines up. The small one suits toolbars, rows of filters and table cells, the medium one (the default) is for forms, and the large one is for a search or a command prompt that stands on its own.
icons, prompts and units
An icon, a prompt or a unit can sit inside the box, before or after the text.
states
| state | looks |
|---|---|
| Default | A thin grey border |
| Focused | A thicker blue border, whenever it has focus: after a click or from the keyboard |
| Invalid | A red border, which stays red while the box has focus |
| Disabled | The whole box dimmed. People can't focus it or change it, and its value isn't sent with the form |
| Read-only | Just like the default. People can focus it, select its text and send it with the form, but they can't change it |
behaviour
- The input is one tab stop. People type, move the text cursor and select text as in any text field, and enter can send the form the input is in.
Today: only the part of the box that holds the text takes clicks. A click on the space at its sides, or on an icon or a unit, doesn't put focus in the field.
The full keyboard map is on the input's React page.
accessibility
- Give it a visible label. A field shows one, and ties the hint or the error to the input, so a screen reader reads them with it. A placeholder is not a label: it disappears with the first keystroke.
- Where a visible label doesn't fit, such as a search box in a toolbar, give the input an accessible name: a label that is hidden on screen but read out. Add a search icon at its start as well, so that sighted people see what the box is for.
- Say what is wrong, in words. An invalid input turns its border red. The colour alone is a weak cue: its contrast with the normal border is only about 1.6 to 1. So always say in words what is wrong: a field's error message does that.
- Put the unit in the label too. Icons and units inside the box are not part of the input's name. An icon hides itself from screen readers, and a unit such as “ms” isn't read out when the field gets focus. So write it into the label as well: “Timeout (ms)”.
- Focus shows in the border, which turns blue and thicker. Buttons and checkboxes show an outline instead. Interaction explains focus.
- Disabled or read-only. A disabled field can't take focus, so people who use a screen reader may never find it. When people need to read or copy the value, make the field read-only instead.
Today:
- A read-only field looks just like one people can edit, so say near it why it can't change.
- There are no styles for forced colours, the high-contrast setting some operating systems offer. Forced colours replace the border colours and remove the inner shadow that makes the focused border thicker, so a field that has focus can look as if it hasn't.
content
- Label. A noun for the value, in sentence case, with no colon: “Host”, “Release name”, “Timeout (ms)”.
- Placeholder. An example of a valid value, or nothing: “api.example.com”. Don't use it for the label or for an instruction: both vanish with the first keystroke.
- Hint. The format or the limit, in one short line: “Lowercase letters, digits and hyphens”.
- Error. What is wrong and how to fix it: “Use 63 characters or fewer”, not “Invalid input”.
- Unit. In the label, and also at the end of the box when people need to see it while they type.
do and don't
related
- Field: the label, hint and error around the input.
- Textarea: the same field, for several lines.
- Select: one value from a known list.
- Filter bar: search and filters above a list or a table.
specs
| size | in code | height | side padding | text | icon or unit to text |
|---|---|---|---|---|---|
| Small | inputSize="sm" |
28px | 8px | 13px | 8px |
| Medium | inputSize="md" (the default) |
34px | 12px | 13px | 8px |
| Large | inputSize="lg" |
42px | 16px | 14px | 12px |
| state | in code | looks |
|---|---|---|
| Default | (none) | A 1px --border-control border on --bg-2 |
| Focused | Any focus inside the box (:focus-within), by mouse or keyboard |
A --cloud-blue border and a 1px inset shadow in the same colour: 2px in all |
| Invalid | invalid, which also sets aria-invalid="true"; a field's error sets it for you |
A --state-error border; while focused, the inset shadow is --state-error too |
| Disabled | disabled |
The whole box at 50% opacity, a not-allowed cursor over the text. Can't be focused, edited or submitted |
| Read-only | readOnly |
Like the default. Can be focused, selected and submitted, not edited |
- Box: square (
border-radius: 0), always the full width of its container, so set the width on the container.classNamelands on the box. - Text: a native
<input>, of the browser's default type,text, unless you pass anothertype. JetBrains Mono,--fg, line-height 1.4. The placeholder is--fg-dim. Every other attribute (value,onChange,type,name,placeholder,aria-*…) and therefland on the native input. - Affixes:
leadingandtrailingtake any element: anIconsuch as the search icon, a>prompt, or a unit such asms. They sit inside the box but outside the input, in--fg-dim, and are not part of its name. - No visible label: name the input with
aria-label. - Focus: shown by the border, not by the
--focus-ringoutline that buttons and checkboxes use. - Contrast: the
--state-errorborder against the normal--border-controlborder is about 1.6:1. - Motion: the border and the shadow change in 120ms
ease.