Inputs and fields

Inputs collect text, choices and codes, and Field gives each one its label, hint and error. Use them whenever people type or pick a value: Input, Select, DatePicker, PasswordInput, Textarea or DigitInput, inside a Field.

  • Beta
  • Accessibility
  • Figma Text box 2 5:878 / 5:1320, Inline Input 5:1255, Text Area 5:1282, Digit Input 5:1242
  • packages/ui/src/components/Field

Beta until the default border contrast (C7) is resolved, and design confirms the digit gap and text area resize handle (C13). Everything else on this page is implemented and tested.

Preview

Pick a control and change the controls to try every supported combination. The preview renders the real components and the code updates with it.

Students see this on the lesson card.

tsx
import { Input, Field } from '@nuton/ui'

<Field label="Lesson title" hint="Students see this on the lesson card.">
  <Input placeholder="Introduction to Design Principles" />
</Field>

Installation

Availability

@nuton/ui and @nuton/tokens are private workspace packages. They aren't published to a registry yet (D4: registry deferred; D12: distribution waits on the provenance review). Inside the Nuton monorepo, add them as workspace dependencies. The commands below apply once the packages are published.

1. Add the packages

bash
pnpm add @nuton/ui @nuton/tokens @remixicon/react

Peer dependencies: react and react-dom 18 or later. @remixicon/react supplies the icons you pass to leadingIcon, trailingIcon and hintIcon (Remix Icon is Nuton's icon set, D1).

2. Load tokens and component styles once

tsx
// app/layout.tsx (or your app's entry file)
import '@nuton/tokens/tokens.css' // --nu-* design tokens
import '@nuton/ui/styles.css'     // component styles (uses the tokens)

3. Point the font tokens at your loaded fonts

Labels, values and hints use --nu-font-body (Inter). DigitInput boxes use Title/H5, which reads --nu-font-header(Fraunces). Load both fonts and map the tokens to them. All controls are native elements (D5), so there's no provider to add: Field shares its ids and state with the control inside it.

tsx
import { Fraunces, Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
const fraunces = Fraunces({ subsets: ['latin'], variable: '--font-fraunces' })

<body
  className={`${inter.variable} ${fraunces.variable}`}
  style={{
    '--nu-font-body': 'var(--font-inter), var(--nu-font-fallback-body)',
    '--nu-font-header': 'var(--font-fraunces), var(--nu-font-fallback-header)',
  }}
>

Usage

tsx
import { Field, Input } from '@nuton/ui'

export function SpaceName({ error }: { error?: string }) {
  return (
    <Field label="Space name" hint="For example, Biology 101." error={error} required>
      <Input name="spaceName" autoComplete="off" />
    </Field>
  )
}
  • Put every control inside a Field. It renders a real <label for> and links the hint and error with aria-describedby, so you don't wire ids yourself.
  • Pass error to the Field, not invalid to the control. The control picks up aria-invalid and the red border from it.
  • required and disabled on the Field reach the control too. A prop set on the control itself wins.
  • Set type, inputMode and autoComplete so phones show the right keyboard and browsers can fill the value.
  • Placeholders are examples (“Biology 101”), never the label or an instruction.

Variants and sizes

Five controls share one box style and one Field. Input covers most Text box 2 types through its slots; Select, PasswordInput, Textarea and DigitInput cover the rest.

Input

Two sizes for the box appearance. Use one size per form.

SizeHeightRadiusInline paddingLeading iconTrailing iconIcon gapType
lg (default)48161624208Paragraph/Small (14/24)
sm3681220208
appearance="inline"3681220208

Heights are measured in the browser and match Figma. Insets were rebalanced at the owner's request (P1); see Spacing below.

Input slots

Slots cover the Figma types without separate components. leadingIcon and trailingIcon are decorative; leadingAddon is a separate segment with a divider; trailingAction holds one labelled button.

Brand badges and flags (C15)

Figma's Card type shows a card-brand badge and Phone Number shows a flag. No brand or flag assets ship in @nuton/ui(provenance and trademark). Your app passes them through trailingIcon or leadingAddon.

Inline input

appearance="inline" is borderless until hover or focus, for editing text in place such as a lesson title. It's always 36px; size is ignored. Clear the title with the button, then type a new one.

Select

A native <select> (D5) styled as a text box, with the same sizes. placeholder adds a hidden first option that shows until people choose.

The chevron is 20px and sits at the input padding (16 / 12). Its colour follows Figma: Soft-400 while empty, Sub-600 on hover and focus, Strong-950 in error, Disabled-500 when disabled. The legacy TextBox Country type is a Select with a flag leadingIcon (C14).

Date picker

DatePicker is the Text box 2 Date type made real: people either type the date or choose it from a calendar in a popover. Typing follows the order of the locale (dd/mm/yyyy by default, mm/dd/yyyy for en-US); a half-typed date waits, and a date that does not exist or is outside min and max turns the box invalid when people leave it. DateRangePicker chooses a start and an end day from one calendar, and Calendar is the month grid on its own.

Type it as dd/mm/yyyy or choose it from the calendar.

No date chosen.

Weekdays from today onwards.

No date chosen.

No dates chosen.

November 2026

MonTueWedThuFriSatSun

No day chosen.

The Figma calendar could not be read (C131)

The Figma library holds a “Date & Range Picker [1.1]”, but it lives in a separate library file that this project cannot open, so the calendar was built from the Nuton tokens and the Input and Menu styles instead. Cell size (40px, 44px on touch), the selected and range colours and the the month and year dropdowns are code decisions to confirm against that component.

Dates are local days with no time zone. Post them as yyyy-mm-dd (the name prop does this). Weeks start on Monday unless weekStartsOn says otherwise. The calendar shows one month; a range that spans months is chosen by moving between them.

Password input

An Input with a show and hide button. Select the eye button to reveal the password.

Use at least 8 characters.

Textarea

Multi-line text for questions, answers and notes. showCounter with maxLength adds the Figma counter. Type to see it update.

Your tutor sees your question.

Radius 12, padding 12 on every side (P1), Stroke/Soft-200 border and the regular x-small shadow. Four rows by default; the native resize handle replaces Figma's resize icon (C13). The counter uses Subheading/2X Small (11/12).

Digit input

One box per character for one-time codes. Four boxes by default; alphanumeric accepts letters too.

We sent a 4-digit code to your email.

Boxes are 80 × 80 with radius 10, set in Title/H5 (Fraunces 24/32). They shrink to fit narrow screens. The 12px gap between boxes isn't specified in Figma, which has a single-box component; design to confirm (C13).

Field

Field adds the label row (label, required asterisk, sublabel), the hint or error and an optional hint icon.

For example, Biology 101.

Enter an email address, like name@school.edu.

Matches Figma Label (4446:59279) and Hint Texts (4446:59288). Label: Label/Small (14/20) in a 24px row, 1px between label, asterisk and sublabel; the sublabel is Paragraph/Small (14/24) in Sub-600. Hint and error: Paragraph/X Small (12/16) after a 16px information-fill icon, which is shown by default (Soft-400, Error/Base on errors, Disabled-300 when disabled); pass hintIcon={null} to hide it. Label, control and hint are 4px apart.

Text box 2: every type, size and state

The full Figma Text box 2 set: seven types, two sizes and the static states. Hover and focus are interactive: point at or tab into any control. Choose a type to see it.

Large (48)

This hint helps people fill in the field.

This hint helps people fill in the field.

Check this value and try again.

This hint helps people fill in the field.

Small (36)

This hint helps people fill in the field.

This hint helps people fill in the field.

Check this value and try again.

This hint helps people fill in the field.

Spacing (P1)

Padding was rebalanced at the owner's request (P1, a product decision that deviates from Figma). Heights are unchanged (48 / 36, inline 36). The Figma correction is listed as FC-08 in docs/design-system/component-notes.md.

PartFigmaCode
Inline padding, Large / Small20 / 2016 / 12
Leading icon, Small2420
Icon ↔ text gap88 (unchanged)
Inline appearance padding1012
Select chevron inset20Follows the input padding (16 / 12)
Text area padding10 / 10 / 10 / 1212
Label ↔ control ↔ hint gap44 (back to Figma at the owner's request; P1 had 6)
Label row / sublabel line box2424 (back to Figma)

States and interactions

Figma defines six states for every text box: Placeholder, Hover, Focus, Filled, Error and Disabled. Hover and focus are live here; interact to see them.

PlaceholderEmpty. Placeholder text in Sub-600; icons in Soft-400 (decorative).
HoverInteract to see: Weak-50 fill and a Strong-950 border. Only on devices with hover (@media (hover: hover)).
FocusInteract to see: white fill, a Strong-950 border and the focus shadow.
FilledValue in Strong-950; icons switch to Sub-600.
ErrorError-base border and aria-invalid. The red border stays on hover, so the error cue never disappears under the pointer. In a Field, set by error, with the message below.
DisabledWeak-50 fill, no border, Disabled-500 text. Can't be focused; prefer explaining why a value can't be changed.

readOnly is passed through to the native element but has no distinct style, and Figma defines none.

Inline input, text area and digit input

The same static states for the other Figma sets. Hover and focus are live.

This hint helps people fill in the field.

This hint helps people fill in the field.

Check this value and try again.

This hint helps people fill in the field.

We sent a 4-digit code to your email.

That code has expired. Request a new one.

Gaps: contrast in three states (C6, C7, C8)

Error text and the required asterisk use Red 700, 5.26:1 (C6; Figma's State/Error/Base is 4.15:1). The default box border Stroke/Sub-300 is 1.53:1, below the 3:1 a lone boundary needs (C7). A disabled field's hint now uses Disabled-500, 4.23:1 (C8, resolved). The border follows Figma until design decides.

State tokens

The states are built from these tokens. Only the focus shadow is a component property (--nu-input-focus-shadow); the rest are applied directly, so change them through the tokens, not per control.

StateBox input and SelectInline inputText area and digit input
DefaultBg/White, Stroke/Sub-300Transparent, no borderBg/White, Stroke/Soft-200, regular x-small shadow
HoverBg/Weak-50, Stroke/Strong-950Bg/Weak-50, no borderBg/Weak-50, no border, no shadow
FocusBg/White, Stroke/Strong-950, buttons/important-focusBg/White, Stroke/Strong-950, buttons/important-focusBg/White, Stroke/Strong-950, buttons/important-focus
ErrorState/Error/Base border; focus shadow buttons/error-focus; icons Strong-950Bg/White, State/Error/Base border, regular x-small shadowState/Error/Base border; focus shadow buttons/error-focus
DisabledBg/Weak-50, no border, Text/Disabled-500Transparent, Text/Disabled-500Bg/Weak-50, no border, Text/Disabled-500

Composition examples

Sign in

Submit the empty form to see errors. Each error is linked to its field, and focus moves to the first field that needs fixing.

Create a space

Input, Select and Textarea in one form. Mark the required field with required and the optional ones with a sublabel.

For example, Biology 101.

Verify an email

onComplete fires when every box is filled. Type any six digits to verify, or 000000 to see the expired-code error. Pasting a whole code into any box fills the rest.

We sent a 6-digit code to your email.

A search input outside a form can use aria-label instead of a Field. The clear button appears once there's a query, and the result count is announced.

  • Photosynthesis basics
  • Cell structure
  • Introduction to Design Principles
  • Plant nutrition
  • Human-centred design

For a long list people search as they type, such as choosing a student or a space, use Combobox on the Dropdown and select page instead of Select.

API reference

Source: Field, Input, Select, PasswordInput, Textarea and DigitInput in packages/ui/src/components/, plus lib/useFieldControl.ts, which merges a control's own props with its Field.

Field props

Field props
PropTypeDefaultDescription
labelrequiredReactNode—Visible label, rendered as <label for>. Every field needs one.
childrenrequiredReactNode—The control: Input, Select, PasswordInput, Textarea or DigitInput.
hintReactNode—Helper text under the control, linked with aria-describedby.
errorReactNode—Error message. When set, the control gets aria-invalid and the message is linked.
requiredbooleanfalseShows the asterisk (hidden from screen readers) and passes native required to the control.
sublabelReactNode—Secondary text after the label, such as “(Optional)”. No default: pass a translated string.
hintIconReactNode<RiInformationFill />Decorative icon before the hint and error text (Figma Hint Texts). Rendered aria-hidden. Pass null to hide it.
disabledbooleanfalseDisables the control and greys the label and hint.
controlIdstringautoId for the control instead of a generated one. Use it to focus the control from your code.
refRef<HTMLDivElement>—Forwarded to the wrapper div.
…restOmit<HTMLAttributes<HTMLDivElement>, 'children'>—Any other attribute for the wrapper div (className, data-*…).

useField() returns the surrounding Field's ids and state (or null), for building a custom control that wires up the same way.

Input props

Input props
PropTypeDefaultDescription
size'sm' | 'lg''lg'Height 36 / 48px. Ignored when appearance is inline.
appearance'box' | 'inline''box'Box is Figma Text box 2; inline is Figma Inline Input (borderless until hover or focus, 36px).
invalidbooleanfrom FieldMarks the value invalid. Inside a Field, comes from error; an explicit value wins.
leadingIconReactNode—Decorative icon before the text. Rendered aria-hidden.
trailingIconReactNode—Decorative icon after the text, such as a card-brand badge your app supplies. Rendered aria-hidden.
leadingAddonReactNode—A segment before the text with a divider, such as a country-code <select>. Label it yourself.
trailingActionReactNode—An interactive element after the text, such as a clear button. Must be focusable and labelled.
controlClassNamestring—Class for the outer box (layout).
classNamestring—Also applied to the outer box, not the <input>.
idstringfrom FieldId of the native input. Inside a Field, the Field sets it.
disabledbooleanfrom FieldNative disabled. Inside a Field, inherited; an explicit value wins.
requiredbooleanfrom FieldNative required. Inside a Field, inherited; an explicit value wins.
aria-describedbystring—Merged with the Field’s hint and error ids.
refRef<HTMLInputElement>—Forwarded to the native input.
…restOmit<InputHTMLAttributes<HTMLInputElement>, 'size'>—Any other native input attribute (type, value, onChange, autoComplete, inputMode…), applied to the <input>.

Select props

Select props
PropTypeDefaultDescription
childrenrequiredReactNode—The <option> elements.
size'sm' | 'lg''lg'Height 36 / 48px.
placeholderstring—Text shown when nothing is selected. Rendered as a disabled, hidden first option.
leadingIconReactNode—Decorative icon before the value, such as a flag your app supplies. Rendered aria-hidden.
invalidbooleanfrom FieldMarks the value invalid. Inside a Field, comes from error.
valuestring | number | readonly string[]—Controlled value (native).
defaultValuestring | number | readonly string[]'' with placeholderUncontrolled initial value. With a placeholder and no value, the placeholder option is selected.
classNamestring—Applied to the outer box, not the <select>.
idstringfrom FieldId of the native select.
disabledbooleanfrom FieldNative disabled.
requiredbooleanfrom FieldNative required.
aria-describedbystring—Merged with the Field’s hint and error ids.
refRef<HTMLSelectElement>—Forwarded to the native select.
…restOmit<SelectHTMLAttributes<HTMLSelectElement>, 'size'>—Any other native select attribute (name, onChange, autoComplete…).

DatePicker props

DatePicker props
PropTypeDefaultDescription
valueDate | null—Controlled date (a local day). null is empty.
defaultValueDate | nullnullUncontrolled initial date.
onValueChange(value: Date | null) => void—Called with a complete, allowed date, or null when the box is cleared. Half-typed text does not call it.
minDate—Earliest day that can be typed or chosen.
maxDate—Latest day that can be typed or chosen.
isDateDisabled(date: Date) => boolean—Return true for days that cannot be chosen, such as weekends or fully booked days.
localestring'en-GB'BCP 47 locale: the typed order and separator, and the month and day names.
weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 61First day of the week: 0 is Sunday.
captionLayout'label' | 'dropdown''dropdown'dropdown: month and year dropdowns between the arrows, quick for a birth date. label: only the month and year.
placeholderstringthe locale formatShown when empty, for example dd/mm/yyyy.
size'sm' | 'lg''lg'Height 36 / 48px, as Input.
invalidbooleanfrom FieldMarks the value invalid. A typed date that is not real or not allowed also marks it, once people leave the box.
namestring—Posts the date as yyyy-mm-dd in a hidden input.
labelsPartial<DatePickerLabels>DATE_PICKER_LABELSButton, popover, month buttons, month and year dropdowns and the invalid hint, for translation.
id, disabled, required, className, aria-label, aria-describedbyas Input—Come from the Field when inside one.
refRef<HTMLInputElement>—Forwarded to the text box.

DateRangePicker props

DateRangePicker props
PropTypeDefaultDescription
valueDateRange—Controlled range, { from: Date | null, to: Date | null }.
defaultValueDateRange—Uncontrolled initial range.
onValueChange(value: DateRange) => void—Called after each click. The range is complete when both ends are set; the popover then closes.
placeholderstring'Select dates'Shown when nothing is chosen.
min, max, isDateDisabled, locale, weekStartsOn, size, invalid, name, labelsas DatePicker—Same meaning. name posts name-from and name-to.
refRef<HTMLButtonElement>—Forwarded to the button that opens the calendar.

Calendar props

Calendar props
PropTypeDefaultDescription
mode'single' | 'range''single'One day, or a start and an end day.
value / defaultValueDate | null (single), DateRange (range)—Controlled or initial selection.
onValueChange(value) => void—Called when a day is chosen. In range mode, first with only from, then with both ends (earlier day first).
month / defaultMonth / onMonthChangeDate / Date / (month: Date) => void—The month on show. Default is the selected month, else today.
min, max, isDateDisabledDate, Date, (date) => boolean—Days outside min and max, and days the function rules out, are disabled. Month arrows stop at min and max.
locale, weekStartsOn, captionLayout, labelsas DatePicker—Month and day names, week start, dropdowns or plain label, and translated button names.
showTodayboolean—Adds a Today button under the grid that moves to today and chooses it when it is allowed.
autoFocusboolean—Moves focus to the active day on mount, for a calendar that opens in a popover.
aria-labelstring—Names the grid. Without it the grid is named by the month shown.
refRef<{ focusDay(): void }>—Handle to move focus to the active day.

PasswordInput props

PasswordInput props
PropTypeDefaultDescription
showLabelstring'Show password'Accessible name of the reveal button while the password is hidden. Pass a translated string.
hideLabelstring'Hide password'Accessible name of the reveal button while the password is visible.
autoCompletestring'current-password'Use 'new-password' on sign-up and change-password forms.
refRef<HTMLInputElement>—Forwarded to the native input.
…restOmit<InputProps, 'type' | 'trailingAction'>—Every Input prop except type (managed by the toggle) and trailingAction (the reveal button).

Textarea props

Textarea props
PropTypeDefaultDescription
showCounterbooleanfalseShows the character counter (“12/200”). Needs maxLength; without it nothing renders.
maxLengthnumber—Native maximum length. Also the counter’s maximum.
formatCounter(count: number, max: number) => string`${count}/${max}`Formats the counter text. Pass a localised formatter if needed.
rowsnumber4Visible rows (native).
invalidbooleanfrom FieldMarks the value invalid. Inside a Field, comes from error.
valuestring | number | readonly string[]—Controlled value. The counter follows it.
defaultValuestring | number | readonly string[]—Uncontrolled initial value. The counter follows typing.
onChangeChangeEventHandler<HTMLTextAreaElement>—Native change handler. Called after the counter updates.
classNamestring—Applied to the outer box, not the <textarea>.
idstringfrom FieldId of the native textarea.
disabledbooleanfrom FieldNative disabled. Also turns off resizing.
requiredbooleanfrom FieldNative required.
aria-describedbystring—Merged with the Field’s hint and error ids and the counter id.
refRef<HTMLTextAreaElement>—Forwarded to the native textarea.
…restTextareaHTMLAttributes<HTMLTextAreaElement>—Any other native textarea attribute (name, placeholder, onBlur…).

DigitInput props

DigitInput props
PropTypeDefaultDescription
lengthnumber4Number of boxes.
valuestring—Controlled value.
defaultValuestring''Uncontrolled initial value.
onChange(value: string) => void—Called with the whole code after every change.
onComplete(value: string) => void—Called when every box is filled.
alphanumericbooleanfalseAccept letters as well as digits. Switches the keyboard from numeric to text.
disabledbooleanfrom FieldDisables every box.
invalidbooleanfrom FieldError border on every box and aria-invalid.
aria-labelstring—Name for the group. Required unless the DigitInput is inside a Field, which names it.
digitLabel(index: number, length: number) => string"Digit {n} of {N}"Accessible name of each box. Pass a translated formatter.
namestring—Adds a hidden input with this name, so the code is submitted with a form.
idstringfrom FieldId of the group.
classNamestring—Class for the group.
refRef<HTMLDivElement>—Forwarded to the group div.

DigitInput doesn't accept other native attributes: only the props above are applied.

Styling hooks

Field renders .nu-field with data-disabled and data-invalid. Input and Select render .nu-input (Select adds .nu-select) with data-size, data-appearance, data-invalid, data-disabled and data-has-addon. Textarea renders .nu-textarea; DigitInput renders .nu-digit-input. Use props rather than styling these attributes.

Supported override points: --nu-input-height-sm, --nu-input-height-lg, --nu-input-height-inline, --nu-input-pad-inline, --nu-input-gap, --nu-input-icon-leading, --nu-input-focus-shadow and --nu-digit-size. A button in trailingAction can use .nu-input__reveal for the same 24 × 24 hit area as the password toggle.

Accessibility

Keyboard

KeyBehaviour
TabMoves to the control. Disabled controls are skipped. In PasswordInput, the next Tab reaches the reveal button. Each DigitInput box is a tab stop.
Enter / SpaceToggles the password reveal button, or a trailing action button.
← / →In DigitInput, move to the previous or next box.
BackspaceIn DigitInput, clears the box, or moves back and clears the previous one when the box is empty.
Ctrl+V / ⌘VIn DigitInput, pastes a whole code from the current box onwards.
Alt+↓In DatePicker, opens the calendar from the text box.
← / → / ↑ / ↓In the calendar, move one day or one week. Enter or Space chooses the day; Esc closes the popover and returns to the box.
Home / EndIn the calendar, go to the first or last day of the week.
Page Up / Page DownIn the calendar, change month. With Shift, change year.
EscCloses the calendar popover and returns focus to the calendar button.

Input, Select and Textarea keep their native keys. In DatePicker the box keeps its native typing keys, and the calendar button is the next Tab stop.

Screen readers

Each control is named by the Field's <label for>, which includes the sublabel. Required is announced from native required; the asterisk is hidden. Hint, error and the Textarea counter are linked with aria-describedby, and errors set aria-invalid. Errors aren't a live region: they're read when focus reaches the field. DigitInput is a group named by the Field label, with each box labelled “Digit n of N”. The reveal button has aria-pressed and aria-controls. Icons are hidden. In DatePicker, the calendar button says it opens a dialog and whether it is open; the calendar is a grid whose days are named in full (“Wednesday, 25 November 2026”), with the selected day marked by aria-selected and today by aria-current; a typed date that is not real is announced with an example.

Focus

Focus shows a Strong-950 border with the focus shadow (error-focus when invalid); forced-colours mode adds a 2px Highlight outline. Focusing a digit box selects its content, and typing moves to the next box. Field never moves focus on its own.

Contrast and colour

Placeholders and the counter use Sub-600 (6.69:1) instead of Figma's Soft-400 (2.49:1) (D7, C1, C12). Error text is Red 700, 5.26:1 (C6). The default border Sub-300 is 1.53:1 (C7); hover and focus borders (Strong-950) pass. A disabled hint is 1.52:1 (C8). Errors are never colour-only: the message says what's wrong.

Touch and pointer

Controls are 48 or 36px tall and full width. Reveal and clear buttons have a 24 × 24 hit area (WCAG 2.5.8). Digit boxes are 80 × 80 and shrink on narrow screens; they bring up the numeric keyboard and support one-time-code autofill. Hover styles apply only on hover-capable pointers.

You must: put every control in a Field, or give it an aria-label; label any leadingAddon or trailingAction control; move focus to the first invalid field when a form fails; pass translated showLabel, hideLabel, digitLabel and formatCounter text; give DigitInput a name when it's part of a form.

Figma mapping

Figma setVariantsCode
Text box 2 (5:878 / 5:1320)Type: Basic · Select · Phone Number · Password · Search · Card · Date. State: Placeholder · Hover · Focus · Filled · Disabled · Error. Size: Small · Large (84 variants in total)Input (+ leadingIcon, trailingIcon, leadingAddon), Select, PasswordInput; size: sm · lg; hover and focus are interactive; invalid, disabled
Inline Input (5:1255)6 states; Left Icon; ButtonInput appearance="inline" + leadingIcon, trailingAction
Text Area (5:1282)6 states; Label; Hint; CounterTextarea + showCounter / maxLength inside Field
Digit Input (5:1242)6 statesDigitInput (a group of N boxes)
Date & Range Picker [1.1] (My Design system library)Date and RangeDatePicker, DateRangePicker, Calendar. Not readable from this file, so built from tokens (C131).
Label (5:896)Required · Sublabel · Normal / DisabledField label, required, sublabel, disabled
Label (4446:59279) · Hint Texts (4446:59288; earlier 5:905)Default · Error · Disabled; Left IconField hint, error, hintIcon
TextBox (5:915, legacy)Basic · Select · Country · Date · Card · Phone (broken properties in Figma)Deprecated: superseded by Text box 2 (C14). Country = Select with a flag leadingIcon

Guidelines and related components

Use inputs and fields for

  • Short text people type: names, emails, search terms (Input).
  • Choosing one option from a short, known list (Select).
  • Longer answers, prompts and notes (Textarea), and one-time codes (DigitInput).
  • A day or a span of days: birth dates, deadlines, study periods (DatePicker, DateRangePicker).

Don't use them for

  • Searching a long list as you type. Use Combobox on the Dropdown and select page.
  • Switching between two or three views. Use Button group.
  • Showing a value people can't change. Show it as text rather than a disabled field.

Do and don't

DoGive every field a visible label. The label stays visible while people type and names the control for screen readers.
Don'tDon't use the placeholder as the label. It disappears on the first keystroke and is lower contrast than a label.

Enter an email address, like name@school.edu.

DoSay what went wrong and how to fix it. People can correct the value without guessing.

Invalid input.

Don'tDon't show a generic error. “Invalid input” says something failed, not what to do next.
DoUse Textarea with a counter for long answers. People see the whole answer and how much room is left.
Don'tDon't squeeze long text into an Input. A single line hides most of the answer while people write it.

Common mistakes

  • Setting showCounter without maxLength: no counter renders.
  • Passing invalid to the control and error to the Field with different values. The control's own prop wins, so the border and message disagree.
  • Leaving out autoComplete and inputMode, so phones show the wrong keyboard and browsers can't fill the value.
  • Using a DigitInput in a form without name: the code isn't submitted.
  • Putting a control in trailingAction without an accessible name.