Gesso
Forms

Input

Text input on the field surface, with a forced-colors-safe focus ring.

Input is a single-line text field for names, email addresses, search terms, and numbers. It renders a native <input> on bg-surface, fills the width of its container at 40px tall, and draws a destructive border under aria-invalid. Pair it with Field for a label, a description, and an error, and use Input group to put an icon or a short suffix inside it. Its corners follow the look's rounded, so it is an 8px rectangle in Washi and rounds to 16px in ICS, where buttons are pills.

Usage

import { Field, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input';

<Field>
  <FieldLabel htmlFor="email">Email</FieldLabel>
  <Input id="email" type="email" autoComplete="email" />
</Field>

Input takes every <input> attribute and adds no props of its own, so type, name, value, onChange, autoComplete, and inputMode work as they do on the element, and a ref reaches the <input>. Its border is the strong border at border-stroke width, which follows the look's weight, 1px in Washi and 2px in ICS, and its text is font-sans at the look's UI weight, with the placeholder in text-fg-subtle. With type="file", the browse button loses its border and background and reads as 14px medium text inside the field.

Invalid and disabled

Set aria-invalid when the value fails validation. The border turns border-destructive, the same as on Textarea, Select, and Checkbox, and a screen reader announces the field as invalid. Say what is wrong in a FieldError below it and list the error's id in aria-describedby, so the message is announced when it appears and read with the field after that.

<Field>
  <FieldLabel htmlFor="email">Email</FieldLabel>
  <Input
    id="email"
    type="email"
    aria-invalid={Boolean(error)}
    aria-describedby={error ? 'email-error' : undefined}
  />
  <FieldError id="email-error">{error}</FieldError>
</Field>

disabled drops the field to 64% opacity with a not-allowed cursor, and a Field around it dims its label to match. readOnly has no style of its own, so a read-only Input looks the same as one that can be edited.

Height and text size

Input is 40px tall, the height of a md Button and a Select trigger, and unlike Button it does not grow on a touch screen. Set another height with a class like h-9. Its text is 16px at every width, because iOS Safari zooms the page in when a field with smaller text takes focus, so on an iPhone the page stays put.

Accessibility

  • It renders a native <input>, so typing, selection, autofill, and form submission work as the browser provides them.
  • It has no name of its own. Name it with a FieldLabel whose htmlFor matches its id, or with aria-label when there is no visible label. A placeholder is not a label, since it disappears as soon as someone types. The placeholder is in text-fg-subtle, a hint the label does not depend on, so it does not read as a typed value. When it shows an example, start it with "e.g." or put the example in a FieldDescription.
  • Focus shows a 2px ring in the contrast ink, flush against the border, on :focus-visible. A text field matches :focus-visible on a click as well as from the keyboard, so the ring shows whenever the field has focus.
  • In forced colors the ring is dropped and a transparent 2px outline takes its place, which the system paints, so focus stays visible in Windows high contrast.
  • aria-invalid paints the border border-destructive and makes a screen reader announce the field as invalid. Forced colors repaints the border in a system color, so the FieldError message is the sign that holds up there.
  • disabled sets the native attribute, so the field leaves the tab order and assistive tech reports it as unavailable.
  • It is 40px tall and as wide as its container, and does not grow on a touch screen.
  • Nothing animates. The border and the ring change at once.

When to use something else

  • Textarea for text that runs past one line, like a comment or a description.
  • Input group for an icon or a short suffix inside the field, like a search glass or a domain.
  • Select to pick one of a few known values, and Combobox when the list is long enough to search.
  • Date picker for a date, picked from a calendar rather than typed.

Install

shadcn add @gesso/input

On this page