CSS Accessibility
You already ship layouts, motion, and modern toolkit tricks. Accessibility in CSS is the layer people feel: Can they see where the keyboard is? Can they read the text? Does a spinning animation make them sick? This session is not an HTML dump (landmarks, alt, ARIA). It is the style habits that keep focus, contrast, and motion usable.
Focus must stay visible
Keyboard users Tab through links and controls. If the focus ring disappears, they are lost. Prefer :focus-visible so mouse clicks stay calm while keyboard focus stays loud:
:root {
--focus-ring: #2563eb;
}
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
}
.text-link:focus:not(:focus-visible) {
outline: none;
}
:focus-visible targets the cases where a focus ring helps (usually keyboard). Pair it with a high-contrast ring and a little outline-offset so the ring does not collide with borders.
Never outline: none without a replacement
Removing the default outline is fine only when you provide a clearer custom focus style. A bare *:focus { outline: none; } is a classic trap:
/* Bad: keyboard users lose the only cue */
.bad-button:focus {
outline: none;
}
/* Good: custom ring that still shows focus */
.good-button:focus-visible {
outline: none;
box-shadow: 0 0 0 3px #fff, 0 0 0 6px #0f766e;
}
If you suppress the browser outline, something else must mark the focused control — box-shadow, border color, or a background shift that remains obvious on every theme.
Contrast is a CSS choice
Brand colors look sharp on a design board and muddy on a phone in sunlight. Treat foreground / background pairs as accessibility decisions, not decoration alone:
:root {
--surface: #0f172a;
--text: #f8fafc;
--muted: #cbd5e1;
--accent: #38bdf8;
--accent-text: #0c4a6e;
}
.hero {
background: var(--surface);
color: var(--text);
}
.hero .lede {
color: var(--muted);
}
.hero .cta {
background: var(--accent);
color: var(--accent-text);
font-weight: 700;
}
Aim for strong contrast on body text and buttons. Decorative greys can sit softer on non-essential labels, but primary copy and controls need to stay readable. Check pairs with a contrast checker before you ship a palette.
Respect reduced motion
Animations and large transforms can cause discomfort. Wrap non-essential motion in prefers-reduced-motion:
.banner {
transition: transform 0.4s ease, opacity 0.4s ease;
}
.banner.is-shown {
transform: translateY(0);
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.banner,
.banner.is-shown {
transition: none;
transform: none;
}
.spinner {
animation: none;
}
}
Users who ask the OS to reduce motion should still get the final UI state — just without the journey. Keep essential opacity or layout changes; drop looping spins and big slides.
High contrast and forced colors
Some environments override your palette (Windows High Contrast / forced-colors). Soften fragile decorations and keep borders real:
.card {
border: 1px solid #e2e8f0;
background: #fff;
box-shadow: 0 8px 24px rgb(15 23 42 / 12%);
}
@media (forced-colors: active) {
.card {
border: 1px solid CanvasText;
background: Canvas;
box-shadow: none;
color: CanvasText;
}
.card a {
color: LinkText;
}
}
System color keywords like Canvas, CanvasText, and LinkText help controls remain visible when the user — not your brand guide — owns the colors.
CSS-side habits
📊 CSS accessibility habits
| Practice | CSS habit | Avoid |
|---|---|---|
| Keep focus findable | :focus-visible + strong ring / shadow |
outline: none with no replacement |
| Readable type | High-contrast text/background tokens | Light grey on white for body copy |
| Kind motion | @media (prefers-reduced-motion: reduce) |
Endless spins for everyone |
| Survive high contrast | Real borders; forced-colors tweaks |
Shadow-only “edges” |
| Reachable controls | Comfortable padding / hit area | Tiny icon-only targets with no focus style |
HTML still owns landmarks, labels, and alt text. CSS owns whether those controls stay perceivable once the page is styled.
Skip links and focus within
A skip link jumps past repeated chrome. Style it so it appears on focus. :focus-within can also highlight a whole group when a child is focused:
.skip-link {
position: absolute;
left: 1rem;
top: -3rem;
padding: 0.5rem 0.75rem;
background: #111827;
color: #fff;
border-radius: 0.375rem;
z-index: 20;
}
.skip-link:focus-visible {
top: 1rem;
outline: 3px solid #fbbf24;
outline-offset: 2px;
}
.nav:focus-within {
box-shadow: inset 0 0 0 2px #2563eb;
}
The skip link stays off-screen until focused, then becomes obvious. The nav ring helps when Tab lands on a child link inside the bar.
Try it yourself
- Add a
:focus-visiblering to links and buttons; Tab through the page and confirm you always see focus. - Pick one low-contrast pair and fix it with darker text or a deeper surface.
- Wrap a transition or animation in
prefers-reduced-motion: reduceand toggle the OS setting (or DevTools emulation). - Under forced-colors emulation, ensure cards still show a real border.
- Style a skip link that appears only on keyboard focus.
Common mistakes
- Global
outline: none(oroutline: 0) with no custom focus style - Pretty brand colors that fail contrast on body text and primary buttons
- Large animations for everyone with no
prefers-reduced-motiongate - Hiding a focusable control with
opacity: 0alone while it still receives Tab - Treating this CSS lesson as a full substitute for semantic HTML and ARIA
Summary
- Keep keyboard focus visible — prefer
:focus-visibleand never erase outline without a stronger cue - Treat color contrast as a requirement for text and controls
- Honor
prefers-reduced-motionand harden styles forforced-colors - Style skip links and use
:focus-withinwhere a group needs a shared cue - Next: CSS Summary & Next Steps (
summary) — wrap the course before the mini project
🧠 Test Your Knowledge
Test Your Knowledge
Challenge yourself with this interactive quiz and see how well you understand the topic
📝 Instructions
- Read each question carefully
- Select the best answer for each question
- You can retake the quiz as many times as you want
- Your progress will be shown at the top