WCAG Color Contrast Explained: How to Pass AA and AAA
What the Contrast Ratio Measures
The Web Content Accessibility Guidelines (WCAG) measure contrast as a ratio between the relative luminance of two colors, from 1:1 (identical) to 21:1 (black on white). Luminance here means perceived lightness, weighted for how sensitive the eye is to red, green and blue. Hue does not count directly: a red and a green with the same luminance have a ratio near 1:1, and many people with color-vision deficiencies cannot tell them apart.
The Formula, Walked Through
Take QuickToolApp's default contrast pair: text #1E3A8A (dark blue) on background
#DBEAFE (pale blue). The formula is defined in the
WCAG 2.2 specification.
- Convert each hex channel to 0–1. #1E3A8A is R 30, G 58, B 138, so 0.1176, 0.2275, 0.5412.
- Linearize each channel. If the value is 0.04045 or less, divide by 12.92. Otherwise use ((value + 0.055) ÷ 1.055)^2.4. This gives R 0.01298, G 0.04231, B 0.25415. (Older versions of the spec print 0.03928 as the cut-off; the difference never changes a result in practice.)
- Weight and add: L = 0.2126 R + 0.7152 G + 0.0722 B = 0.00276 + 0.03026 + 0.01835 = 0.0514.
- Repeat for the background. #DBEAFE (219, 234, 254) linearizes to 0.70838, 0.82279, 0.99110, so L = 0.8106.
- Ratio = (lighter L + 0.05) ÷ (darker L + 0.05) = 0.8606 ÷ 0.1014 = 8.49:1.
8.49:1 passes every text requirement below. Notice how much green dominates: 71.5% of the weight. That is why yellow and light green look bright and fail on white so often, while pure blue (#0000FF) passes at 8.6:1 despite looking vivid.
The Thresholds in WCAG 2.2
| What | Level AA | Level AAA | Success criterion |
|---|---|---|---|
| Normal text | 4.5:1 | 7:1 | 1.4.3 (AA), 1.4.6 (AAA) |
| Large text: 18 pt (24 CSS px) and up, or 14 pt (about 18.7 px) bold and up | 3:1 | 4.5:1 | 1.4.3, 1.4.6 |
| UI components and meaningful graphics (input borders, focus rings, icons, chart lines) | 3:1 against adjacent colors | no separate AAA level | 1.4.11 |
Exempt: logos and brand names in text, purely decorative elements, and inactive (disabled) controls. The W3C's Understanding Contrast (Minimum) and Understanding Non-text Contrast pages explain edge cases. The thresholds are hard limits: 4.49:1 fails AA. Most organizations, and many accessibility laws and procurement rules, target AA.
Ten Colors That Fail on White, and Fixes That Keep the Hue
These are common brand-style colors used as text or button backgrounds with white. The fix keeps the same hue and saturation and lowers lightness until the pair reaches 4.5:1. The ratio is the same whether the color is the text on white or the background behind white text.
| Color | Hex | Ratio with white | AA fix | Fixed ratio |
|---|---|---|---|---|
| QuickToolApp default blue | #3B82F6 | 3.68:1 | #1E6FF5 | 4.52:1 |
| Safety orange | #FF6600 | 2.94:1 | #C95100 | 4.51:1 |
| Amber yellow | #FFC107 | 1.63:1 | #967000 | 4.55:1 |
| Sky blue | #1DA1F2 | 2.83:1 | #0B7BBF | 4.57:1 |
| Bright green | #2ECC71 | 2.10:1 | #1F874B | 4.54:1 |
| Lime | #8BC34A | 2.10:1 | #59812B | 4.57:1 |
| Teal | #1ABC9C | 2.41:1 | #12846E | 4.61:1 |
| Cyan | #00BCD4 | 2.30:1 | #008192 | 4.61:1 |
| Pure red | #FF0000 | 4.00:1 | #ED0000 | 4.56:1 |
| Medium gray | #999999 | 2.85:1 | #757575 | 4.61:1 |
Two other options that keep the original color exactly:
- Switch to dark text. Black on #FFC107 amber is 12.9:1 and on #FF6600 orange is 7.2:1. Bright warm colors usually work better with dark text than white.
- Use the bright color for large elements only. At 3:1, the original orange still fails for large text (2.94), but sky blue nearly passes and the QuickToolApp blue passes for large text and UI borders.
Keep the original color for decoration, illustrations and large fills, and use the darker variant for text and controls. Design systems often call these "brand" and "brand-strong" tokens.
Palettes: Harmony Does Not Guarantee Contrast
Color-harmony schemes describe relationships of hue on the color wheel:
- Monochromatic: one hue at different lightness levels. Easiest to make accessible, because the lightness spread gives contrast.
- Analogous: neighboring hues (blue, blue-green, green). Pleasant, but neighbors often have similar luminance.
- Complementary: opposite hues (blue and orange). High visual tension, but not necessarily high luminance contrast: #3B82F6 blue against its complement #F6AF3B orange is only 1.95:1.
- Triadic: three evenly spaced hues. Lively, and needs a neutral dark or light for text.
The practical rule: choose hues for harmony, then choose lightness for contrast. Check every pair you will actually put text on, not just the palette as a whole.
Exporting Tokens From QuickToolApp
The Design section of the QuickToolApp home page generates a palette from a primary color and a scheme, recommends a readable text/background pair, and shows the WCAG ratio and a Good/Mid/Bad rating (Good at 4.5:1 or above, Mid from 3:1). The export buttons copy the palette to your clipboard as:
- CSS: custom properties in a
:rootblock. - Tailwind: a config snippet that adds the colors under
theme.extend.colors. - JSON: design tokens with value, role and type.
- Figma: a JSON list of color variables with names, values and descriptions, which you can bring into Figma with a variables-import plugin.
After exporting, add the darker "strong" variants for any color that fails as text, and record the measured ratio next to each approved pair in your design documentation.
A Note on APCA and WCAG 3
WCAG 3 is still a working draft. An alternative contrast method called APCA (Accessible Perceptual Contrast Algorithm) has been discussed for it. APCA accounts for font size and weight and for the fact that light text on dark backgrounds is perceived differently from dark on light. It is not part of any published WCAG standard. For compliance today, use the WCAG 2.2 ratios above; APCA can be a useful second opinion, especially for dark mode.
Checklist
- Body text at 4.5:1 or more (7:1 if you target AAA).
- Large headings at 3:1 or more, but check the actual rendered size and weight.
- Placeholder text, captions and "muted" gray text measured too; they fail most often.
- Input borders, focus indicators, icons and chart lines at 3:1 against what is next to them.
- Links distinguishable from body text by more than color alone, or by 3:1 against the surrounding text plus a visible cue on focus and hover.
- Text over images or gradients checked at the lightest point behind the text.
- Dark mode tested separately; it is a different set of pairs.
- Information never conveyed by color alone (pair red/green states with icons or labels).