Tessera UI

Field

A text input field with a label, helper text, FieldDropdown prefix, Button suffix, leading/trailing icon buttons, a clearable button, and a character counter. Supports all intent states and full RTL layout.

Installation

pnpm add @tessinaui/ui

Usage

import { Field, FieldDropdown, FieldDropdownItem } from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";
<Field label="Email" placeholder="you@example.com" />

Three label layout variants are available via labelPosition:

  • "outside-top" (default) — label above the field
  • "inside" — label inside the container above the placeholder text
  • "outside-left" — label to the left of the field (horizontal layout)

Playground

Configure every property interactively:

Preview
Loading...

Showcase

All intents, sizes, and feature combinations:

Preview
Loading...

API Reference

Props

PropTypeDefaultDescription
labelstringLabel text
requiredbooleanfalseShows a red * after the label and sets aria-required
labelPosition"outside-top" | "inside" | "outside-left""outside-top"Label layout variant. inside renders the label inside the container above the input. outside-left creates a horizontal label + field layout
infoTextstringSmall info text shown top-right in the label row (or next to label for outside-left)
intent"none" | "error" | "warning" | "success" | "info""none"Controls border color, focus ring color, and supporting text color/icon
size"xs" | "sm" | "md" | "lg" | "xl""md"Controls height, padding (proportional to size), and text/icon size
rounded"full" | "lg" | "md" | "sm" | "none""full"Border radius
leadingIconReact.ReactNodeIcon rendered inside the container on the leading side (as a ghost IconButton)
leadingIconLabelstring""Accessible label for the leading icon (screen readers)
leadingIconOnClick() => voidMakes the leading icon interactive when provided
trailingIconReact.ReactNodeIcon rendered inside the container on the trailing side
trailingIconLabelstring""Accessible label for the trailing icon
trailingIconOnClick() => voidMakes the trailing icon interactive when provided
clearablebooleanfalseShows an × IconButton when the field has a value. Visible alongside trailingIcon
onClear() => voidCalled when the clear button is clicked
prefixReact.ReactNodeSlot to the left of the input container — use <FieldDropdown> for the country selector pattern
suffixReact.ReactNodeSlot to the right of the input container — use <Button> with matching size and rounded
supportingTextstringText below the input. Color and icon follow intent
showCharacterCounterbooleanfalseShows current / maxLength counter. Requires maxLength
dir"ltr" | "rtl"Text direction. RTL flips the full layout
wrapperClassNamestringExtra className for the outer wrapper <div>
containerClassNamestringExtra className for the bordered container <div>
disabledbooleanfalseDisables the input and mutes all styles
maxLengthnumberNative maxLength for the <input>. Used by character counter
placeholderstringNative placeholder text
value / defaultValuestringControlled / uncontrolled value
onChangeChangeEventHandlerNative onChange handler

All other native <input> attributes (type, name, autoComplete, etc.) are forwarded.

With Prefix & Suffix

Use <FieldDropdown> for the prefix (country selector, currency picker) and <Button> for the suffix. Pass the same size and rounded values to all three so they share identical border radius and height:

import { Field, FieldDropdown } from "@tessinaui/ui";
import { Button } from "@tessinaui/ui";

<Field
  label="Phone"
  placeholder="Enter number"
  rounded="full"
  size="md"
  prefix={
    <FieldDropdown
      flag="si"
      text="SLO(+386)"
      size="md"
      rounded="full"
    />
  }
  suffix={
    <Button size="sm" rounded="full">
      Verify
    </Button>
  }
/>

FieldDropdown props

PropTypeDefaultDescription
flagstringISO 3166-1 alpha-2 country code (e.g. "si" for Slovenia). Loads from flagcdn.com
textstringText label shown next to the flag (e.g. "SLO(+386)")
showChevronbooleantrueWhether to show the chevron. When false, the button is non-interactive (pointer-events-none)
childrenReact.ReactNodeDropdown items (use <FieldDropdownItem>). When provided with showChevron={true}, clicking opens a Radix dropdown
roundedsame as Field"full"Must match the parent Field's rounded prop
sizesame as Field"md"Must match the parent Field's size prop
onClickMouseEventHandlerClick handler when no children dropdown is used

Controlled Usage

const [value, setValue] = useState("");

<Field
  label="Search"
  value={value}
  onChange={(e) => setValue(e.target.value)}
  clearable
  onClear={() => setValue("")}
  showCharacterCounter
  maxLength={100}
/>

Label Variants

{/* Inside — label floats above input text inside the container */}
<Field labelPosition="inside" label="Email" placeholder="you@example.com" />

{/* Outside left — horizontal layout, label column on the leading side */}
<Field labelPosition="outside-left" label="Email" placeholder="you@example.com" />

RTL

Pass dir="rtl" to flip the full layout including the label row, input icons, prefix/suffix, and supporting text. Works with all labelPosition variants:

<Field dir="rtl" label="بحث" placeholder="نص العنصر النائب" leadingIcon={<Search />} />
<Field dir="rtl" labelPosition="outside-left" label="البريد" placeholder="you@example.com" />

Accessibility

  • The <input> is associated with its <label> via a generated (or provided) id/htmlFor pair
  • aria-required is set when required={true}
  • aria-invalid is set when intent="error"
  • The clear button has aria-label="Clear" and is excluded from tab order (tabIndex={-1})
  • Touch target for the clear button: ≥ 44×44 px
  • Focus ring appears on the container (not the inner input) via focus-within:ring-*
  • Color contrast: all intent states meet WCAG 2.1 AA

On this page