Skip to content

Field

We only use it for billing receipts.

<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>
ClassPart
.fieldOne label, control and its help text
.field-groupSeveral fields stacked, and the container a responsive field measures
.field-setA semantic <fieldset> with the same spacing
.field-legendIts <legend>; data-variant="legend" or "label" sizes it
.field-contentGroups title and description when the label sits beside the control
.field-titleTitle with label styling inside .field-content
.field-descriptionHelp text
.field-errorValidation message
.field-separatorDivider between sections
AttributeOnMeaning
data-orientation="horizontal".fieldLabel beside the control
data-orientation="responsive".fieldStacked, switching at the group’s width
data-invalid.fieldMarks the whole block as in error
data-disabled.fieldDims the block; a disabled control does this on its own

Description sits under the control.

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.

Contact

Optional, for delivery updates only.


Used in URLs, so keep it short.

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.

Billing address

That postcode is not valid.

A summary of what changed, every Monday.

Plan

Helps us find which features go unused.

Whole steps from 0 to 10.

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.

When the label sits beside the control, .field-content keeps the title and description together on one side.

Weekly digest

A summary of what changed, every Monday.


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.

Enter a valid email address.

This one is fine.

Three attributes do the work, and each has a different job:

AttributeOnDoes
aria-invalid="true"the controlAnnounces the control as invalid; recolours border and ring
aria-describedbythe controlPoints at the message so it is read with the control
data-invalidthe .fieldCarries 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.

Contact an owner to change this.

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.

  • Every control needs a name. A <label> with for is the first choice; aria-label when 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.