Browse documentation
On this page
Forms
input, select, textarea, label, and fieldset/legend are styled
directly. This page covers the shared basics; see the sibling pages for
behavior specific to each input type.
Show HTML
<form>
<fieldset>
<label>
Full name
<input type="text" name="name" placeholder="Ada Lovelace" autocomplete="name">
</label>
<label>
Role
<select name="role" required>
<option value="" selected disabled>Select…</option>
<option>Engineer</option>
<option>Designer</option>
<option>Manager</option>
</select>
</label>
<label>
Bio
<textarea name="bio" placeholder="A short bio"></textarea>
<small>Optional, shown on your public profile.</small>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
<form>
<fieldset>
<label>
Full name
<input type="text" name="name" placeholder="Ada Lovelace" autocomplete="name">
</label>
<label>
Role
<select name="role" required>
<option value="" selected disabled>Select…</option>
<option>Engineer</option>
</select>
</label>
<label>
Bio
<textarea name="bio"></textarea>
<small>Optional, shown on your public profile.</small>
</label>
</fieldset>
<button type="submit">Submit</button>
</form>
Layout #
labelis a block, with a bottom margin. Put the control right after its text, inside the samelabel, and it gets a small top margin automatically.- Text inputs,
select, andtextareaare full width and get a bottom margin (--cirth-spacing) so stacked fields space themselves. An explicitsizeattribute (<input size="6">) opts a text input out of the full-width default instead, sizing it to its native, content-based width. textareaopens four rows tall instead of the browser's two, so a field meant for a paragraph shows a paragraph. An explicitrowsattribute (<textarea rows="2">) takes over completely — the default only applies when you haven't said how tall you want it. The four rows are measured inlh, so they stay four rows of this textarea's text if you change its font size.- Controls are at least 44px tall (the WCAG 2.5.5 target size) whatever
font size you set on them — buttons,
selectandtextareaeach hold that floor independently of the type scale. Inside anavthe floor becomes a compact 40px band, still above WCAG 2.5.8's 24px AA minimum. Equivalent one-line controls share the same font, line-height, padding, border and height formula, so an input, select and adjacent button align without per-component offsets. - A
smallimmediately after a form control becomes helper/hint text: block, muted, with a small negative top margin to sit tight under the field. fieldsetis full width with no border/padding of its own;legendmatches label styling.
States #
Show HTML
<form>
<label>
Valid email
<input type="email" value="ada@example.com" aria-invalid="false">
<small>Looks good.</small>
</label>
<label>
Invalid email
<input type="email" value="not-an-email" aria-invalid="true">
<small>Enter a valid email address.</small>
</label>
<label>
Disabled field
<input type="text" value="Read only" disabled>
</label>
<div class="grid" data-readonly-number-example>
<label>
Editable quantity
<input type="number" value="3" min="0" max="10">
</label>
<label>
Readonly quantity
<input type="number" value="3" min="0" max="10" readonly>
</label>
</div>
</form>
<input type="email" value="ada@example.com" aria-invalid="false">
<input type="email" value="not-an-email" aria-invalid="true">
<input type="text" value="Unavailable" disabled>
<input type="number" value="3">
<input type="number" value="3" readonly>
aria-invalid="false"marks the valid state: border in--cirth-form-element-valid-border-color, a check icon, and the adjacentsmallcolored with--cirth-ins-color.aria-invalid="true"marks the invalid state: border in--cirth-form-element-invalid-border-color, an alert icon, and the adjacentsmallcolored with--cirth-del-color.:user-invalidgets the invalid treatment automatically, with noaria-invalidattribute and no JS, once a field has been interacted with and the browser has checked it against its native constraints (required,type="email",pattern, …). Native:user-validstays neutral because satisfying HTML constraints does not necessarily mean that the submitted data is correct. Usearia-invalid="false"when an explicit positive state is useful. Range inputs are excluded from automatic validity styling, and a native-invalidselectstays neutral while focused so merely opening it does not show an error. An explicitaria-invalidalways wins.:focuslifts the recessed field surface back to the canvas, adds an accent border, and adds a box shadow ring in--cirth-form-element-focus-color(or the valid/invalid focus variant when an explicitaria-invalidor matching:user-invalidstate applies).[disabled](or an ancestorfieldset[disabled]) applies opacity--cirth-form-element-disabled-opacity, pointer events off.[readonly]uses a distinct surface and dashed border. A readonly number input also hides its increment/decrement controls, while an editable number input keeps its existing native affordance and behaviour.::placeholderis colored with--cirth-form-element-placeholder-color.
Key tokens #
--cirth-form-element-background-color, -border-color, -color,
-placeholder-color, -focus-color, -active-border-color,
-valid-border-color, -invalid-border-color, -disabled-opacity. See
Customization for how these relate to the semantic color
groups.