What we write down when we decide something
A decision that only exists in a conversation gets re-argued every few months. Writing it down is cheaper than remembering it.
A consent checkbox that starts checked is not consent. A few small decisions separate a form that asks from one that assumes.
1 min read
Written by
Seratlas Team
Engineering and delivery
1 min read
We build and operate assessment software. We write here about the parts that are harder than they look.
The contact form on this site has six fields and a checkbox. It took longer to get right than several pages of layout, because most of the decisions in a form are about what happens when something goes wrong.
This is the only lawful starting state, and it has a consequence in the code: the
validation schema cannot narrow the field's type to true, because then the
unchecked default fails to typecheck. So the rule is expressed as a check on a
boolean rather than as a literal.
That is a small thing, but it is the kind of small thing where the convenient type and the correct behaviour point in opposite directions, and the type wins by default unless someone notices.
Each message sits next to its field, referenced by aria-describedby, and is
announced when it appears. A summary box at the top of a long form is a second
place to maintain and a second thing to keep in sync with the fields; worse, on a
narrow screen it scrolls out of view before the reader reaches the field it is
describing.
If the submission endpoint is not configured, the form does not render and throw at the moment someone presses the button. It renders a mail link instead, with the subject prefilled, because the visitor's goal is to reach us and not to use our form.
If the request fails, the error stays on the page. We deliberately do not use a toast for this: an auto-dismissing message is indistinguishable from nothing having happened, and the reader's next move is to submit again.
It is positioned off-screen, hidden from assistive technology, and removed from
the tab order. display: none is the one approach that a competent bot skips and
some autofill implementations still populate, which produces the exact inversion
you were trying to avoid: real people rejected, scripts let through.
A decision that only exists in a conversation gets re-argued every few months. Writing it down is cheaper than remembering it.
What this site is for, what is still missing from it, and why we would rather publish it in that state than wait.
A reading-time estimate is a guess presented as a fact. That is fine, as long as the guess is not wrong in a way the reader will notice.
Removing the server removes most of the classic vulnerabilities and moves the remainder into the build, the bucket, and the third parties you embed.
We would rather answer a direct question than write a follow-up post nobody asked for.