Form section
A titled group of self-labelling controls that ends in an action row — the unit intake forms, settings panels, and dialog bodies are assembled from.
Default
A project-details section from an environmental review intake. Each control carries
its own label, help-text and required state — that is what
wires the name to the input inside its shadow root, and it is the whole field chrome, so no
wrapper is involved. Short, related fields (lead agency / county) pair on one row; everything
else runs single column. The action row closes the section: primary submit first, ghost
cancel beside it.
Project details
Tell us about the project under review. Fields marked * are required.
Validation state
The same section after a failed submit: the name is missing and no pathway is selected. Two things happen, and both are needed.
An esa-error-summary goes above the form and takes focus. Without it,
pressing Submit appears to do nothing: focus is on the button at the bottom, the errors are
added off-screen above, and a screen reader — which is on one element at a time — is told
nothing at all. It is also what a page-zoom user, seeing only the region around the button,
gets instead of silence.
Then every invalid control takes error-text. One attribute reddens the border,
sets aria-invalid, points aria-describedby at the message, and
renders it with an icon and a visually-hidden "Error:" so the line is not distinguished from
a help line by colour alone. Option groups take it too now. Word the summary and the inline
message the same.
Project details
Tell us about the project under review. Fields marked * are required.
Composition notes
- Self-chromed controls label themselves.
esa-text-field,esa-textareaand the other Lit controls render into shadow DOM, so a<label>outside them can never be associated with the input inside —esa-form-field'slabelis a visual caption only, and a control wrapped in it with its ownlabelunset has no accessible name. Set the control's ownlabel/help-textand leave the wrapper's unset. Reach foresa-form-field's label only around a native<input>you can wire afor/idpair to yourself, or around a group that has no label of its own. - A failed submit needs an
esa-error-summary, not just red fields. Focus is on the Submit button when the form fails; the errors appear above it, off-screen, and nothing announces them. The summary is the thing that takes focus and says how many problems there are and where each one is. Set itserrorsand callfocus()in the same handler that sets each field'serror-text, and word the two the same. -
Error messages live in components, never ad-hoc styled spans. Every control that collects
a value now takes
error-text— includingesa-radio-groupandesa-checkbox-group, which previously had no error channel at all.esa-field-erroris the escape hatch for a server-rendered message that is already on the page at load. -
The error no longer replaces help text — both render, and
aria-describedbyannounces the error first, then the hint. A format requirement is most needed at the moment the format was got wrong, and the old either/or deleted it exactly then. - The action row belongs to the section, not to any field: primary action first, ghost cancel second, left-aligned on the same axis as the labels, separated from the fields by a divider.
-
Single column by default. Only pair short, related fields on one row, and collapse the pair
on narrow viewports. Controls run full width — the section's
max-widthsets the measure, not the individual control. -
One size across the section: everything defaults to
mdon the shared scale, so inputs, selects, and buttons share heights and line up when they meet on a row.