Forms
Field colours and metrics. Thirty-four files use an input today and there is no shared field component, so this is the largest unowned surface in the frontend.
What this replaces
The frontend has 34 files containing an <input>, 18 with a <form>, 7 with a <select> and 5 with a <textarea>, and no shared field component anywhere. Search, newsletter, contact, feedback, login, register, reset, settings and the two calculators each style their own controls.
These are the tokens that come first. The field component itself lands with the forms wave, but nothing should be built against ad-hoc values in the meantime.
Colours
--p234-color-field-bg
Input, select and textarea background.
--p234-color-field-border
Resting border. Non-text, needs 3:1, which is what makes the control visible at all.
--p234-color-field-border-hover
--p234-color-field-ink-placeholder
Placeholder text. Still 4.5:1, because a placeholder nobody can read is not a hint.
--p234-color-field-bg-disabled
Two of these carry a specific obligation. field-border is checked at 3:1 against the page, which is what makes an empty control visible at all; a hairline at 1.5:1 is a box sighted readers find and nobody else does. field-ink-placeholder is checked at 4.5:1 against the field, because a placeholder nobody can read is not a hint.
Metrics
--p234-field-height | 2.75rem | 44px, the touch target floor. Most readers are on a phone. | |
--p234-field-padding-x | 0.75rem | ||
--p234-field-radius | 4px | radius.md. Form controls are the one place a visible radius helps, because it separates the control from the rule grid. | |
--p234-field-border-width | 1px | ||
--p234-field-border-width-invalid | 2px | An invalid field thickens its border as well as changing colour, so the state is not carried by colour alone. |
field.height is 44px and a test fails the build below that. Most readers are on a phone, and a control that needs a careful tap is a control that gets missed.
Validation is never colour alone
An invalid field changes colour and thickens its border, from 1px to 2px, and carries a message in text. A test asserts the two widths differ, so the colour-only version cannot ship by accident.
Resting, focused, invalid, disabled
Enter an email address in the form you@example.com
Tab through these to see the shared focus ring, and switch themes to check both.
HTML
<div style="display:grid;gap:var(--p234-space-5);max-width:24rem">
<label style="display:block">
<span style="display:block;font-family:var(--p234-type-ui-sm-family);font-size:var(--p234-type-ui-sm-size);font-weight:600;margin-bottom:var(--p234-space-3)">Email address</span>
<input type="email" placeholder="you@example.com" style="width:100%;height:var(--p234-field-height);padding:0 var(--p234-field-padding-x);background:var(--p234-color-field-bg);color:var(--p234-color-ink);border:var(--p234-field-border-width) solid var(--p234-color-field-border);border-radius:var(--p234-field-radius);font-family:var(--p234-type-ui-family);font-size:var(--p234-type-ui-size)">
</label>
<label style="display:block">
<span style="display:block;font-family:var(--p234-type-ui-sm-family);font-size:var(--p234-type-ui-sm-size);font-weight:600;margin-bottom:var(--p234-space-3)">State</span>
<select style="width:100%;height:var(--p234-field-height);padding:0 var(--p234-field-padding-x);background:var(--p234-color-field-bg);color:var(--p234-color-ink);border:var(--p234-field-border-width) solid var(--p234-color-field-border);border-radius:var(--p234-field-radius);font-family:var(--p234-type-ui-family);font-size:var(--p234-type-ui-size)">
<option>Lagos</option><option>Abuja</option><option>Kano</option>
</select>
</label>
<div>
<label style="display:block">
<span style="display:block;font-family:var(--p234-type-ui-sm-family);font-size:var(--p234-type-ui-sm-size);font-weight:600;margin-bottom:var(--p234-space-3)">Email address</span>
<input type="email" value="not-an-address" aria-invalid="true" aria-describedby="err" style="width:100%;height:var(--p234-field-height);padding:0 var(--p234-field-padding-x);background:var(--p234-color-field-bg);color:var(--p234-color-ink);border:var(--p234-field-border-width-invalid) solid var(--p234-color-error);border-radius:var(--p234-field-radius);font-family:var(--p234-type-ui-family);font-size:var(--p234-type-ui-size)">
</label>
<p id="err" style="font-family:var(--p234-type-meta-family);font-size:var(--p234-type-meta-size);color:var(--p234-color-error);margin:var(--p234-space-3) 0 0">Enter an email address in the form you@example.com</p>
</div>
<label style="display:block">
<span style="display:block;font-family:var(--p234-type-ui-sm-family);font-size:var(--p234-type-ui-sm-size);font-weight:600;margin-bottom:var(--p234-space-3);color:var(--p234-color-ink-muted)">Country (set by your plan)</span>
<input value="Nigeria" disabled style="width:100%;height:var(--p234-field-height);padding:0 var(--p234-field-padding-x);background:var(--p234-color-field-bg-disabled);color:var(--p234-color-ink-muted);border:var(--p234-field-border-width) solid var(--p234-color-rule);border-radius:var(--p234-field-radius);font-family:var(--p234-type-ui-family);font-size:var(--p234-type-ui-size)">
</label>
</div>
<p style="font-family:var(--p234-type-caption-family);font-size:var(--p234-type-caption-size);color:var(--p234-color-ink-subtle);margin-top:var(--p234-space-5)">Tab through these to see the shared focus ring, and switch themes to check both.</p>Rules
- Every control has a real
<label>. A placeholder is not a label: it disappears exactly when the reader needs it. - Mark invalid fields with
aria-invalidand point at the message witharia-describedby. - Say what to do, not what went wrong. "Enter an email address in the form you@example.com" beats "Invalid input".
- Keep the 4px radius. Form controls are the one place a visible radius helps, because it separates the control from the rule grid around it.
- Do not rely on the browser's default focus ring. The system has one; see Focus.
- Do not shrink a control below 44px to fit a layout. Change the layout.
- Do not put the error message only in a tooltip or a colour change.