A red border is the most common way forms mark invalid fields, and for roughly one in twelve men with a colour vision deficiency, anyone in bright sunlight, and everyone using Windows High Contrast mode, it is often the only signal — and it is invisible: the border looks the same as a valid field’s, and the user cannot tell which of ten fields is wrong.
WCAG’s “Use of Color” criterion (1.4.1) requires that colour is not the only visual means of conveying information, and “Non-text Contrast” (1.4.11) requires that visual indicators of state have sufficient contrast. This page, part of error summary and messaging, builds error styles that work with colour, without colour, in forced-colors mode and in dark mode.
Context and prerequisites
Signals that do not depend on colour:
- Text — the error message itself, ideally starting with a visible or visually hidden “Error:” prefix.
- Icons — a warning or cross icon next to the message, with the message text as its accessible counterpart (the icon itself is decorative).
- Shape and weight — a thicker border (for example 2px → 4px), a left bar, or a changed outline style.
- Position — the message placed directly with the field, not in a distant corner.
Contrast requirements (WCAG 2.2 AA):
- Error text must meet 4.5:1 against its background (3:1 if large).
- The visual indicator of the invalid state (a border, a bar, an icon) must meet 3:1 against adjacent colours.
- A red that passes on white often fails on a pale pink error background or in dark mode.
The core pattern: layered signals in CSS
:root {
--error-text: #b3261e; /* 6.5:1 on white, 6.0:1 on --error-bg */
--error-border: #b3261e; /* ≥ 3:1 against white and the field background */
--error-bg: #fdf3f2; /* text must still pass on this */
}
@media (prefers-color-scheme: dark) {
:root {
--error-text: #ffb4ab; /* re-checked for dark surfaces */
--error-border: #ffb4ab;
--error-bg: #3a1a17;
}
}
/* 1. Shape changes, not only colour: thicker border plus a left bar. */
.field[data-invalid="true"] input,
input[aria-invalid="true"] {
border: 2px solid var(--error-border);
box-shadow: inset 4px 0 0 0 var(--error-border); /* left bar inside the field */
}
/* 2. The message: icon + text, with a visible (or visually hidden) prefix. */
.field-error {
display: flex; gap: .4rem; align-items: flex-start;
color: var(--error-text);
font-weight: 600;
}
.field-error::before {
content: ""; /* decorative icon via mask; the text carries meaning */
flex: none; width: 1.1em; height: 1.1em; margin-top: .1em;
background: currentColor;
mask: url("/icons/alert.svg") center / contain no-repeat;
}
/* 3. Forced colors (Windows High Contrast): use system colours so signals remain. */
@media (forced-colors: active) {
input[aria-invalid="true"] {
border: 3px dashed Mark; /* dashed: a shape change survives any palette */
box-shadow: none;
}
.field-error { color: CanvasText; }
.field-error::before { background: CanvasText; forced-color-adjust: none; }
}
<div class="field" data-invalid="true">
<label for="postcode">Postcode</label>
<input id="postcode" name="postcode" aria-invalid="true" aria-describedby="postcode-error">
<p id="postcode-error" class="field-error"><span class="visually-hidden">Error:</span> Enter a real postcode, like SW1A 1AA.</p>
</div>
Step-by-step walkthrough
- Always pair colour with text. The message is the primary signal; colour reinforces it. A field with a red border and no message fails 1.4.1.
- Change shape as well as colour. Increase border width, add a left bar, or switch to a dashed outline in forced-colors mode. Test in greyscale: invalid fields must still stand out.
- Add an icon, decoratively. The icon speeds visual scanning; the text carries the meaning, so the icon needs no separate text alternative.
- Check contrast on the actual backgrounds. Error text against the page and against any error background tint; the border against both the page and the field’s fill (3:1). Re-check in dark mode.
- Support forced colors. In Windows High Contrast, author colours are replaced; box-shadows are removed. Use system colour keywords (
Mark,CanvasText,Highlight) and border styles that survive. - Keep success states subtle. Green ticks on valid fields add noise and repeat the colour problem; if you show them, use text (“Looks good”) and do not make them the only confirmation.
Why the message text is the real accessibility feature
Colour, borders and icons help sighted users scan a long form for problems. But the thing that lets a user fix the problem is the message, and the message is also what reaches screen-reader users through aria-describedby, voice-control users through visible text they can read aloud, and cognitive-accessibility needs through clear, specific wording. A form whose visual error styling is perfect but whose messages say “Invalid input” is still inaccessible in the ways that matter most. Style for scanning; write for fixing — the guidance in writing error messages that tell the reader what to do.
Designing the error palette as tokens
Treat error colours as design tokens with contrast guarantees rather than as ad-hoc hex values sprinkled through components. Define a small set — error text, error border, error surface tint — for each theme, and record the contrast ratio each pair must meet next to the token. When the brand palette changes, the tokens change in one place and the ratios can be re-verified automatically in a unit test that computes relative luminance. Components then reference tokens only, so no individual component can reintroduce a red that fails on its background, and dark mode, high-contrast variants and future themes inherit correct error styling by construction.
Failure modes and edge cases
1. Error background tints that break contrast
A red message on a pink error panel often drops below 4.5:1. Check text against the tint, not against white.
2. box-shadow borders in forced colors
Forced-colors mode removes box-shadow, so an error indicator drawn only with a shadow disappears. Provide a real border or outline alternative in the forced-colors media query.
3. Placeholder-only errors
Replacing the placeholder with “Required!” in red is invisible once the user types and usually fails contrast. Errors belong in their own element below or above the field.
4. Icon fonts
Icon fonts can be replaced by the user’s font settings or read aloud as letters. Use inline SVG or CSS masks with currentColor, marked decorative.
5. Group errors
For radio and checkbox groups, apply the shape change to the group container (a left bar on the fieldset) and to each option’s input, as in accessible errors for radio and checkbox groups.
Verification checklist
Frequently Asked Questions
Is a red border acceptable if there is also a message?
Yes. The requirement is that colour is not the only means. With a message present, the border is a helpful additional signal; making it thicker than the default border adds a non-colour cue for scanning.
How do I test forced-colors mode without Windows?
Chromium DevTools can emulate forced-colors: active in the Rendering panel. It is a good first check; test on Windows with a real High Contrast theme before release.
Should valid fields turn green?
Usually not. Green success styling adds colour-only information and visual noise. If positive feedback helps (for example, a username is available), use text, and keep it calm.