An accessible color palette is the foundation of an inclusive interface. Colors should provide sufficient contrast for text and interactive elements, and be distinguishable by people with color vision deficiencies. This article covers the steps for creating a palette with WCAG in mind.
Principles of an accessible palette
- Contrast — text and important elements must have a contrast ratio of at least 4.5:1 (AA) for normal text
- Not color alone — do not convey important information using color alone (add icons, labels, or patterns)
- Distinguishability — color pairs should be distinguishable with different forms of color blindness
Choosing a base color
Start with your brand’s primary color. Define it in HSL to make generating shades easier:
:root {
--primary-h: 220; /* Hue */
--primary-s: 70%; /* Saturation */
--primary-l: 50%; /* Lightness */
}Create shades by changing Lightness: 50, 60, 70 for light shades; 40, 30, 20 for dark shades.
Generating a palette
A typical palette contains 5–9 shades, from light to dark:
| Shade | Use |
|---|---|
| 50–100 | Backgrounds, hover states |
| 200–400 | Borders, secondary text |
| 500–600 | Primary color, buttons |
| 700–900 | Text, accents on light backgrounds |
Checking contrast
Check the contrast ratio for each text-background combination. Common pairs:
- Text on white: dark gray (#374151 or darker) for body text
- Text on a colored background: white or very light colors on dark shades (500+)
- Links: a contrast ratio of at least 4.5:1 against the background, plus a visual distinction (underline on hover)
Tools
Color Contrast on rechecker.ru checks contrast against WCAG. Color Converter converts HEX, RGB, and HSL to help you choose shades.
Color blindness
Check your palette using simulation modes:
- Protanopia (red-green)
- Deuteranopia (red-green)
- Tritanopia (blue-yellow)
Chrome DevTools (Rendering → Emulate vision deficiencies) and specialized plugins can help assess how colors are perceived.
Dark mode
For dark mode, invert the logic: light text on a dark background. Maintain a contrast ratio of 4.5:1. Check light and dark modes separately.
CSS variables
Structure your palette using variables:
:root {
--color-text: #1f2937;
--color-text-muted: #6b7280;
--color-bg: #ffffff;
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
}Contrast for states
Check not only the default state, but also :hover, :focus, :active, :disabled. Disabled elements can have lower contrast (at least 3:1), but must not be invisible.
Documenting your palette
Create a page or document with usage examples: body text, headings, links, buttons, errors, and warnings. Specify the contrast ratios for each pair.
Further reading
Learn more about WCAG contrast requirements in WCAG color contrast: requirements and accessibility testing.
Semantic colors
In addition to the base shades, define semantic colors:
| Semantics | Use |
|---|---|
| success | Successful actions, confirmations |
| warning | Warnings |
| error | Errors, critical messages |
| info | Informational hints |
Each semantic color must have sufficient contrast against the background when used for text or icons.
Palette generation tools
You can use the following methods to generate palettes from a base color:
- HSL rotation: change the hue to create analogous colors
- Lightening/darkening: change lightness to create shades
- Online generators: enter a color to get a palette with contrast checks
Color blindness: practical tips
- Avoid using “red-green” pairs as the only way to distinguish things (add icons or labels)
- Blue and orange are generally easy to distinguish with protanopia and deuteranopia
- Check your palette in simulators (Chrome DevTools, Colorblindly)
Integration with a design system
If your project uses a design system (Figma, Storybook), export color tokens as CSS variables. Document the contrast ratio for each token in the component library.
Example token structure
:root {
--color-text-primary: #1f2937;
--color-text-secondary: #6b7280;
--color-bg: #ffffff;
--color-bg-muted: #f9fafb;
--color-primary: #2563eb;
--color-primary-hover: #1d4ed8;
--color-error: #dc2626;
--color-success: #16a34a;
}Working with gradients
Gradients make contrast checks more complicated because contrast varies along the gradient. Options:
- Check the area with the lowest contrast
- Use the gradient only for the background, with text on a solid-color overlay
- Limit gradients to decorative elements
Palette for forms
Define separate colors for input fields, errors, and hints:
- Border default: neutral gray
- Border focus: accent color
- Border error: red with sufficient contrast
- Text error: red, with a contrast ratio of 4.5:1 against the background
- Background error: light red to highlight the field
A/B testing colors
When changing the palette, run an A/B test. Contrast affects readability, time on page, and conversions. Measure the metrics before and after the changes.
Exporting to code
When using design tools (Figma, Sketch), export colors in a format compatible with your stack: CSS variables, SCSS, a Tailwind config, or design system tokens.
Contrast for icons
Icons that convey meaning (success, error, warning) must have a contrast ratio of at least 3:1 against the background. Decorative icons can have lower contrast. If in doubt, add a text label.
Palette for tables
Alternating row colors (zebra striping) improve readability. Contrast between rows should be sufficient, but not excessive. Text in cells should have a contrast ratio of 4.5:1 against the cell background.
Accessibility and branding
Balance brand guidelines with accessibility by:
- Using brand colors for large elements, not small text
- Choosing contrasting shades from the approved palette
- Documenting accessible combinations in the brand guide