Field
Label, control, hint and error with the ids wired. Controls inside pick up id, aria-describedby, aria-invalid and required.
Examples
Label and hint
The control takes its id from the Field; no need to repeat it.
Shown to agents in tool lists. Letters, numbers and dashes.
<Field id="server-name" label="Server name" hint="Shown to agents in tool lists. Letters, numbers and dashes.">
<Input name="name" value="github" />
</Field>Required and error
Say what is wrong and how to fix it. The error sits above the hint and both are announced.
Enter a full URL starting with https://.
The MCP endpoint, usually ending in /mcp or /sse.
<Field
id="server-url"
label="Server URL"
required
error="Enter a full URL starting with https://."
hint="The MCP endpoint, usually ending in /mcp or /sse."
>
<Input name="url" value="api.githubcopilot.com/mcp" mono />
</Field>Inline
Label beside the control, for settings pages. Stacks again below 640px.
Tells the router when to pick this server.
<Field id="routing-hint" label="Routing hint" inline hint="Tells the router when to pick this server.">
<Input name="hint" placeholder="Use for GitHub issues, pull requests and code search" />
</Field>Usage
import { Field } from '@switchboard/ui'
<Field id="server-url" label="Server URL" required error={errors.url}>
<Input name="url" type="url" mono />
</Field>Props
Every component also takes mix, for css() and mixins applied after its own style.
Field
| Prop | Type | Default | Description |
|---|---|---|---|
| id* | string | – | Id given to the control. Hint and error ids derive from it: `${id}-hint`, `${id}-error`. |
| label* | RemixNode | – | Rendered as a <label for={id}>. |
| hint | RemixNode | – | Help text under the control, linked with aria-describedby. |
| error | RemixNode | – | Error message with an alert icon. Marks the control aria-invalid and is listed first in aria-describedby. |
| required | boolean | false | Passed to the control as required. |
| inline | boolean | – | Label and control side by side. Stacked by default and below 640px. |
Also accepts every <div> attribute except id. Input, Textarea, InputGroup, SecretInput, Select, Switch and Checkbox read its context; props set on the control win.
Accessibility
- The <label for> points at the control, so clicking the label focuses it and screen readers read it as the name.
- Hint and error are tied to the control with aria-describedby, error first, so they are read on focus.
- An error sets aria-invalid="true" on the control; the message is text with an icon, not colour alone.
- Ids must be unique on the page; when a form appears twice, prefix them.
Guidance
Do
- Write errors that say what is wrong and how to fix it: "Enter a full URL starting with https://."
- Keep labels short nouns; put guidance in the hint.
Don’t
- Use a placeholder as the label.
- Show an error before the person has submitted or left the field.
- Wrap a RadioGroup in a Field; it has its own legend.