Skip to main content
Executive Support by Beige Threat

Forms

Forms aren’t decorative. Get the label right, the helper right, the error right – the rest is restraint. Every control on the site composes from a small set of primitives bound to the same tokens; the work is making each field obvious and the form as a whole easy to finish.

Anatomy

Five parts. The bottom three are optional. Every Field carries label, control, and – when the moment calls for it – helper text, an error, and a required marker. The wiring is <label for> to control id, aria-describedby to helper or error, aria-required and aria-invalid for state.

123We use this for the monthly digest. Never shared.
  1. 1
    Label – always visible, sentence case. Required marker is a single asterisk in the danger token; “(optional)” is the inverse mark.
  2. 2
    Control – one of six primitives. 40px target, 1px border, 4px radius, teal focus ring.
  3. 3
    Helper – one line. Formatting hints, privacy notes. Replaced by error text when the field is invalid.
  4. 4
    Error – single line, plain, owns the problem. Linked to the control via aria-describedby.
  5. 5
    Required marker – visual asterisk is decorative; the real signal is aria-required=“true”.

Field controls

Three text primitives plus a select. Default height is 40px. Compact forms (settings rows, table inline edits) use 32px. Long-form composition fields use 48px. Padding is 10px / 14px, border is 1px, radius is --es-radius-control (4px). The focus ring is the universal teal pair from the states foundation – never a custom colour.

Input · text
Input · email
Input · password
Textarea
Select · native
Input · number

Control states

Six surfaces. Hover lifts the border to --es-color-stone-500; focus pairs the teal border with the universal focus ring; error reuses the danger token; disabled drops to the sunken surface; read-only sits on cream-tinted stone-50 and stays selectable.

Default
Hover
Focus
Error
Use the format name@org.com.
Disabled
Read-only

Field meta

Label, helper, error, and required marker. They’re separate jobs and the page treats them that way. A field never shows helper and error at the same time – the error replaces the helper for the duration of the problem.

Field meta
Label
Inter 13 / 600 · sentence case · always visible

Sits above the control. Never substituted by a placeholder. UK spelling.

Required marker
aria-hidden asterisk · feedback-danger-text

Decorative; the real signal is `aria-required` on the control. Default is required – mark optional fields, not required ones.

Work email
Helper
Inter 12 · stone-700 · one line

Formatting hint, scope, or privacy note. Linked via `aria-describedby`. Replaced by the error when the field is invalid.

We use this for the monthly digest. Never shared.
Error
Inter 12 · feedback-danger-text · one line

Owns the problem and points to the fix. Lives with the field – not at the top of the form. Linked via `aria-describedby`; pair with `aria-invalid='true'`.

Use the format name@org.com.

Selection controls

Checkbox, radio, and switch. All three have real keyboard support, real focus rings, and the same ticked-teal accent. Use checkbox for independent options, radio for one-of-many, switch for a setting that takes effect immediately.

Checkbox group
Topics
Radio group
Membership
Switch
Single checkbox · consent

Field groups

Related fields belong to a <fieldset> with a visible <legend>. The legend acts as the group’s label – screen readers announce it with each control. Vertical rhythm between fields uses --es-spacing-stack-md (16px). Tightly related fields – the parts of a name, the lines of an address – tighten to --es-spacing-stack-sm (12px).

Billing address

Form layouts

Single column, always. The eye reads top-to-bottom; the label sits directly above the control; the control runs the full width of the form column. Two columns are reserved for genuinely paired fields – first and last name, city and postcode – and only at the --es-bp-md breakpoint and above. On mobile, everything stacks.

Do · Single column, label above
Full name
Work email

Labels above. Controls full-width. The eye runs straight down the form and finishes faster.

Don't · Side-labelled or two-column
Full name
Work email

Side-labelled forms force the eye to ping-pong. They look tidy in mockups and read poorly in use.

Validation

Validate on submit by default. For high-cost fields – email, billing details, anything that hits a paid API – validate on blur, after the first interaction. Never validate on every keystroke; that’s not feedback, that’s nagging.

TriggerWhat surfacesWhy
Submit – any formEvery invalid field shows its error. Focus moves to the first invalid control.One source of truth at submit. Keyboard users land where they need to act.
Blur – high-cost fields, after first interactionField-level error appears below the control.Email, card numbers, and anything we can’t undo. Catch it once they leave the field, not while they’re typing.
Retry – refocus and editError clears as soon as the input is valid.Reward the correction. Don’t make them blur again to confirm.
Server – network, auth, policyForm-level banner above the form, plus field-level when attributable.A 503 isn’t the email field’s fault. Say what’s true; offer a path forward.

A specimen form

The membership sign-up. Real fields, real labels, real focus rings – everything composes from the primitives above.

Become a member

Eight issues a year, the digital archive, and quarterly Leaders calls.

For the monthly digest. Never shared.
Cancel

You’ll be charged today. No contract – cancel from your profile.

Usage rules

Do · Label above the control
Work email

Labels stay visible. The placeholder describes format, not purpose.

Don't · Placeholder as label

Placeholders disappear the moment someone types. The label needs to stay.

Do · Error below the field
Work email
Use the format name@org.com.

Error sits with the field that owns it. The eye finds it on the way down.

Don't · Error above the form
There were errors. Please review the form.
Work email

Top-of-form banners hide which field is wrong. Reserve them for server errors.

Do · Primary on the right
Cancel

Primary on the right. Cancel as a link on the left – never a competing button.

Don't · Two equal buttons

Two outline buttons make the user choose between equals. They aren’t equals.

Do · Mark optional, default to required
Work email
Display name (optional)

Most fields on a form are required. Mark the few that aren’t.

Don't · Asterisks on every field
Work email *
Full name *
Plan *

A column of asterisks is noise. If everything is required, mark nothing.

Do · Helper or error – never both
Work email
Use the format name@org.com.

The error replaces the helper while the field is invalid. One line, one job.

Don't · Disabled submit instead of validating
(button stays grey until valid)

A disabled submit hides what’s wrong. Let them press it; show the errors; move focus to the first one.

Tokens to ship

Every value above resolves to an existing token. The component layer never reaches into a ramp directly.

Field control
--es-radius-control
4px (--es-radius-sm)

Corner radius for inputs, textareas, selects, and buttons. Sits beside cards which share the same value.

--es-spacing-inset-md
16px / 10px-14px

Vertical padding 10px and horizontal 14px, aligned to the inset scale. Hits the 40px target with 13–14px text.

10 / 14
--es-focus-ring
2px paper offset · 2px teal-600

Universal focus ring from the states foundation. Bound to every focusable control on the page.

--es-color-stone-300
Default border · #D3D1CA

Resting border for every control. Lifts to stone-500 on hover; replaced by teal-600 on focus.

--es-state-disabled-bg
stone-100 · #F6F3EC

Disabled control background. Pairs with stone-500 text and stone-200 border.

--es-state-readonly-bg
stone-50 · #FCFAF3

Read-only background. Distinct from disabled – the value can still be selected and copied.

Field meta and error state
--es-color-feedback-danger-text
#530203

Error message colour and the visual asterisk. Pairs with feedback-danger-border on the control.

Use the format name@org.com.
--es-color-feedback-danger-border
#AF1113

Border colour for invalid controls. Reinforced by a 1px outer shadow at the same hue.

--es-spacing-stack-md
16px (--es-space-4)

Default vertical rhythm between fields in a form.

--es-spacing-stack-sm
12px (--es-space-3)

Tighter rhythm for visually paired fields – name parts, address lines.