Forms
One geometry for the field and the select: 40px tall, 12px corners, 18px side padding, a whisper of bottom shadow and an inset focus ring. They are peers in a form and constantly sit adjacent, so they must be identical.
Input and Label
.folio-input / .folio-labelSelect
.folio-select-triggerThe list is the same surface as a menu: 12px radius on --dropdown, 32px rows.
Textarea
.folio-textareaDate
.folio-calendar-trigger / .folio-calendarNever <input type="date">: it renders as a different control in every browser, so a form built from it cannot look native anywhere.
Switch and Checkbox
.folio-switch / .folio-checkboxtsx
<div className="folio-form-row"> {/* two-up, collapses under 560px */}
<div className="folio-field">
<Label htmlFor="name">Name</Label>
<Input id="name" />
</div>
<div className="folio-field">
<Label>Type</Label>
<Select value={kind} onValueChange={setKind}>
<SelectTrigger><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="payment">Payment</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="folio-field">
<Label>Start date</Label>
{/* YYYY-MM-DD in and out, '' for empty. min/max bound the grid;
clearable={false} drops the Clear action; portalled={false} inside
a popup body, whose scroll lock swallows a portalled panel. */}
<DatePicker value={date} onChange={setDate} min={todayStr()} />
</div>Rules
- Labels above fields, 14px medium, 4px gap. Never placeholder-as-label.
- Group related fields in a
.folio-form-row; one column under 560px. - Errors go in an error note under the form or beside the field, never as a red border alone.
- A create or edit flow is a full page. Popups are for confirmations and short forms.