Single-line text entry. Its sm/md/lg axis shares size.interactive with Button, Select, and SegmentedControl, so controls composed on one row stay aligned. Every state is answered on the border, focus included, so there is no offset ring crowding the label.
Height and type move together while the comfortable space.3 field inset stays fixed; medium is the modifier-free default. Field sizes preserve the text column instead of scaling like content-sized Buttons.
| Example | Size | Modifier | Height | Usage |
|---|---|---|---|---|
| Small | --sm | size.interactive.sm | Toolbars, table filters, dense forms | |
| Medium | ds-input | size.interactive.md | Forms and most surfaces | |
| Large | --lg | size.interactive.lg | High-emphasis and touch-oriented flows |
Real DOM drivers set every state; demo classes force the pointer states here and behave identically.
| Example | State | Driver | Roles | Usage |
|---|---|---|---|---|
| Rest | — | border.defaulttext.muted | Placeholder is never essential content | |
| Rest, filled | — | text.primary | Entered value on surface.page | |
| Hover | :hover | border.hover | The border answers the pointer | |
| Focus | :focus-visible | focus.ring | The engaged control — the border takes the focus color, no offset ring | |
| Invalid | [aria-invalid] | status.error.border.fg | Pair with an error message via aria-describedby | |
| Read-only | [readonly] | surface.canvas | Focusable and copyable, not editable | |
| Disabled | :disabled | action.disabled.bg.text | Excluded from interaction and the tab order |
One recipe across the semantic types (email, password, number, and friends): the type buys the right mobile keyboard, native validation, masking, or steppers — never a different look. The type is an attribute, not a variant.
The control is bare by design — labels, help, and errors compose around it.
| Verdict | Rule |
|---|---|
| Do | Give every input a label: a visible one via FormField, or aria-label when the surrounding context is the label. |
| Do | Keep the placeholder a hint, never a label and never essential content — text.muted sits below AA by design. |
| Do | Choose read-only vs disabled by whether the value still matters: read-only keeps it in the page (focusable, selectable, submitted); disabled removes the control from interaction and the tab order. |
| Do | Set aria-invalid with a message referenced by aria-describedby — never signal errors with color alone. |
| Don’t | Disable an input as validation feedback — show the error on the field instead. |