Form.
A form is a sheet laid on the drawing: vellum, a hairline frame, a hard offset shadow, and white fields, so an input is the lightest thing on the page and the only white surface on it. Every part comes from one module, ui/site/form.tsx.
Playground.
import { FIELD, LABEL } from "#/ui/site/form";
<label htmlFor={id} className={LABEL}>Work email</label>
<input id={id} type="email" className={cn(FIELD, "mt-2")} />Field.
White, a graphite hairline that darkens on hover, and the redline border with a soft ring on focus. The label is mono uppercase in graphite, a step darker than ambient annotation, because it labels something the visitor has to find. Every field has a visible label; a placeholder is an example, never the label.
Show codeHide code
import { FIELD, LABEL } from "#/ui/site/form";
<label htmlFor={id} className={LABEL}>Company</label>
<input id={id} className={cn(FIELD, "mt-2")} />Chips.
For a choice of several. Selected is the redline border on a redline tint, the site’s one selected look; each chip is a button with aria-pressed.
Which CAD system do you use?
Show codeHide code
import { CHOICE_BASE, CHOICE_OFF, CHOICE_ON } from "#/ui/site/form";
<button type="button" aria-pressed={on} className={cn(CHOICE_BASE, on ? CHOICE_ON : CHOICE_OFF)}>
SolidWorks
</button>Radio tiles.
For exactly one of several, where the options exclude each other. Native radios inside the chip’s tile, accented in the redline, as checkboxes are too.
Drawings a month
Show codeHide code
<label className={cn(CHOICE_BASE, "flex items-center gap-3", on ? CHOICE_ON : CHOICE_OFF)}>
<input type="radio" name="volume" className="accent-redline" />
6-20 / month
</label>Calculator fields.
Where a form is a tool rather than a request.
The calculators set two more, both on the same white box and redline focus: a field whose label rides up inside it once it has a value, on the savings calculator, and a number boxed inside a sentence. They live with their calculators, because a page of numbers is the only place either belongs.
Best practices.
Do
- Give every field a visible label, and use the placeholder only for an example.
- Ask for the least you need. The demo form's first step asks for an email and nothing else.
- Show one error under the fields, in words a person would use, and keep what was typed.
- Use chips for several choices and radio tiles for one.
Don't
- Do not copy the field recipe into a form. Import it from ui/site/form.
- Do not label a field with its placeholder.
- Do not put a field on paper; a form sits on its vellum sheet.
- Do not mark a field red before the visitor has tried to send.