Before a person types a single character, a form has already made promises about each
field: this one is mandatory, that one wants a particular format, this one holds your
own phone number. When those promises live only in the visual layer — a red asterisk a
screen reader skips, a hint inside a placeholder that evaporates on the first
keystroke, a name field the browser has no way to recognise — the user is left
guessing. They submit, hit an error they were never warned about, and re-type data the
browser already knew.
This lesson fixes three of the most common expectation defects. None of them needs new
components or heavy ARIA: the required state belongs in the markup with
required, instructions belong in persistent text tied to the field, and a
field's purpose belongs in a standard autocomplete token. Wire those three
in and the form tells everyone the same thing at the same time.
What you'll learn
How to mark a field required so it's announced as well as seen,
without leaning on colour alone; how to give a field a persistent instruction tied
with aria-describedby instead of a vanishing placeholder; and how to
declare a personal-data field's purpose with the correct autocomplete
token so it satisfies Identify Input Purpose and autofills for everyone.
Standards this lesson maps to
Standard
Criterion
Level
What it requires
WCAG 2.2
3.3.2 Labels or Instructions
A
Labels or instructions are provided when content requires user input, including which fields are required and what format is expected.
WCAG 2.2
1.4.1 Use of Color
A
Colour is not the only visual means of conveying information — a required state can't be signalled by red alone.
WCAG 2.2
1.3.1 Info and Relationships
A
The required state and any instruction are conveyed in markup, not by visual presentation alone.
WCAG 2.2
1.3.5 Identify Input Purpose
AA
The purpose of each field collecting information about the user is programmatically determinable via autocomplete tokens.
WCAG 2.2
4.1.2 Name, Role, Value
A
A field's required state is exposed to assistive technology as part of its name, role, and value.
EN 301 549
9.3.3.2 / 9.1.3.5 (incorporates WCAG)
—
European harmonised standard; references the WCAG A/AA set including labels, instructions, and input purpose.
Section 508
502.3 / 504 (incorporates WCAG A & AA)
—
US federal ICT must meet WCAG 2.0 Level A and AA, including labels and instructions.
ADA Title II
WCAG 2.1 AA (DOJ rule)
AA
US state/local government web content must conform to WCAG 2.1 AA, which includes 1.3.5 Identify Input Purpose.
The three problems we'll fix
Each card below isolates one common expectation defect. For every issue you get a
plain-language statement of the problem, a Bad example (shown as
escaped, non-running code so it can't harm this page), a Good
example, the copyable Code, and an ordered fix.
A label coloured red to mean "this field is required"
carries the whole message in one channel: colour. Someone who can't perceive that
red — a colour-blind user, anyone in high-contrast or forced-colours mode, a screen
reader user — gets no signal at all that the field is mandatory (1.4.1). And because
the colour is purely visual styling, the required state never reaches the
accessibility tree, so the field is announced exactly like an optional one (4.1.2).
The fix is two-sided: add the native required attribute so the state is
programmatic, and pair the visual cue with text such as the word "required" or a
visible asterisk that is explained once for the whole form.
Bad
Only a CSS class makes the label red. There is no required
attribute and no text cue, so the requirement is invisible to anyone who can't
see or distinguish the colour.
The required attribute makes the state programmatic, so the field
is announced as "required". A visible asterisk repeats the cue in a second
channel, and a legend at the top of the form explains what the asterisk means.
If a control can't use the native attribute (for example a custom widget), set
aria-required="true" instead, and spell the word out in the visible
label so the cue never depends on colour or a lone symbol.
Add the native required attribute (or
aria-required="true" on custom widgets) so the state reaches the
accessibility tree.
Pair any colour with a second cue — the word "required", or an asterisk that
is explained once for the whole form.
Explain the asterisk in visible text and reference it with
aria-describedby; hide the decorative symbol itself with
aria-hidden="true" so it isn't read as "star".
Check in forced-colours / high-contrast mode that the requirement is still
obvious, and confirm the field announces as "required".
Instructions placed only in the placeholder
WCAG 2.2 · 3.3.2A1.3.1AEN 301 549Section 508
When a field needs a rule — "at least 8 characters",
"format DD/MM/YYYY", "use the name on your card" — and that rule lives only in the
placeholder, it disappears the instant the user types. The person who
most needs the reminder, someone with a memory or attention disability, loses it
exactly when they start filling the field. Placeholder text is also low-contrast
grey by default, isn't reliably announced as an instruction, and can be mistaken for
a pre-filled value. An instruction that the form requires (3.3.2) has to be
persistent and tied to the field in the markup (1.3.1) — usually a small line of
help text linked with aria-describedby.
Bad
The format rule is hidden in the placeholder. It vanishes on the first
keystroke and isn't programmatically associated with the field as an
instruction.
The instruction is a persistent line of text tied to the input with
aria-describedby, so it stays on screen and is read after the
label. The placeholder, if used at all, only shows an example format.
good-instruction.html
<label for="pw">Password</label>
<input type="password" id="pw" name="pw" aria-describedby="pw-help">
<p id="pw-help" class="field-hint">Use at least 8 characters and one number.</p>
Code
One field can reference several descriptions: list multiple ids in
aria-describedby, separated by spaces, to combine a format hint with
a current error. The hint stays; the error is added and removed as needed.
describedby-multiple.html
<label for="dob">Date of birth</label>
<input type="text" id="dob" name="dob"
aria-describedby="dob-hint dob-error" aria-invalid="true">
<p id="dob-hint" class="field-hint">Format: DD/MM/YYYY.</p>
<p id="dob-error" class="error">Enter a real date, for example 24/05/1990.</p>
How to fix
Move every rule the user must follow out of the placeholder and into visible
help text near the field.
Tie that text to the input with aria-describedby pointing at the
hint's id, so it's announced after the label.
Use placeholder at most for an example of the format — never as
the only place an instruction appears.
To combine a hint with an error, list both ids in
aria-describedby separated by a space.
Check the hint meets normal text-contrast requirements; don't leave it as
faint placeholder-grey.
Personal-data field missing its autocomplete token
WCAG 2.2 · 1.3.5AAEN 301 549Section 508ADA Title II
Identify Input Purpose (1.3.5) applies to any field that
collects information about the user — their name, email, phone, address,
birthday. Each such field must declare its purpose with one of the fixed
autocomplete tokens from the HTML specification. With the right token
the browser can autofill the value, assistive tools can present a familiar icon, and
preference extensions can adapt the field — all of which spare people with
cognitive, motor, or memory disabilities from recalling and re-typing their own
data. A type="text" input with no token leaves that purpose
undeclared, and the tokens are a controlled vocabulary, not free text.
Bad
These fields collect the user's own contact details but declare no purpose, so
the browser can't map them and autofill is unreliable (1.3.5).
Each field declares its purpose with the correct token. The type
still drives the keyboard and validation; the autocomplete token
satisfies 1.3.5 and enables autofill.
Tokens can be split for multi-part data and scoped to a contact type. Use
autocomplete="off" only where autofill would genuinely be wrong,
such as a one-time code.
token-vocabulary.html
<input autocomplete="given-name"> <!-- first name -->
<input autocomplete="family-name"> <!-- last name -->
<input autocomplete="street-address">
<input autocomplete="postal-code">
<input autocomplete="bday"> <!-- date of birth -->
<input autocomplete="work email"> <!-- scoped to work contact -->
<input autocomplete="one-time-code"> <!-- SMS code field -->
How to fix
Add an autocomplete attribute to every field collecting the
user's own data — name, email, phone, address, birthday, and so on.
Use the exact tokens from the HTML autofill vocabulary
(name, email, tel,
given-name, postal-code, bday…), not
invented values.
Set the matching type too, so the right keyboard and validation
appear on mobile.
Reserve autocomplete="off" for fields where autofill is
genuinely inappropriate; never use it to defeat password managers.
Recap
Mark required fields with the required attribute so the state is
announced, and pair the visual asterisk with the word "required" or a legend —
never colour alone (3.3.2, 1.4.1, 4.1.2).
Put instructions in persistent text tied to the field with
aria-describedby; a placeholder vanishes on the first keystroke and
can't carry the instruction (3.3.2, 1.3.1).
Declare every personal-data field's purpose with the correct
autocomplete token from the HTML autofill vocabulary (1.3.5).
The same fixes satisfy WCAG, EN 301 549, Section 508, and ADA
Title II at once — state the expectation in markup and you meet them all.