The exact problem: a form is technically accessible — every message is associated, announced and visible — and readers still abandon it, because every message describes the rule that failed rather than the action that would succeed.
Context and Prerequisites
This is the copy half of error summary and messaging. The wiring is assumed: messages reach the field through aria-describedby, and the summary reads the same strings. Nothing here changes any of that. What changes is what the strings say.
Message quality is the one part of the error pipeline no automated check can catch. Contrast, association and announcement are all testable; “does this sentence tell the reader what to type” is not.
The Four Properties of a Message That Works
/**
* A message is a sentence the reader can act on. Each property below removes
* one reason they cannot: not knowing which field, not knowing what is wrong,
* not knowing what would be right, or not being able to see it as an error.
*/
interface Message {
/** 1. Names the field, so it stands alone in a summary. */
readonly subject: string; // "Post code"
/** 2. States what to do, in the imperative. */
readonly action: string; // "Enter a post code, for example M1 4AB"
/** 3. Never blames. No "invalid", no "you must", no "error:". */
readonly tone: 'neutral';
/** 4. Carries meaning without colour — the text alone is enough. */
readonly code: string; // for analytics, not for the reader
}
Name the subject. In a summary the message appears without its field, so “Enter a value” is unusable. Post code — enter a post code, for example M1 4AB reads correctly in both places.
Say what to do. “Invalid format” describes the validator’s conclusion. “Enter a post code, for example M1 4AB” describes the reader’s next keystroke. The example is doing more work than the rule name ever could.
Do not blame. “You entered an invalid email” makes a mistake into an accusation. “Enter an address we can reach you at” is the same information with no accusation in it — and it also explains why the field exists.
Do not rely on colour. Red text with no words that indicate a problem fails for anyone who cannot distinguish it, including anyone reading in bright sunlight. The sentence itself must be recognisable as a problem.
Step-by-Step Walkthrough
-
Write the message where the rule is. A rule and its message belong in the same place, so changing one prompts changing the other. A message table keyed by error code, far from the schema, drifts.
-
Start with the field label. Then a dash, then the action. This reads correctly beside the field and inside the summary without a second string.
-
Give an example for anything with a format. Post codes, card numbers, reference numbers, dates. An example removes an entire class of guessing.
-
Explain the requirement where it is not obvious. “Phone number — so we can text you the delivery code” answers the question a required phone field always raises.
-
Keep it to one sentence. A message that needs two sentences is usually a field that needs a hint. Hints appear before the reader types; messages appear after.
-
Map server codes to your own copy. A rejection written for an operator does not become reader-facing just because it arrives in a
detailfield.
Failure Modes and Edge Cases
1. The message is longer than the answer
“Please ensure that the value entered conforms to the required format” is nineteen words to say “use DD/MM/YYYY”. Long messages are skimmed, and skimmed messages are unread.
2. The same message on every field
A generic “This field is required” repeated eight times makes a summary that says nothing eight times. Per-field wording costs a line each and makes the summary a map rather than a wall.
3. Blaming for a system failure
“Something went wrong with your submission” for a gateway timeout implies the reader did something. “We could not reach the service — try again” puts it where it belongs, and is also more accurate.
4. Copy that assumes sighted, sequential reading
“See above” and “the field below” are meaningless when the message is read out on its own, from a summary, or on a narrow screen where the layout has reflowed. Reference fields by their labels.
5. Translated messages with concatenated fragments
Building a message from label + " " + rule produces sentences that are ungrammatical in most languages other than English. Keep whole sentences as translatable units, with the label interpolated.
A short review pass catches most of it, and it can be done without opening the form:
Verification Checklist
Related
- Error Summary and Messaging — where these sentences are read twice
- Building an Accessible Error Summary — the structure that amplifies them
- Wiring aria-describedby for Multiple Errors — how the message reaches the reader
Frequently Asked Questions
Should the message go above or below the field?
Below the label and above the input, or immediately below the input — either works, as long as it is inside the element the field is described by and it does not move the input when it appears. What matters more than position is that the message is in the accessibility tree via aria-describedby, because that is what determines whether it is read at all; visual position only affects sighted readers, who will find it either way if it is close.
Is it worth writing per-field required messages?
Yes. Eight fields sharing ‘This field is required’ produce a summary that lists the same sentence eight times, which tells the reader nothing about which field to go to or why it matters. Per-field wording — ‘Phone number, so we can text you the delivery code’ — turns the summary into a map and answers the question a required field always raises.
What about messages that come from the server?
Treat the server’s wording as a fallback and map its stable code to copy you control. Server messages are written for whoever reads the logs, may be untranslated, and often leak internal vocabulary. Falling back to them when a code is unrecognised is still right — an imperfect message beats a silent failure — but the mapped copy should be what readers normally see.