// preview in
saved for every page

Field

Wraps a form control with its label, description and error, wires them together for assistive tech and tracks validity. Comes with Input, Textarea, Label and Fieldset.

Built on Base UIPattern HTML label + aria-describedbyClient componentSource ↗
minimal · light · #3fa34d

We send invoices and sign-in links here.

invalid
disabled

Installation

Set up Camaleo once (Installation), then import what you need.

import { Field, FieldLabel, FieldDescription, FieldError } from "@camaleo/react";
import { Input, Textarea, Label, Fieldset, FieldsetLegend } from "@camaleo/react";

Anatomy

A Field is a grid of up to four parts. The label points at the control, and the description and error are read with it.

labelinputdescriptionerror
[data-slot="field"][data-slot="field-label"] · [data-slot="field-description"] · [data-slot="field-error"]control: [data-slot="input"] · [data-slot="textarea"]

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d

As it appears on your ID. We print it on invoices.

0 / 160 characters

Textarea with a counter

Type in it. A live count in FieldDescription is read with the field, so people hear the limit before they hit it.

Required

Type a letter, delete it and leave. valueMissing waits until the field was changed or the form submitted, so a fresh form never opens in red.

Enter the full address, like ana.souza@acme.com.

Error from the server

When the error comes from your API, set invalid and render FieldError match. The input gets aria-invalid and the message is linked to it.

Lowercase letters, numbers, dots and underscores.

Custom validation

Type a space or delete letters. validate returns the message, and an empty FieldError shows it while people type.

Only workspace owners can change it.

Disabled

disabled on the Field reaches the label and the control. Say why in the description, or nobody knows who can change it.

Emergency contact

Grouped with a fieldset

Fieldset and FieldsetLegend name a group, so "Name" is announced as part of "Emergency contact".

Label outside a field

For a checkbox or switch, wrap it in Label: the whole line becomes clickable and names the control.

Input types

The right type and autoComplete bring up the matching phone keyboard and let the browser fill the value in.

In context

Fields come in forms. These are whole pieces of UI you can copy, in the skin you picked.

Create your account

Free for 14 days. No card needed.

At least 8 characters, with one number.

Signup formCard · Field · Input · Checkbox · Button
Shipping address
Shipping addressFieldset · Field · Input · NativeSelect

Public profile

Shown on your profile and next to your comments.

Profile settingsCard · Field · Input · Textarea · Button

Guidelines

We send invoices and sign-in links here.

DoA visible label above the input.

It stays put while people type, and the placeholder is free to show an example.

Don’tThe placeholder as the label.

It disappears on the first keystroke, and its low contrast is hard to read.

Use at least 8 characters, with one number.
DoSay how to fix it.

The error names the rule, so people know what to type.

Invalid input.
Don’tA generic error.

"Invalid input." tells people something is wrong, not what to change.

Props

Field takes the props of the Base UI field root. These are the ones you use most:

PropTypeDefaultDescription
disabledbooleanfalseDisables the label and the control.
namestring—Identifies the field when the form is submitted. Takes precedence over the control's name.
validate(value, formValues) => string | string[] | null—Returns the error message(s), or nothing when the value is valid. Can be async.
validationMode"onSubmit" | "onBlur" | "onChange"'onSubmit'When validate and the native constraints run.
validationDebounceTimenumber0Milliseconds to wait between validate calls in onChange mode.
invalidboolean—Marks the field invalid, for errors that come from your server or a form library.
dirtyboolean—Control these states from a form library.
touchedboolean—Control these states from a form library.
Also accepts every prop of the underlying primitive and native attributes.

FieldError takes match: true to always show it, a ValidityState key such as "valueMissing" to show it only for that error, or nothing to show it whenever the field is invalid. Fieldset takes disabled. Input and Textarea take the attributes of <input> and <textarea>; Label takes those of <label>.

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the field itself.

Attributes

data-slot"field" · "field-label" · "field-description" · "field-error" · "input" · "textarea"
data-invalidpresent on the field and control when invalid
data-validpresent after a passing validation
data-disabledpresent when disabled
data-touched · data-dirtypresent once focused and left · once changed
data-filled · data-focusedpresent when it has a value · while focused

Tokens it reads

Color

--cml-border-control--cml-danger--cml-fg--cml-fg-muted--cml-focus-ring

Shape & size

--cml-font-size-field--cml-font-size-help--cml-font-size-label--cml-gap--cml-radius-control--cml-radius-field--cml-toolbar-btn-size

Type & motion

--cml-duration-fast--cml-ease-standard--cml-font-body--cml-font-display--cml-font-field--cml-font-label--cml-font-style-help--cml-font-weight-display--cml-font-weight-error--cml-font-weight-label--cml-letter-spacing-label--cml-text-transform-label

This component only

--cml-corner-shape--cml-disabled-opacity--cml-field-bg--cml-field-border-sides--cml-field-border-width--cml-field-gap--cml-field-height--cml-field-padding--cml-field-shadow--cml-placeholder-style
/* a tighter form, only in this dialog */
.quick-add [data-slot="field"] {
  --cml-field-gap: 0.25rem;
}

Accessibility

TabMoves focus to the control. Clicking the label focuses it too.
Shift + TabMoves focus back to the previous control.
Was this page helpful? · Edit this page on GitHub