Accessible CSS
CSS can make a page accessible or break it. Colour contrast, focus visibility, readable text, motion, hidden content and layout order all depend on your styles. This lesson gathers the CSS habits that keep pages usable for everyone.
Contrast
WCAG requires a contrast ratio of at least 4.5:1 for normal text, 3:1 for large text (about 24px, or 19px bold) and for interface parts like input borders and icons. Browser developer tools show the ratio when you inspect a colour.
Visible focus
Keyboard users need to see where they are at all times. A global :focus-visible style with good contrast is one of the most valuable lines of CSS you can write. Never remove outlines without an equally visible replacement.
Respect text resizing and zoom
- Use
remfor font sizes so users' browser font-size settings apply. - Avoid fixed heights on elements containing text — use
min-heightso text can grow. - Never disable zooming in the viewport meta tag.
- Test at 200% zoom: content should reflow into one column without horizontal scrolling or overlapping.
Reduced motion
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Large movements, parallax and zooming can trigger dizziness and nausea for people with vestibular disorders. Honour the system setting — either globally like this, or by designing a calmer alternative for each animation.
Hide content correctly
| Goal | Use |
|---|---|
| Hide from everyone | display: none or the HTML hidden attribute |
| Hide visually, keep for screen readers | A .visually-hidden utility class |
| Hide decorations from screen readers | aria-hidden="true" in HTML, or content: "★" / "" for generated text |
.visually-hidden:not(:focus):not(:active) {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
The :not(:focus) part lets the same class be used for "skip to content" links that appear when focused.
Keep visual order and DOM order in sync
Flexbox order, row-reverse, grid placement and absolute positioning can make the visual order differ from the HTML order. Screen readers and Tab follow the HTML. If something is first visually, it should usually be first in the HTML too.
Don't rely on colour alone
Touch targets and spacing
.icon-button {
min-width: 44px;
min-height: 44px;
}
.inline-links a {
padding-block: 0.25em;
}
Make interactive elements at least 24×24 CSS pixels (44×44 is better), with space between them, so people with limited dexterity — and everyone on a phone — can tap accurately.
Forced colours and high contrast
Windows High Contrast mode replaces your colours with the user's chosen palette, removing backgrounds and shadows. Use real borders or outlines for important boundaries (a transparent border is enough — it becomes visible in forced-colours mode), and test with @media (forced-colors: active).
Checklist
- Text and UI contrast meet 4.5:1 / 3:1.
- Every interactive element has a visible focus style.
- Font sizes in
rem; no fixed heights on text containers. - Page works at 200% zoom and 320px wide.
- Motion respects
prefers-reduced-motion. - Visual order matches DOM order.
- Information never conveyed by colour alone.
Try it yourself
Fix the problems: the low-contrast text, the missing focus style and the colour-only error.