CSS Tutorial
CSS Accessibility
Accessible CSS ensures your site works for everyone, including people using keyboards, screen readers, or with visual and motion sensitivities. CSS choices around focus indicators, color contrast, motion, and hiding content directly shape usability. This guide covers the CSS techniques that make interfaces inclusive without sacrificing design.
Why CSS Matters for Accessibility
Accessibility (often shortened to a11y) is about making content usable by people with a wide range of abilities. While semantic HTML does the heavy lifting, CSS controls the visual and interactive layer: whether keyboard users can see where they are, whether text has enough contrast to read, and whether animations respect people who get motion sickness.
Always Keep a Visible Focus State
Keyboard users navigate with the Tab key and rely on a visible focus ring to know which element is active. Never remove focus outlines without providing a clear replacement. The :focus-visible pseudo-class is ideal: it shows a focus style for keyboard users while suppressing it for mouse clicks.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; }
button { padding: 10px 18px; border: none; border-radius: 6px; background: #0f766e; color: #fff; margin-right: 8px; cursor: pointer; }
button:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
</head>
<body>
<p>Press Tab to move between the buttons and watch the focus ring.</p>
<button>First</button>
<button>Second</button>
<button>Third</button>
</body>
</html>outline: none with no replacement is one of the most common accessibility failures on the web. If you dislike the default ring, restyle it - do not delete it.
Color Contrast
Text must contrast enough with its background to be readable. The WCAG guidelines require a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text. Never rely on color alone to convey meaning - pair it with text, an icon, or an underline so colorblind users get the message too.
| Content | Minimum contrast ratio |
|---|---|
| Normal body text | 4.5 : 1 |
| Large text (18pt+ or 14pt bold) | 3 : 1 |
| UI components and graphics | 3 : 1 |
Hiding Content Accessibly
There is a crucial difference between hiding something from everyone and hiding it only visually. display: none and visibility: hidden remove content from screen readers too. To provide text for screen readers while hiding it visually - like a label for an icon button - use a visually-hidden utility class.
.visually-hidden {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}Use .visually-hidden for text you want screen readers to announce but not display, such as "Search" next to a magnifying-glass icon. Use display: none only when you truly want to hide content from everyone.
Respect Reduced Motion
Some users experience nausea or discomfort from animations. The prefers-reduced-motion media query lets you honor their system setting by disabling or softening motion. It is a small addition that makes a big difference.
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; }
.spin { width: 60px; height: 60px; border-radius: 8px; background: #7c3aed; animation: rotate 2s linear infinite; }
@keyframes rotate { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
.spin { animation: none; }
}
</style>
</head>
<body>
<p>If your system requests reduced motion, this box stops spinning.</p>
<div class="spin"></div>
</body>
</html>Respect Font Size Preferences
Set font sizes in rem rather than px so text scales with the user's browser and operating-system font-size settings. Using px for body text locks out people who need larger text. Also allow layouts to reflow rather than clipping when text grows.
Key points
- Keep a visible focus indicator - use :focus-visible, never bare outline: none.
- Meet WCAG contrast ratios (4.5:1 for normal text) and never rely on color alone.
- display: none hides from everyone; use a visually-hidden class for screen-reader-only text.
- Honor prefers-reduced-motion to disable animations for sensitive users.
- Size text in rem so it respects the user's font-size preferences.
