Form controls
The form primitives. Field owns the label/hint/error and wires accessibility onto its single
child control; the controls themselves stay native where possible (real keyboard + AT behavior).
Where the order routes
Notes are required
Wraps one control and wires id β label, aria-describedby (hint + error), and aria-invalid.
import { Field, Input, Select } from '@repo/ui/form';
<Field label="Branch" hint="Where the order routes" error={errors.branch}> <Select value={branch} onChange={e => setBranch(e.target.value)}> <option value="hq">HQ</option> </Select></Field>| Prop | Type | Notes |
|---|---|---|
label |
string |
Rendered as the eyebrow-style <label htmlFor> |
hint |
string |
Helper text; hidden when error is set |
error |
string |
Sets aria-invalid + error styling on the child |
required |
boolean |
Adds the * marker |
id |
string |
Optional; auto-generated via useId otherwise |
Controls
Section titled βControlsβInputβ styled native<input>(import { Input } from '@repo/ui/form'for React;@repo/ui/Inputexports the Astro component).Selectβ styled native<select>with the brand chevron. All<select>props.Textareaβ styled native<textarea>. All<textarea>props.Checkbox/Radioβ native inputs tinted viaaccent-color; optional inlinelabel. Wrap radios inRadioGroup(role=radiogroup,orientation).Switchβrole=switchtoggle. Controlled:checked+onCheckedChange. Keyboard via the underlying button (space/enter).
import { Input, Checkbox, RadioGroup, Radio, Switch } from '@repo/ui/form';
<Input placeholder="PO-10492" value={poNumber} onChange={e => setPoNumber(e.target.value)} /><Checkbox label="Mark as test order" checked={isTest} onChange={β¦} /><RadioGroup label="Tier" orientation="horizontal"> <Radio name="tier" value="1" label="Tier 1" /></RadioGroup><Switch checked={on} onCheckedChange={setOn} label="Email notifications" />Do / donβt
Section titled βDo / donβtβ- Do wrap every control in
Fieldso the label and error are wired for screen readers. - Do pass an accessible name to
Switch(label) β it has no text of its own. - Donβt use a
Switchfor choosing between options β thatβsRadioGroup. A switch is an immediate on/off. - Donβt hand-style raw
<input>s; reuse these so focus rings and error states stay consistent.
Accessibility
Section titled βAccessibilityβ- Native controls keep native keyboard + AT semantics;
Switchuses a real button withrole=switch+aria-checked. Focus-visible ring uses--om-orange-press(β₯3:1 on light). - Verified with
jest-axe(zero violations).