Skip to content

Text Field

A single-line text input with label, help text, validation and optional prefix/suffix, plus the field primitives other inputs compose.

Preview

We’ll send receipts and claim updates here.

Installation

pnpm add @syntara/react @syntara/tokens
import { TextField, Label, Description, FieldError, Input, FieldGroup } from '@syntara/react';

Usage

import { TextField } from '@syntara/react';

export function EmailField() {
  return <TextField label="Email" type="email" description="We'll send receipts here." isRequired />;
}

Examples

States

Default, required with description, invalid and disabled.

Printed on the front of your card.
Enter a 10-digit mobile number.

Prefix and suffix

Currency, units or icons inside the input box.

Validation

Native required checks plus a validate() function, shown after submit.

11 characters, e.g. ABCD0123456.

Sizes

sm, md and lg, each next to a Button of the same size: same height, same corner.

Accessibility

KeysAction
TabMoves focus to the input.
EnterSubmits the surrounding form.
  • Label, description and error message are linked to the input with aria-labelledby and aria-describedby.
  • Errors show an icon and text, never colour alone.
  • The required asterisk is aria-hidden: the input is announced as required through the native required attribute.
  • Text inputs show the focus ring whenever focused, as browsers do for text inputs.
  • The field edge is a 1px boundary of at least 3:1 against canvas, default, raised and sunken surfaces in every brand and scheme (WCAG 1.4.11): border.strong in light, a mix of 80% border.strong and 20% border.default in dark. The test "field boundary contrast" proves it over the 5 tenants and 1,000 fuzz brands.

Guidelines

Do

  • Always give a visible label; put format hints in description.
  • Use the right type and inputMode so mobile keyboards match the data.
  • Validate on submit and explain how to fix the error.
  • In a row with a Button, give both the same size: they share height and corner radius.

Don’t

  • Don't use the placeholder as the label.
  • Don't show an error before the person has had a chance to type.

API reference

TextField

label
ReactNode

Visible label, linked to the control. Without one, pass aria-label.

description
ReactNode

Help text, linked with aria-describedby.

errorMessage
string | ((validation: ValidationResult) => string)

Shown with an icon when invalid. Defaults to the browser or validate() message.

placeholder
string

Example value. Not a substitute for a label.

type
  • textdefault
  • email
  • tel
  • url
  • password
  • search

Native input type.

prefix
ReactNode

Content before the input, inside the box (text in text.subtle, or an icon).

suffix
ReactNode

Content after the input, inside the box (a unit, icon or small button).

value / defaultValue
string

Controlled / uncontrolled value.

onChange
(value: string) => void

Called on every edit.

isRequired
boolean

Marks the field required (native required + a decorative asterisk on the label).

Default false

isInvalid
boolean

Forces the invalid state (aria-invalid, danger border, error message).

Default false

isDisabled
boolean

Disables the field.

Default false

validate
(value) => string | string[] | true | null | undefined

Custom validation; return an error message to show.

inputRef
Ref<HTMLInputElement>

Ref to the underlying input.

size
  • sm
  • mddefault
  • lg

Height of the box: md is the density's control height, sm one 8px step shorter, lg two steps taller. Height, corner radius and inline inset match a Button of the same size, so a field and a button in one row line up.

Label

isRequired
boolean

Adds a decorative (aria-hidden) asterisk; the control carries the required state.

Default false

Description

children
ReactNode

Help text. Renders React Aria's Text with slot="description".

FieldError

children
ReactNode | ((validation: ValidationResult) => ReactNode)

Error text with an alert icon; renders nothing while the field is valid.

Input

className
string | ((renderProps) => string)

The bordered input. Inside a FieldGroup it becomes bare and the group draws the box.

FieldGroup

children
ReactNode | ((renderProps) => ReactNode)

Input plus adornments. Draws the box, hover/invalid/disabled states and the focus ring; a click on it focuses the input.

Tokens

The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.

Colour11
border.defaultborder.strongfeedback.danger.fgfeedback.danger.solidfocus.ringsurface.defaultsurface.sunkentext.defaulttext.disabledtext.subtlesurface.canvas
Type9
font.size.mdfont.size.smfont.size.xsfont.weight.mediumline-height.normalline-height.snugfont.tracking.mdfont.tracking.smfont.tracking.xs
Space and size5
control-heightcontrol-padding-inlinespace.1space.2space.4
Shape1
radius.field
Motion6
motion.duration.fastmotion.duration.normalmotion.easingmotion.easingOutmotion.duration.springmotion.spring