Default
Show the codeHide the code
Default, code
<TextField label="Full name" defaultValue="Priya Shah" autoComplete="name" />A labelled text input (or textarea) with hint and error, replacing v1's 17 Input sets.
These are the real React components. Use them with a mouse, touch or keyboard, and switch the theme and density for every example at once.
Default, code
<TextField label="Full name" defaultValue="Priya Shah" autoComplete="name" />With hint, code
<TextField
label="Email"
type="email"
autoComplete="email"
defaultValue=""
hint="We only use this to send receipts."
isRequired
/>Invalid, code
<TextField
autoComplete="name"
label="Email"
type="email"
defaultValue="priya@"
isInvalid
errorMessage="Enter an email address like name@example.com"
/>Disabled, code
<TextField label="Full name" defaultValue="Priya Shah" autoComplete="name" isDisabled />With icons, code
<TextField
autoComplete="name"
label="Search team"
type="search"
defaultValue=""
placeholder="Name or email"
iconStart={Search}
/>Email, code
<TextField
label="Work email"
type="email"
autoComplete="email"
defaultValue="priya@northwind.co.uk"
iconStart={Mail}
/>Multiline, code
<TextField
autoComplete="name"
label="Short bio"
multiline
maxLength={160}
showCount
defaultValue="Product designer in Leeds."
hint="160 characters at most."
/>Mobile size, code
<TextField label="Email" type="email" autoComplete="email" defaultValue="" size="lg" />The 19 design tokens TextField reads, with the value each has in the theme and density chosen above. Change a token and the component follows, in Figma and in code.
| Token | Value now | CSS variable |
|---|---|---|
| color.border.subtle | … | --sd-color-border-subtle |
| color.icon.default | … | --sd-color-icon-default |
| color.text.disabled | … | --sd-color-text-disabled |
| control.height.lg | … | --sd-control-height-lg |
| field.hint | … | --sd-field-hint |
| font.line-height.normal | … | --sd-font-line-height-normal |
| input.bg | … | --sd-input-bg |
| input.bg-disabled | … | --sd-input-bg-disabled |
| input.border | … | --sd-input-border |
| input.border-hover | … | --sd-input-border-hover |
| input.border-invalid | … | --sd-input-border-invalid |
| input.fg | … | --sd-input-fg |
| input.height | … | --sd-input-height |
| input.padding-x | … | --sd-input-padding-x |
| input.placeholder | … | --sd-input-placeholder |
| input.radius | … | --sd-input-radius |
| motion.duration.fast | … | --sd-motion-duration-fast |
| motion.easing.standard | … | --sd-motion-easing-standard |
| spacing.inset.sm | … | --sd-spacing-inset-sm |
The npm package isn’t published yet. When it is, this is the install line; until then, this page runs the same build of the package (from commit fa3abf8).
Install
npm i @superduperui/react @superduperui/tokensImport
import { TextField } from "@superduperui/react"TextField also takes the props of the React Aria component it’s built on.
| Prop | Type | What it does |
|---|---|---|
labelrequired | string | Visible label (required: placeholders are not labels). |
hint | string | Help text under the field, linked by aria-describedby (3.3.5). |
errorMessage | string | ((validation: ValidationResult) => string) | Error text, shown with an icon and "Error:" when the field is invalid. |
placeholder | string | |
type | 'text' | 'email' | 'tel' | 'url' | 'search' | 'password'Default 'text' | Sets the keyboard too: email, tel and url get the matching inputmode. |
iconStart | LucideIcon | Decorative Lucide icon inside the start of the field. |
iconEnd | LucideIcon | Decorative Lucide icon inside the end of the field. |
multiline | boolean | Multi-line (renders a textarea). |
rows | numberDefault 4 | |
showCount | boolean | With maxLength: show "n of max characters" and announce politely when 20 or fewer remain. |
inputRef | Ref<HTMLInputElement> | |
textAreaRef | Ref<HTMLTextAreaElement> | |
actionEnd | ReactNode | An interactive control inside the end of the field, e.g. a show-password toggle (44px). |
hideLabel | boolean | Keep the label for assistive technology but hide it visually (only where a visible heading or column label names the field). |
size | 'default' | 'lg'Default 'default' | default: control/height/md (44px Comfortable); lg (mobile): control/height/lg (52px), for phone form anatomy. Single-line only. |
Screen readers announce "Email, edit text, required, invalid entry, Error: Enter an email address like name@example.com".
WCAG 2.2 AAA criteria the component meets in Comfortable density: 1.4.6, 1.4.9, 2.1.3, 2.3.2, 2.4.13, 2.5.5, 2.5.6.
Compact density meets AA (2.5.8, 24px targets), not AAA.
Shared patterns it ships with: 1.3.6 (set autoComplete); 3.3.5 (hint); 3.3.6 (errors with a way to recover); 3.3.8 / 3.3.9 (paste and password managers never blocked).
| axe-core (WCAG A, AA, AAA and best practice) | 180 runs across every story, theme, density and width: 0 violations |
|---|---|
| Rendered text contrast | 260 text runs: 0 below 7:1, lowest 8.43:1 |
| Target size | 44 targets: 0 under 44px in Comfortable |
| Focus appearance (2.4.13) | 36 focus stops in 3 browser engines: all pass |
| Keyboard script | Chromium 3 of 3, Firefox 3 of 3, WebKit 3 of 3 |
Manual passes with VoiceOver and NVDA haven’t been done yet.
Each step is scripted and run in Chromium, Firefox and WebKit.
| Key | What happens |
|---|---|
| Tab | Tab reaches the field; it is invalid and its error is its description |
| ControlOrMeta+A | Native editing keys work (select all) |
| Text is entered, replacing the selection |
The Figma component is TextField, with State (Default, Hover, Focus, Disabled, Invalid); Content (Placeholder, Filled); Label visibility (Visible, Hidden); Multiline (False, True); Size (Default, Mobile). The Figma library isn’t published yet.
Figma to code: State (Default, Hover, Focus, Disabled = isDisabled, Invalid = isInvalid); Content (Placeholder = placeholder, Filled = defaultValue); Label visibility → hideLabel (Visible = false, Hidden = true); Multiline → multiline (False = false, True = true); Value → placeholder | defaultValue (by Content); Leading icon → iconStart when Show leading icon; Trailing icon → iconEnd when Show trailing icon; Size → size (Default = "default", Mobile = "lg"); nested Label → label; nested Hint → hint; nested FieldError → errorMessage.