Field
We only use it for billing receipts.
<div class="field"> <label class="label" for="field-email">Email</label> <input class="input" id="field-email" type="email" placeholder="you@example.com" /> <p class="field-description">We only use it for billing receipts.</p></div><div class="field"> <label class="label" for="email">Email</label> <input class="input" id="email" type="email" /> <p class="field-description">We only use it for billing receipts.</p></div>Composition
Section titled “Composition”| Class | Part |
|---|---|
.field | One label, control and its help text |
.field-group | Several fields stacked, and the container a responsive field measures |
.field-set | A semantic <fieldset> with the same spacing |
.field-legend | Its <legend>; data-variant="legend" or "label" sizes it |
.field-content | Groups title and description when the label sits beside the control |
.field-title | Title with label styling inside .field-content |
.field-description | Help text |
.field-error | Validation message |
.field-separator | Divider between sections |
| Attribute | On | Meaning |
|---|---|---|
data-orientation="horizontal" | .field | Label beside the control |
data-orientation="responsive" | .field | Stacked, switching at the group’s width |
data-invalid | .field | Marks the whole block as in error |
data-disabled | .field | Dims the block; a disabled control does this on its own |
Anatomy
Section titled “Anatomy”Description sits under the control.
<div class="field"> <label class="label" for="anatomy-input">Label</label> <input class="input" id="anatomy-input" aria-describedby="anatomy-description" /> <p class="field-description" id="anatomy-description">Description sits under the control.</p></div>Three parts, in this order: the label names the control, the control is the control, and the
description explains it. aria-describedby is what ties the third to the second — the layout alone
does not.
<form class="field-group"> <fieldset class="field-set"> <legend class="field-legend" data-variant="label">Contact</legend>
<div class="field"> <label class="label" for="form-email">Email</label> <input class="input" id="form-email" type="email" required /> </div>
<div class="field"> <label class="label" for="form-phone">Phone</label> <input class="input" id="form-phone" type="tel" /> <p class="field-description">Optional, for delivery updates only.</p> </div> </fieldset>
<hr class="field-separator" />
<div class="field"> <label class="label" for="form-notes">Notes</label> <textarea class="textarea" id="form-notes"></textarea> </div>
<button class="btn" type="submit">Save</button></form>Used in URLs, so keep it short.
<div class="field"> <label class="label" for="field-input">Project name</label> <input class="input" id="field-input" placeholder="partialkit" /> <p class="field-description">Used in URLs, so keep it short.</p></div>Textarea
Section titled “Textarea”<div class="field"> <label class="label" for="field-textarea">Description</label> <textarea class="textarea" id="field-textarea" placeholder="What does it do?"></textarea></div>Select
Section titled “Select”<div class="field"> <label class="label" for="field-select">Visibility</label> <select class="native-select" id="field-select"> <option>Private</option> <option>Public</option> </select></div>Fieldset
Section titled “Fieldset”A <fieldset> groups related fields and its <legend> names the group. Screen readers announce
that name with every control inside, which a heading would not do.
<fieldset class="field-set"> <legend class="field-legend" data-variant="label">Billing address</legend>
<div class="field"> <label class="label" for="fieldset-street">Street</label> <input class="input" id="fieldset-street" /> </div>
<div class="field" data-invalid> <label class="label" for="fieldset-postcode">Postcode</label> <input class="input" id="fieldset-postcode" value="000" aria-invalid="true" aria-describedby="fieldset-postcode-error" /> <p class="field-error" id="fieldset-postcode-error">That postcode is not valid.</p> </div></fieldset>Checkbox
Section titled “Checkbox”A summary of what changed, every Monday.
<div class="field" data-orientation="horizontal"> <input class="checkbox" type="checkbox" id="checkbox-newsletter" /> <div class="field-content"> <label class="field-title" for="checkbox-newsletter">Weekly digest</label> <p class="field-description">A summary of what changed, every Monday.</p> </div></div><fieldset class="field-set"> <legend class="field-legend" data-variant="label">Plan</legend>
<div class="radio-group"> <label class="label"> <input class="radio" type="radio" name="plan" value="free" checked /> Free </label> <label class="label"> <input class="radio" type="radio" name="plan" value="pro" /> Pro </label> <label class="label"> <input class="radio" type="radio" name="plan" value="team" /> Team </label> </div></fieldset>Switch
Section titled “Switch”Helps us find which features go unused.
<div class="field" data-orientation="horizontal"> <div class="field-content"> <label class="field-title" for="switch-analytics">Share anonymous usage</label> <p class="field-description">Helps us find which features go unused.</p> </div> <input class="switch" type="checkbox" role="switch" id="switch-analytics" checked /></div>Slider
Section titled “Slider”Whole steps from 0 to 10.
<div class="field"> <label class="label" for="slider-quality">Quality</label> <input class="slider" type="range" id="slider-quality" min="0" max="10" step="1" value="7" /> <p class="field-description">Whole steps from 0 to 10.</p></div>Choice Card
Section titled “Choice Card”A label wrapping a whole field becomes a selectable card: the border, the hover and the checked highlight all come from the control inside it.
<div class="radio-group"> <label class="label"> <div class="field" data-orientation="horizontal"> <input class="radio" type="radio" name="tier" value="starter" checked /> <div class="field-content"> <span class="field-title">Starter</span> <p class="field-description">One project, community support.</p> </div> </div> </label>
<label class="label"> <div class="field" data-orientation="horizontal"> <input class="radio" type="radio" name="tier" value="team" /> <div class="field-content"> <span class="field-title">Team</span> <p class="field-description">Unlimited projects, priority support.</p> </div> </div> </label></div>Field Content
Section titled “Field Content”When the label sits beside the control, .field-content keeps the title and description together
on one side.
A summary of what changed, every Monday.
<div class="field" data-orientation="horizontal"> <div class="field-content"> <span class="field-title">Weekly digest</span> <p class="field-description">A summary of what changed, every Monday.</p> </div> <select class="native-select" aria-label="Weekly digest" style="width: auto"> <option>On</option> <option>Off</option> </select></div>Field Group
Section titled “Field Group”<div class="field-group"> <div class="field"> <label class="label" for="group-name">Project name</label> <input class="input" id="group-name" placeholder="partialkit" /> </div> <div class="field"> <label class="label" for="group-visibility">Visibility</label> <select class="native-select" id="group-visibility"> <option>Private</option> <option>Public</option> </select> </div> <hr class="field-separator" /> <div class="field"> <label class="label" for="group-notes">Notes</label> <textarea class="textarea" id="group-notes" placeholder="Anything worth remembering"></textarea> </div></div>Orientation
Section titled “Orientation”<div class="field" data-orientation="horizontal"> <label class="label" for="orientation-plan">Plan</label> <select class="native-select" id="orientation-plan"> <option>Free</option> <option>Pro</option> </select></div>
<div class="field" data-orientation="horizontal"> <label class="label" for="orientation-seats">Seats</label> <input class="input" id="orientation-seats" type="number" value="3" /></div>Responsive Layout
Section titled “Responsive Layout”data-orientation="responsive" starts stacked and switches to a row at the container’s width —
a container query, so it reacts to its own column rather than the viewport. It needs a
.field-group around it to measure.
<div class="field-group"> <div class="field" data-orientation="responsive"> <label class="label" for="responsive-name">Name</label> <input class="input" id="responsive-name" /> </div> <div class="field" data-orientation="responsive"> <label class="label" for="responsive-role">Role</label> <select class="native-select" id="responsive-role"> <option>Developer</option> <option>Designer</option> </select> </div></div>Validation and Errors
Section titled “Validation and Errors”Enter a valid email address.
This one is fine.
<div class="field-group"> <div class="field" data-invalid> <label class="label" for="validation-email">Email</label> <input class="input" id="validation-email" value="not-an-email" aria-invalid="true" aria-describedby="validation-email-error" /> <p class="field-error" id="validation-email-error">Enter a valid email address.</p> </div>
<div class="field"> <label class="label" for="validation-name">Name</label> <input class="input" id="validation-name" value="Ada" /> <p class="field-description">This one is fine.</p> </div></div>Three attributes do the work, and each has a different job:
| Attribute | On | Does |
|---|---|---|
aria-invalid="true" | the control | Announces the control as invalid; recolours border and ring |
aria-describedby | the control | Points at the message so it is read with the control |
data-invalid | the .field | Carries the error colour to the label |
<div class="field" data-invalid> <label class="label" for="email">Email</label> <input class="input" id="email" aria-invalid="true" aria-describedby="email-error" /> <p class="field-error" id="email-error">Enter a valid email address.</p></div>Since these are plain attributes, a server-rendered form needs no client-side state — the same markup describes the error on the first paint.
Disabled
Section titled “Disabled”Contact an owner to change this.
<div class="field"> <label class="label" for="field-locked">Workspace</label> <input class="input" id="field-locked" value="acme" disabled /> <p class="field-description">Contact an owner to change this.</p></div>Set dir="rtl" on the page or on any subtree — padding, corners and positions follow the reading
direction on their own. See the button page for a live example.
Accessibility
Section titled “Accessibility”- Every control needs a name. A
<label>withforis the first choice;aria-labelwhen there is no visible label. - Tie the description to the control with
aria-describedby, or it is just text nearby. - Group related fields in a
<fieldset>with a<legend>rather than a heading. - Do not disable your way out of a problem. A disabled control is not focusable, so a keyboard user cannot reach it to find out why it is off. Explain it in a description instead.