Forms
What a form is made of, which components carry which part, and the four decisions that decide whether it can be filled in.
What it solves
A form is the place where a person hands you something and gets told whether it worked. Everything hard about it lives in that second half: where the error appears, when it appears, and whether it says what to do next.
The components below do not decide that for you. They make each of those decisions explicit instead of accidental.
Anatomy
A form has four layers, and skipping one is what makes forms feel wrong rather than look wrong.
- The form owns validation rules and the state of the whole thing —
GrForm. One place decides whether submitting is allowed. - Sections group fields that belong together —
GrFormSection. A long form without them is a list; with them it is a shape you can scan. - Fields bind a label, a control, a hint and an error into one unit —
GrFormField. The label and the error are attached to the control, not placed near it. - Controls take the value —
GrInput,GrSelect,GrCheckboxand the rest of the group.
The order matters in one direction only: a control never owns its own label or error. The moment it does, two fields on the same page start disagreeing about where errors go.
Example
Do and don’t
Do — Error under the field it belongs to
People read the error where they made it. Attach it to the control so a screen reader announces it with the field, not as a separate paragraph somewhere above.
Don’t — A summary of errors at the top
«Fix three fields» above a form of twelve is a search task, not a hint. It works only as an addition to per-field errors, never instead of them — and even then each line must be a link that moves focus to the field.
Do — Validate on blur, re-validate on input
Checking while someone is still typing tells them they are wrong before they have finished being right. Check when they leave the field; once a field is already wrong, re-check on every keystroke so the error disappears the moment it is fixed.
Don’t — Validate on every keystroke from the start
An email field turns red at a, a@, a@b — three lies before the first truth.
The person learns to ignore the colour, which is the one signal you needed.
Do — Say what to do, not what is wrong
«Password needs at least 8 characters» beats «Invalid password»: the first is an instruction, the second is a verdict. The rule that produced the error is known to you — say it.
Don’t — Disable submit until the form is valid
A disabled button explains nothing and cannot be focused: the person is left looking for what is missing. Let them submit, then show what stopped it — that is the moment they are asking the question.
Accessibility
Three things carry most of the weight here, and all three are structural rather than cosmetic.
The label belongs to the control. GrFormField binds them; a label placed
merely next to an input is not a label for anyone who is not looking at it.
The error must be announced, not just shown. It is tied to the control and marked as its description, so it reaches a screen reader when focus arrives — without the person having to hunt for the reason.
Colour is never the only signal. A red border says nothing to a person who cannot see red, and nothing to anyone on a monochrome display. The text of the error is the signal; the colour repeats it.
Nothing on this page replaces testing with a screen reader. The keyboard contracts below come from the library and are checked there; how the whole form reads aloud is a property of your page, not of the components.
Components in this pattern
The list is built from frontmatter, not from links in the prose: renaming a slug does not lose it.
GrFormformsGrFormSectionformsGrFormFieldformsGrInputformsGrSelectformsGrCheckboxformsGrButtonactionsGrAlertfeedback
Keyboard
Contracts come from the library. The pattern does not restate them — it points at them.
GrInputComponent contractGrSelectComponent contractGrCheckboxComponent contract