Skip to content

fix(frontend): registration form says what is missing in its own voice - #281

Merged
sabinem merged 1 commit into
mainfrom
style/registration-form-errors
Sep 1, 2026
Merged

fix(frontend): registration form says what is missing in its own voice#281
sabinem merged 1 commit into
mainfrom
style/registration-form-errors

Conversation

@sabinem

@sabinem sabinem commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Somebody signing up for a hackathon left the T-shirt size unanswered and got told off by their browser, not by us. The message came out as a grey system tooltip in a system font — "Please select an item in the list." — pinned over the form, in a page that otherwise puts every label in mono and every warning in the theme's danger colour. Now the same mistake is reported in the app's own voice: a short line under the field ("Choose one of the options.", "Tick this to carry on."), the field's border in danger, and the cursor moved back to the first field that needs an answer. Fix one and its complaint disappears as you answer it.

Before this, the message was literally unstyleable. The tooltip is drawn outside the page by the browser — Chrome removed the pseudo-elements that once reached it, and Firefox and Safari never had any — so no amount of CSS could touch it. The only route to a themed message is to stop the browser from speaking and ask the same validation API ourselves, which is what this does.

  • novalidate is set on the form from JavaScript, not written into the markup. A visitor whose JS never ran keeps the native bubble — ugly, but present — rather than losing the client-side check entirely.
  • Nothing about enforcement changed: every required attribute stays where it was, and the backend refuses a submission with an unanswered mandatory question exactly as before. This only decides who says it and how it looks.
  • The invalid styling keys off aria-invalid, not :user-invalid. With novalidate the browser never runs its own check, so a control the person never touched — the untouched select this was written for — never counts as "user-invalid". The attribute is set by whoever renders the message, so border and sentence cannot disagree.

An unanswered required question was reported by the browser's native
validation bubble — a system tooltip in a system font, sitting on top of
the app's own type and colours, saying "Please select an item in the
list." That bubble is painted outside the page in every current browser
and no stylesheet can reach it, so the only way to theme the message is
to stop the browser drawing it.

The register form now switches `novalidate` on from JavaScript and asks
the same Constraint Validation API itself: the message appears under the
field in the app's own type, the field's border turns danger via a new
`aria-invalid` state on `.field` and `.checkbox`, focus lands on the
first field that failed, and answering a field clears its message.

`novalidate` is set from an action rather than written into the markup,
so a visitor whose JavaScript never ran keeps the native bubble instead
of losing the client-side check altogether. The `required` attributes
are unchanged and the backend still repeats every check.

Only /register/[id] is converted; other forms keep the native bubble.
@sabinem
sabinem merged commit d7be426 into main Sep 1, 2026
2 checks passed
@sabinem
sabinem deleted the style/registration-form-errors branch September 1, 2026 06:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant