Accessible Dark Backgrounds: Contrast, Legibility, and WCAG Compliance
Dark backgrounds look modern and they reduce screen glare in low-light conditions, but they make accessibility harder to get right, not easier. Light text on a dark surface fails contrast for different reasons than dark text on a light one — pure white on pure black scores well on a contrast checker but reads as harsh and aliased on most displays, while a comfortable off-white can drift just under the threshold. This page is a practical guide to getting both the math and the perceived legibility right on the kinds of backgrounds covered elsewhere on this site: solid black, dark gradients, glassmorphism over photography, and saturated dark color combinations.
The Two Numbers That Matter
WCAG 2.1 sets contrast minimums based on luminance ratios between text and its background. The two thresholds you actually need to remember:
- 4.5:1 for normal text (under about 18pt, or under 14pt bold).
- 3:1 for large text and for non-text UI elements (icons, focus rings, form-control borders).
WCAG AAA tightens the small-text minimum to 7:1, which is worth aiming for on long-form reading surfaces but is not the working standard most sites are evaluated against.
The trap on dark surfaces is that contrast is not symmetric in perception. A 4.5:1 ratio with light text on a dark background often looks sharper than the same ratio reversed, because your eyes are dilated and overshoot bright pixels. That can push designers to use fully white text — which then triggers a different set of problems described below.
Quick Reference: Common Dark Background Pairings
| Background | Text color | Ratio | Body text (4.5:1) | Large/UI (3:1) |
|---|---|---|---|---|
| #000 | #ffffff | 21.0 | Pass | Pass |
| #000 | #e0e0e0 | 15.4 | Pass | Pass |
| #000 | #999999 | 5.92 | Pass | Pass |
| #0a0a0a | #777777 | 3.65 | Fail | Pass |
| #0a1a3a navy | #1a73e8 cobalt | 2.4 | Fail | Fail |
| #000 | #ff0000 | 5.25 | Pass | Pass |
| #000 | #00ff00 | 15.3 | Pass | Pass |
| #000 | #0000ff | 2.4 | Fail | Fail |
Two patterns jump out. Pure red on pure black squeaks past the 4.5:1 line for body text but is borderline; pure blue is well below it. Mid-grey body copy needs the underlying surface to be near pure black — once the surface lifts to #0a0a0a or higher, mid-grey starts to fail.
Off-White Beats Pure White for Body Copy
Contrast checkers reward #fff on #000 with a perfect 21:1, and that score is misleading. On most displays, fully bright white pixels next to fully dark pixels produce visible halation — the white appears to bleed into the surrounding black. The effect is worse on OLED screens, which can drop pixels to true zero luminance, and worse again on small text. The result reads as "shimmery" or "vibrating" body copy.
The fix is to drop white slightly. #e8e8e8, #ddd, or a warm off-white like #f0e9d6 all retain well over 4.5:1 contrast against #000 while removing the halation. Reserve pure white for headings, where the small text-area lets the harshness register as crispness rather than noise.
:root {
--bg: #050505;
--text-primary: #e8e8e8; /* body */
--text-secondary: #a0a0a0; /* metadata, labels */
--text-heading: #ffffff; /* H1/H2 only */
--link: #7eb6ff; /* light-blue link reads at 8.6:1 on #050505 */
--focus: #ffd76a; /* warm focus ring, 11:1, also visible to colorblind users */
}
Links and Focus States
Default browser blue (#0000ee) on a near-black background fails contrast badly. Lighten the link color until it sits comfortably above 4.5:1 on the page's actual background — for most dark themes, something in the #7eb6ff to #9ec6ff range works. Underline links on dark backgrounds even more religiously than on light ones; a colored link with no underline is the single most common reported "I can't tell what's clickable" complaint on dark UI.
Focus rings get their own minimum: 3:1 against both the focused element and the surrounding background. A pure white ring on a dark surface satisfies that easily, but a brand-colored ring often does not. Default to a warm yellow or amber focus color; it visually distinguishes itself from typical brand palettes (which lean blue and purple) and reads clearly for users with red-green colorblindness.
Saturated Color on Dark Backgrounds
The colored-combo pages on this site (red and black, blue and black, green and black, purple and black) all share the same accessibility issue: saturated colors that feel "iconic" against pure black are usually below WCAG AA for body text. Use the saturated version only for short accents — buttons, badges, headings — and use a desaturated, lighter cousin for paragraphs.
Worked example for a "blue and black" interface:
- Page background:
#000or#0a0a0a. - Body text:
#dcdde0(off-white with a cool tint), not a blue. - Buttons / accent:
#1a73e8on the dark surface (3.0:1 — passes for large/UI only, so make button text bold). - Inline links:
#86b8ff(5.4:1 — passes for body text). - Disabled state:
#5a6072at 50% opacity, with the disabled affordance reinforced by a label, not just by being dimmer.
Glassmorphism and Photo Overlays
Glass surfaces and photo-backed heroes are the hardest cases for contrast. The "background" the text sits on is not a single color — it's whatever happens to be behind the blur or photo at that pixel. Two practical rules:
- Always darken under text. Sandwich a translucent dark gradient (
rgba(0,0,0,0.55)torgba(0,0,0,0.85)) between the photo and the copy. This is the trick that background blend modes are good for; it stabilizes contrast across all the underlying images you might swap in. - Test against the worst frame. If the hero cycles photos, check contrast against the brightest one, not a representative one. The page is only as accessible as its weakest state.
Tools That Belong in Your Workflow
- The WebAIM Contrast Checker for spot-checks.
- Browser DevTools — both Chrome and Firefox surface contrast directly in the element inspector, including the WCAG AA / AAA verdict.
- The
color-contrast()CSS function (where supported) for picking text color from a list automatically based on the background. Treat it as progressive enhancement; provide a static fallback for browsers that haven't shipped it. - A colorblind simulator — DevTools also includes deuteranopia, protanopia, and tritanopia rendering modes. Every dark palette should be checked under at least deuteranopia, the most common form.
Accessibility Checklist for Dark Backgrounds
- Body text ≥ 4.5:1 against the actual surface, not a darker reference.
- Body text not pure white; halation handled.
- Link color clearly distinguishable from body text and meets 4.5:1.
- Underline or icon on links — color alone never carries meaning.
- Focus ring at 3:1 against both the focused element and its surroundings.
- Button text ≥ 4.5:1 against the button surface; if the button is a saturated brand color, check first.
- Photo/glass backgrounds shipped with a darkening overlay; contrast checked against the brightest underlying frame.
- Page rendered with deuteranopia simulation — visual hierarchy still readable.
color-scheme: darkdeclared on the root so form controls render in dark mode.- Animation respects
prefers-reduced-motion; the static fallback also passes contrast.
For the broader coverage of dark CSS surfaces this checklist applies to, see dark backgrounds, the background-color reference, and dark-mode backgrounds with prefers-color-scheme. For specific palettes, the colored-combo pages each include accessibility notes that build on what's here.