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-label
Select.folio-select-trigger

The list is the same surface as a menu: 12px radius on --dropdown, 32px rows.

Textarea.folio-textarea
Date.folio-calendar-trigger / .folio-calendar

Never <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-checkbox
tsx
<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.