Skip to content

Field

Form field primitives that wire labels, controls, descriptions, and validation messages without owning the actual input component.

Preview

Installation

$pnpm exec dgadd add ui/field
[Installs to]src/components/ui/field[Item]ui/field

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { Field } from "@/components/ui/field";import { Input } from "@/components/ui/input";export default function FieldInput() {  return (    <Field invalid required>      <Field.Label>Email</Field.Label>      <Field.Control>        <Input placeholder="you@example.com" />      </Field.Control>      <Field.Description>Used for review notifications.</Field.Description>      <Field.Error>Email is required.</Field.Error>    </Field>  );}

Examples

Valid, invalid, disabled

Preview

Checkbox and Select

Preview

Form integration

Preview

API Reference

Field

NameTypeDefaultDescription
controlIdstringOverride the auto-generated id used by the wrapped control, label, description, and error.
invalidbooleanfalseMarks the control as invalid; sets aria-invalid and surfaces Field.Error in aria-describedby.
requiredbooleanMarks the control as required and shows a required indicator next to Field.Label. The indicator is muted at rest and resolves to the error hue only while the field is invalid, so a resting form carries no error-hue marks. This remains authoritative when the child sets required={false}.
disabledbooleanDisables the control and applies data-disabled to the field root. This remains authoritative when the child sets disabled={false}.

Field.Label

NameTypeDefaultDescription
htmlForstringcontrolIdOverride the htmlFor target. Defaults to the Field's resolved control id (a consumer id on the control child wins). For div-based controls such as Checkbox and Radio, clicking the label also focuses and activates the control.
idstringOverride the auto-generated label id used for aria-labelledby on the control.

Field.Control

NameTypeDefaultDescription
childrenrequiredReactElementSingle child control. Field clones it with id, disabled, required, aria-invalid, aria-describedby, and aria-labelledby. If the child supplies its own id, that id wins and Field.Label's htmlFor follows it.

Field.Description

NameTypeDefaultDescription
childrenReactNodeHelper text. Returns null when empty so aria-describedby stays clean, and renders screen-reader-only while the field is invalid and Field.Error has content.

Field.Error

NameTypeDefaultDescription
childrenReactNodeValidation message. Renders and is surfaced in aria-describedby only while Field is invalid.

Accessibility

Notes

Form Contract

Field.Control clones one control element and applies id, required, disabled, aria-invalid, and aria-describedby from Field.Root. A disabled Field.Root remains authoritative even if the child explicitly sets disabled={false}. A required Field.Root remains authoritative even if the child explicitly sets required={false}. Use Field.Control with Input, Textarea, Select, or another control that accepts those props. A consumer id on the control child wins: Field keeps the child's own id and points Field.Label's htmlFor at it. Slots register through context, so wrapping Field.Label, Field.Description, or Field.Error in a layout element keeps the ARIA wiring intact.

One Helper Slot

Field.Description and Field.Error share one visible row. While the field is invalid and Field.Error has content, the description renders screen-reader-only and the error takes its place, so field height does not depend on validity and a form validating on blur does not push the input the user is correcting. The description keeps its id and its place in aria-describedby, which lists the error first so the problem is announced before the hint.

InputGroup vs Field

InputGroup is only a decorated input shell for prefix and suffix content. Plain text affixes are aria-hidden decoration; interactive affixes need explicit labels. Field is the form wrapper for label, helper text, and error wiring.

Source

Install via CLI: pnpm exec dgadd add ui/field.

Highlighted source loads after this disclosure opens. Browse the source repository.