Field
PortedLabel, help text and error around one form control. The label and messages are linked to the control. Native <div> attributes and ref pass through.
The ID printed on the package.
import { Input } from "@etched/monet/input";
import { Field } from "@etched/monet/field";
export function Preview() {
return (
<Field label="Chip ID" description="The ID printed on the package.">
<Input placeholder="SH-24A-0000" />
</Field>
);
}Installation
import { Field, FieldGroup, Fieldset } from "@etched/monet/field";Examples
Fields
The ID printed on the package, for example SH-24A-0193.
Type a value that is not an email, then leave the field.
import { Input } from "@etched/monet/input";
import { Field } from "@etched/monet/field";
export function Fields() {
return (
<div className="grid">
<Field label="Chip ID" description="The ID printed on the package, for example SH-24A-0193.">
<Input placeholder="SH-24A-0000" />
</Field>
<Field label="Notes" required={false}>
<Input placeholder="Anything the next shift should know" />
</Field>
<Field label="Lot" error="Lot L-7799 does not exist.">
<Input defaultValue="L-7799" />
</Field>
<Field label="Email" error={{ message: "Enter an email address.", match: "typeMismatch" }} description="Type a value that is not an email, then leave the field.">
<Input type="email" defaultValue="sam@" />
</Field>
</div>
);
}Label beside the control
FieldGroup orientation="horizontal" puts each label in a column beside its control, also for controls that take label directly. Help text and errors go under the control. An <hr> between two fields draws a line.
Interview invites come from this address.
import { Input, InputArea } from "@etched/monet/input";
import { Select } from "@etched/monet/select";
import { FieldGroup } from "@etched/monet/field";
const zones = { pt: "Pacific (PT)", et: "Eastern (ET)", tw: "Taipei (CST)" };
export function LabelBesideTheControl() {
return (
<FieldGroup orientation="horizontal" style={{ maxInlineSize: "40rem" }}>
<Input label="Display name" defaultValue="Ana Ruiz" />
<Input label="Work email" type="email" defaultValue="ana@etched.ai" description="Interview invites come from this address." />
<hr />
<Select label="Time zone" items={zones} defaultValue="pt" />
<InputArea label="Email signature" labelTooltip="Plain text only. Links are kept." required={false} rows={3} placeholder="Shown under every outreach email" />
</FieldGroup>
);
}Responsive
orientation="responsive" keeps the label beside the control while both fit, and puts it on top in a narrow box, such as a phone screen or a dialog. Drag the box's corner to try it.
import { Input } from "@etched/monet/input";
import { FieldGroup } from "@etched/monet/field";
export function Responsive() {
return (
<div className="card" style={{ boxSizing: "border-box", resize: "horizontal", inlineSize: "40rem", maxInlineSize: "100%", padding: "var(--et-space-4)" }}>
<FieldGroup orientation="responsive">
<Input label="Team name" defaultValue="Silicon bring-up" />
<Input label="Slack channel" defaultValue="#bring-up" error="Channel #bring-up is private. Invite the Orbit bot first." />
</FieldGroup>
</div>
);
}Fieldset
One legend over several fields, with one message for the group. disabled turns off every control in it, for example while the form sends.
import { Input } from "@etched/monet/input";
import { useState } from "react";
import { Button } from "@etched/monet/button";
import { Fieldset } from "@etched/monet/field";
export function Fieldsets() {
const [sending, setSending] = useState(false);
return (
<div className="grid">
<Fieldset legend="LinkedIn and/or email" description="Enter at least one, so we can reach the candidate.">
<Input aria-label="LinkedIn URL" placeholder="linkedin.com/in/…" />
<Input aria-label="Email" type="email" placeholder="name@example.com" />
</Fieldset>
<Fieldset legend="Referrer" error="Enter a LinkedIn URL or an email." disabled={sending}>
<Input aria-label="Referrer LinkedIn URL" placeholder="linkedin.com/in/…" />
<Input aria-label="Referrer email" type="email" placeholder="name@example.com" />
</Fieldset>
<div className="row">
<Button onClick={() => setSending(!sending)}>{sending ? "Stop sending" : "Send"}</Button>
</div>
</div>
);
}Accessibility
Fieldlinks its label to the control, so the label is the control's name.required={false}adds "(optional)" to the label, and so to the name.descriptionanderrordescribe the control (aria-describedby); an error setsaria-invalid. The error is not a live region: screen readers hear it with the control.labelTooltipadds an info button named "More information" (labelTooltipLabel, on aFieldor a control) and described by the tip. It sits beside the label, so the control's name stays clean.Fieldsetis agroup, named by itslegendand described by itsdescriptionorerror.disabledturns off every control in it.- Without
label, give the control anaria-label; in aFieldset, give each control its own name. SetnativeLabel={false}when the control in aFieldis a button.
API reference
FieldGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| orientation | "vertical" | "horizontal" | "responsive" | "vertical" | Where every field inside puts its label, including the fields that controls make from label. |
Field
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | ReactNode | – | The control: Input, InputArea, Select and so on. |
| label | ReactNode | – | Visible label. Leave it out when the control renders its own label. |
| labelTooltip | ReactNode | – | More about the field, behind an info button after the label. Shown on hover and focus; screen readers hear it as the button's description. |
| labelTooltipLabel | string | "More information" | Names the info button. |
| required | boolean | – | false adds the optional marker after the label. |
| optionalLabel | ReactNode | "(optional)" | Text after the label when required is false. |
| description | ReactNode | – | Help text below the control. Hidden while an error shows. |
| error | FieldError | – | |
| nativeLabel | boolean | true | false for button triggers (Select, Combobox): the label names the control without passing clicks or hover to it. |
| orientation | "vertical" | "horizontal" | "responsive" | "vertical", or the orientation of the FieldGroup around it | Where the label goes. |
Fieldset
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | – | |
| legend * | ReactNode | – | The group's title. |
| children * | ReactNode | – | The fields: Field, Input, InputArea, Select and so on. |
| required | boolean | – | false adds the optional marker after the legend. |
| description | ReactNode | – | Help text below the group. Hidden while an error shows. |
| error | ReactNode | – | A message for the whole group, for example "Enter a LinkedIn URL or an email". |
| disabled | boolean | – | Turns off every control in the group, for example while the form sends. |