By WebNest Studio Engineering Team
CSS Tutorial
CSS and Accessibility
Accessibility in CSS is about making sure the visual styling of a page doesn't create barriers for people with different vision, motor, or cognitive needs. Because CSS controls color, contrast, focus indication, and motion, it plays a direct role in whether a site is usable by people relying on screen readers, keyboard navigation, or assistive settings at the operating-system level.
Three concerns come up constantly in accessible CSS: sufficient color contrast for readability, visible focus indicators for keyboard users, and respecting a user's request to reduce motion — each backed by concrete, testable CSS techniques rather than vague guidelines.
Why it matters
CSS can make a page unusable without changing a word of it: pale grey text that cannot be read in sunlight, a removed focus outline that leaves keyboard users lost, motion that makes some people ill. A few rules, written once, prevent all three, and they are checked in every accessibility audit.
Color Contrast
Text needs sufficient contrast against its background to be readable by users with low vision or color blindness. The Web Content Accessibility Guidelines (WCAG) define a minimum contrast ratio of 4.5:1 for normal text and 3:1 for large text (roughly 18pt or larger, or 14pt bold) at the AA compliance level. Contrast is a function purely of the color and background-color values you choose in CSS, so checking a color pairing with a contrast-ratio tool before shipping is a simple, high-impact habit.
Focus-Visible Styles
Keyboard users navigate a page by tabbing between focusable elements (links, buttons, form fields), and they rely entirely on a visible focus indicator — usually an outline — to know where they currently are on the page. Removing the default outline with outline: none without providing a clear replacement is one of the most damaging accessibility mistakes in CSS. The modern :focus-visible pseudo-class lets you style focus specifically for keyboard interaction while avoiding an outline appearing on a simple mouse click, giving the best experience for both input methods.
prefers-reduced-motion
Some users experience discomfort, dizziness, or distraction from animated motion, and many operating systems expose a "reduce motion" accessibility setting that CSS can detect with the prefers-reduced-motion media feature. Wrapping non-essential animations and transitions in @media (prefers-reduced-motion: reduce) { ... } lets you shorten or remove motion for users who have requested it, without affecting the experience for everyone else.
Examples
A visible focus style that only appears for keyboard navigation
.button {
outline: none; /* removing default requires a deliberate replacement below */
}
.button:focus-visible {
outline: 3px solid #0057d9;
outline-offset: 2px;
}
Clicking the button with a mouse shows no outline, but tabbing to it with a keyboard displays a clear, high-contrast 3px outline offset from the edge, so keyboard users always know where focus currently is.
Disabling non-essential animation for users who prefer reduced motion
.hero-heading {
animation: fade-in-up 0.6s ease-out forwards;
}
@media (prefers-reduced-motion: reduce) {
.hero-heading {
animation: none;
opacity: 1;
transform: none;
}
}
Most users see the heading animate in with a fade and slide, while users with reduced-motion enabled at the OS level see the heading appear immediately in its final position, with no animation at all.
Common Mistakes
- Removing outline: none on focusable elements without providing any visible replacement, leaving keyboard users with no way to see where focus is.
- Choosing text and background colors based on aesthetics alone without checking the resulting contrast ratio against WCAG minimums.
- Building elaborate scroll-triggered or looping animations with no prefers-reduced-motion fallback, causing discomfort for motion-sensitive users.
- Conveying important information through color alone (e.g. red text for errors) without an additional visual cue like an icon or label, which fails for color-blind users.
Exercise
Fix three problems. The paragraph's light grey text has too little contrast on white; change it to #374151. The button's focus outline has been removed; give keyboard users a 3px solid outline in #1d4ed8 with a 2px offset. The button animates constantly; stop the animation for users who prefer reduced motion.
Expected result: darker paragraph text, a visible outline when the button is reached with the Tab key, and no pulsing when the system's reduced-motion setting is on.
/* The page contains: <main> with an <h1>, a <p> and a <button> */
p {
color: #c0c0c0;
}
button {
outline: none;
animation: pulse 1s infinite alternate;
}
@keyframes pulse {
to {
transform: scale(1.1);
}
}
Show hints
- Remove
outline: noneand stylebutton:focus-visible. - The motion preference is tested with
@media (prefers-reduced-motion: reduce).
Show solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */
p {
color: #374151;
}
button {
animation: pulse 1s infinite alternate;
}
button:focus-visible {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}
@keyframes pulse {
to {
transform: scale(1.1);
}
}
@media (prefers-reduced-motion: reduce) {
button {
animation: none;
}
}
Quick quiz
1. What contrast ratio does WCAG level AA require for normal-sized text?
- A. 4.5:1
- B. 2:1
- C. 3:1
- D. 7:1
Show answer
A. Large text needs 3:1. Level AAA asks for 7:1 for normal text.
2. Why is outline: none on focusable elements a problem?
- A. It slows the page
- B. Keyboard users can no longer see which element has focus
- C. It breaks hover styles
- D. It is invalid CSS
Show answer
B. If the default outline is removed, a clear replacement focus style must be provided.
3. What does :focus-visible match that :focus does not distinguish?
- A. Only links
- B. Only mouse clicks
- C. Focus that the browser judges should be shown, such as keyboard focus, and usually not a mouse click on a button
- D. Hidden elements
Show answer
C. It lets keyboard users have a strong focus ring without showing it on every mouse click.
Interview questions
How can CSS harm accessibility?
Show answer
Low contrast between text and background makes content unreadable for many people. Removing focus outlines hides the keyboard position. display: none hides content from screen readers as well as from sight, and changing the visual order with flexbox or grid can make it differ from the reading and tab order. Fixed pixel font sizes ignore the user's settings, and unrestrained animation can cause real discomfort.
How do you hide content visually but keep it available to screen readers?
Show answer
Use a "visually hidden" utility class that takes the element out of view without removing it from the accessibility tree: absolute positioning, a size of 1px, overflow: hidden and a clip that hides the content. It is used for text such as a label for an icon-only button or a "skip to content" link. display: none and visibility: hidden must not be used for this, since they hide the content from everyone.
Key Points to Remember
- WCAG AA requires a minimum contrast ratio of 4.5:1 for normal text and 3:1 for large text.
- :focus-visible lets you style keyboard focus clearly without showing the same outline on every mouse click.
- @media (prefers-reduced-motion: reduce) lets a stylesheet respect a user's OS-level request to minimize animation.
- Accessible CSS is testable, not just aesthetic — contrast ratios and focus visibility can be verified concretely before shipping.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering