Choosing Accessible Colors
A practical guide to choosing accessible colors for websites and web applications, covering WCAG contrast ratios, text, UI components, color blindness, focus states, palettes, gradients and common accessibility mistakes.
Color is one of the most important parts of a web interface, but it should never be the only way users receive information. A carefully chosen palette can improve readability, make controls easier to understand and help users distinguish different interface states. A poorly chosen palette can make text difficult to read, hide important states and create problems for people with color-vision deficiencies.
Accessible color selection is therefore more than picking colors that look good together. It involves checking contrast, considering different types of color vision, providing non-color cues, making interactive states visible and testing the actual combinations used in the interface.
What Does Color Accessibility Mean?
Color accessibility means designing colors so that important information remains understandable for as many users as possible. This includes people with low vision, color-vision deficiencies, age-related changes in vision and users viewing a page under difficult lighting conditions.
For web development, accessibility guidelines such as WCAG provide measurable requirements for contrast and the use of color. These requirements are useful because visual judgments alone are unreliable. Two colors can look sufficiently different on one monitor and still provide inadequate contrast for some users.
Why Color Alone Is Not Enough
One of the most important principles of accessible color design is that color should not be the only way to communicate information, indicate an action or distinguish one state from another.
<p class="error">Invalid email address</p>
<p class="success">Email address verified</p>If the only difference between the two messages is that one is red and the other is green, some users may not be able to distinguish them reliably. Adding an icon, text label or another visual distinction makes the information available through more than one channel.
<p class="error">
<span aria-hidden="true">!</span>
Invalid email address
</p>
<p class="success">
<span aria-hidden="true">✓</span>
Email address verified
</p>Understanding Contrast Ratio
Contrast ratio measures the difference in relative luminance between two colors. It is expressed as a ratio ranging from 1:1 to 21:1.
| Ratio | Example relationship |
|---|---|
| 1:1 | Identical luminance |
| 3:1 | Moderate contrast |
| 4.5:1 | Common minimum for normal text |
| 7:1 | Higher contrast level for normal text |
| 21:1 | Maximum possible contrast |
A ratio of 1:1 means there is no luminance difference between the two colors. Black against white produces the maximum theoretical contrast ratio of 21:1.
WCAG Text Contrast Requirements
WCAG 2.x defines contrast requirements for text. For normal-sized text, WCAG Level AA generally requires a contrast ratio of at least 4.5:1. Large text has a lower threshold of 3:1.
The higher Level AAA requirement is generally 7:1 for normal text and 4.5:1 for large text. These thresholds are useful when choosing foreground and background colors, but they should not be treated as the only consideration when designing an accessible interface.
| Content | WCAG AA | WCAG AAA |
|---|---|---|
| Normal text | 4.5:1 | 7:1 |
| Large text | 3:1 | 4.5:1 |
Large text has a lower threshold because larger characters are easier to distinguish even when the luminance difference is smaller. The exact definition of large text depends on font size and weight, so do not assume that any visually large-looking label automatically qualifies.
Contrast for User Interface Components
Text is not the only thing that needs contrast consideration. WCAG also contains requirements for graphical objects and user interface components. Important visual boundaries and states should remain distinguishable from adjacent colors.
This is particularly important for buttons, form controls, input borders, focus indicators, icons and other interactive elements. A component can have readable text while its actual boundary or state indicator is difficult to see.
.input {
border: 1px solid #777;
background: #fff;
}
.input:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}A visible focus indicator is especially important for keyboard users. Do not remove outlines simply because they do not match the visual design. Replace them with a clearly visible alternative when necessary.
Foreground and Background Are a Pair
Accessibility testing should evaluate color combinations rather than individual colors. A gray color can be perfectly acceptable on a white background but fail on a light gray background. The same accent can work for large text and fail for normal body text.
| Foreground | Background | What to consider |
|---|---|---|
| Dark text | White | Body text and labels |
| White text | Dark brand color | Buttons and headers |
| Muted text | Light surface | Secondary information |
| Accent text | White | Links and interactive content |
| Icon | Card background | Meaningful graphical content |
When designing a palette, test the combinations that will actually appear together instead of testing each token in isolation.
Why Light Gray Text Is Often a Problem
A common design pattern is using light gray text for secondary information. It can make an interface look subtle and minimal, but it often creates insufficient contrast against white or very light backgrounds.
.muted {
color: #999;
background: #fff;
}The problem is not that gray is inherently inaccessible. The problem is that the specific foreground and background combination may not provide enough luminance difference.
Placeholder Text Needs Care
Placeholder text is frequently displayed in a lighter color than normal input text. Designers sometimes make it so faint that users can barely read it.
input::placeholder {
color: #666;
}
input {
color: #222;
background: #fff;
}A placeholder should not be used as the only label for an important field. Labels should remain available even after the user enters a value, and the placeholder should be treated as supporting information.
Links Should Be Recognizable
Links are often distinguished from surrounding text by color. Color can be part of that distinction, but it should not necessarily be the only difference.
a {
color: #005fcc;
text-decoration: underline;
}
a:hover,
a:focus-visible {
text-decoration-thickness: 2px;
}An underline or another persistent visual distinction makes links easier to identify, particularly for users who have difficulty distinguishing certain colors.
Designing Accessible Buttons
Buttons often combine a background color, text color, border, hover state, active state and focus indicator. Each state should remain understandable and distinguishable.
.button {
background: #155eef;
color: #fff;
border: 2px solid transparent;
}
.button:hover {
background: #104bc1;
}
.button:focus-visible {
outline: 3px solid #111;
outline-offset: 3px;
}
.button:disabled {
opacity: 0.6;
cursor: not-allowed;
}Do not rely only on a subtle background-color change for a critical interaction state. Focus, disabled and active states may benefit from borders, outlines, icons, text or other visual changes.
Color Blindness and Color Vision Deficiency
Color-vision deficiencies affect how some users distinguish colors. The most common forms involve difficulty distinguishing certain combinations of red, green and related hues, although there are several different types and severities.
A palette that looks clearly different to someone with typical color vision may become much harder to distinguish under a color-vision deficiency simulation. This is especially important for charts, status indicators, maps, tags and dashboards.
Common Problematic Color Combinations
- Red and green used as the only distinction between states.
- Green and brown used without another visual cue.
- Blue and purple when their luminance is very similar.
- Several pastel colors with little luminance difference.
- Multiple saturated colors that differ mainly by hue.
The problem is not that these color combinations can never be used. They become problematic when users are expected to identify meaning from hue alone.
Use Shape, Text and Icons Alongside Color
The safest approach is to make color one part of the visual language rather than the complete language. Text, icons, borders, patterns, shapes and positioning can all reinforce the meaning.
<div class="status status-error">
<span class="status-icon" aria-hidden="true">!</span>
<span>Payment failed</span>
</div>
<div class="status status-success">
<span class="status-icon" aria-hidden="true">✓</span>
<span>Payment completed</span>
</div>This approach is especially valuable for data visualizations. A chart should not require users to distinguish several lines or categories purely by color. Labels, patterns, symbols or direct annotations can provide additional information.
Choosing an Accessible Color Palette
A good accessible palette starts with semantic roles rather than a random collection of visually attractive colors. Think about which colors will represent text, surfaces, borders, primary actions, secondary actions, success, warning, error and informational states.
| Role | Example purpose |
|---|---|
| Primary text | Main headings and body text |
| Secondary text | Supporting information |
| Surface | Page and component backgrounds |
| Border | Component boundaries |
| Primary action | Main interactive controls |
| Success | Successful operation or valid state |
| Warning | Potential problem or caution |
| Error | Invalid or failed state |
| Information | Neutral informational message |
Each semantic color should then be tested against the surfaces on which it will appear. A single color may need several variants if it is used for text, backgrounds, borders and decorative elements.
Do Not Build a Palette Around Hue Alone
It is tempting to create a palette by choosing several hues at the same saturation and lightness. This can result in colors that appear very different numerically but surprisingly similar visually.
Perceptual color spaces such as OKLCH can make systematic palette construction easier because lightness and chroma provide useful dimensions for controlling the visual relationships between colors.
:root {
--blue: oklch(58% 0.18 250);
--green: oklch(65% 0.17 145);
--orange: oklch(70% 0.17 65);
--red: oklch(58% 0.18 25);
}Even with a perceptual color space, however, every important foreground/background combination should still be tested. A color model can help you construct a palette, but it does not automatically make the palette accessible.
Accessible Dark Mode
Dark mode introduces a different set of color relationships. Simply inverting a light theme often produces excessive contrast, overly bright text and accents that become too saturated or visually aggressive.
:root {
--surface: #ffffff;
--surface-raised: #f5f6f8;
--text: #1f2328;
--text-muted: #5c6370;
--primary: #155eef;
}
[data-theme="dark"] {
--surface: #15171a;
--surface-raised: #1d2025;
--text: #f0f2f5;
--text-muted: #b0b6c0;
--primary: #7aa2ff;
}The goal is not to maximize contrast everywhere. Extremely bright text on an almost-black background can create an uncomfortable visual experience. Instead, establish a clear hierarchy between primary text, secondary text, surfaces and interactive colors, then verify the important combinations.
Accessible Focus Colors
Keyboard focus needs to be visible against the surrounding interface. This becomes more difficult when a focus indicator has a color similar to either the component or the page background.
button:focus-visible {
outline: 3px solid #ffbf47;
outline-offset: 3px;
}A focus indicator should remain visible across the backgrounds where it can appear. In component libraries, this often means choosing a focus treatment that is independent enough from the component's normal border and background.
Borders and Dividers
Very light borders are common in modern interfaces, but an extremely subtle border may disappear for some users. Before reducing a border to a barely visible gray, ask whether the border communicates structure or merely provides decoration.
If a border communicates an important boundary, state or control, it should have sufficient visual distinction. If it is purely decorative, a lower-contrast treatment may be appropriate.
Accessible Gradients
Gradients make contrast more complicated because the background color changes across the element. White text may have sufficient contrast over one part of a gradient and insufficient contrast over another.
.hero {
background:
linear-gradient(
135deg,
#173b8f,
#7026a8
);
color: #fff;
}Testing only the first or last gradient stop is not enough. Text can overlap an intermediate area where the contrast is lower than either endpoint.
A practical solution is to place a semi-transparent overlay, adjust the gradient stops, change the text color, or place the text inside a solid or sufficiently contrasting surface.
.hero {
position: relative;
color: #fff;
background: linear-gradient(
135deg,
#173b8f,
#7026a8
);
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background: rgb(0 0 0 / 0.2);
}
.hero-content {
position: relative;
z-index: 1;
}Text Over Images
Images create the same problem as gradients, but with much less predictable backgrounds. A text color that works against one part of an image may disappear against another.
- Add a solid or semi-transparent background behind the text.
- Use an overlay to control the luminance of the image.
- Place text in a dedicated surface rather than directly over the image.
- Choose an image region with relatively uniform luminance.
- Test the final composition rather than the text and image separately.
Color Accessibility in Charts
Charts are one of the areas where color accessibility problems are especially common. A visualization may use several colors to distinguish categories, but users may need to identify those categories without relying exclusively on hue.
<div class="legend">
<span>
<span class="marker marker-sales" aria-hidden="true"></span>
Sales
</span>
<span>
<span class="marker marker-refunds" aria-hidden="true"></span>
Refunds
</span>
</div>Good chart design can combine color with labels, patterns, line styles, marker shapes or direct annotations. This also makes the visualization easier to understand for users who simply do not want to decode a legend.
Color in Form Validation
Forms frequently use red for errors and green for valid fields. Color can reinforce these states, but it should not be the only indication.
<label for="email">Email address</label>
<input
id="email"
aria-describedby="email-error"
aria-invalid="true"
/>
<p id="email-error">
Please enter a valid email address.
</p>The semantic attributes and error message make the state understandable even without the color. Color then acts as an additional visual cue rather than carrying the entire meaning.
Semantic Colors Need Multiple Variants
A common mistake is creating one color for each semantic state and using it everywhere. An error color used as a text color may need to be darker than the same semantic color used as a background.
:root {
--error-text: #b42318;
--error-background: #fef3f2;
--error-border: #fecdca;
--error-icon: #d92d20;
}The semantic meaning remains the same, but each variant is optimized for its context. This is usually more robust than forcing a single raw color to work everywhere.
Contrast Is Not the Same as Accessibility
Passing a contrast check does not automatically make a component accessible. Contrast is one measurable part of accessible color design. Users may still have difficulty interpreting an interface if information depends on color alone, focus states are missing, labels are unclear or the visual hierarchy is confusing.
Conversely, a visually comfortable combination can sometimes fall outside a particular automated threshold while still being usable in a specific context. Automated checks are valuable, but they should be combined with design review and testing.
Testing Accessible Colors
A reliable color workflow combines automated contrast checking with visual inspection and testing under different conditions.
- Check text and background contrast.
- Check important graphical objects and component boundaries.
- Test keyboard focus indicators.
- Simulate common color-vision deficiencies.
- Check gradients and image backgrounds at multiple points.
- Review dark and light themes separately.
- Test disabled, hover, active and error states.
- Check charts without relying only on their colors.
- Review the interface in different brightness and display conditions.
Using a Contrast Checker
A contrast checker is one of the fastest ways to validate a foreground/background pair. Enter the foreground and background colors and compare the calculated ratio against the WCAG requirement relevant to the content.
For a real project, test all combinations that users can encounter. If a design token is used for body text on several surfaces, check every relevant surface rather than checking the token only once.
Building a Color Accessibility Workflow
Color accessibility is easier when it becomes part of the design workflow rather than a final inspection step. Start by defining semantic color roles, then establish the main foreground/background relationships and test them before the palette spreads across the application.
- Define semantic color tokens.
- Choose initial colors for each role.
- Check required foreground/background combinations.
- Create separate variants for text, background, border and icon usage when necessary.
- Add non-color cues for important states.
- Test focus, hover, active, disabled and error states.
- Run color-vision simulations.
- Review gradients and images with text overlays.
- Retest the final implementation in the browser.
A Practical CSS Color Token Structure
:root {
--color-text: #1f2328;
--color-text-muted: #5c6370;
--color-surface: #ffffff;
--color-surface-raised: #f6f8fa;
--color-border: #d0d7de;
--color-primary: #155eef;
--color-primary-hover: #104bc1;
--color-success-text: #067647;
--color-success-background: #ecfdf3;
--color-warning-text: #b54708;
--color-warning-background: #fffaeb;
--color-error-text: #b42318;
--color-error-background: #fef3f2;
}The exact values are only examples. The important part is separating semantic roles and recognizing that text, background and border variants may require different colors to satisfy their respective contexts.
Using Modern Color Spaces for Accessible Palettes
Modern color spaces such as OKLCH can make palette construction more systematic. Because OKLCH exposes lightness and chroma separately, it can be useful for creating related colors while maintaining a controlled visual hierarchy.
:root {
--primary: oklch(58% 0.18 250);
--primary-light: oklch(88% 0.06 250);
--primary-dark: oklch(42% 0.15 250);
--text: oklch(25% 0.02 250);
--surface: oklch(98% 0.01 250);
}This can be useful for creating a palette, but accessibility still has to be verified. A perceptual color space does not guarantee a particular WCAG contrast ratio.
Common Mistakes
Using Color as the Only State Indicator
A red border for an invalid input or a green border for a valid input can be helpful, but users should not have to rely on those colors to understand what happened. Add text, icons or semantic information.
Using Very Light Secondary Text
Reducing contrast to make secondary information look subtle often creates text that is unnecessarily difficult to read. Secondary does not mean invisible.
Testing Only the Main State
A button can pass contrast checks in its default state and fail in its hover, focus or disabled state. Interactive states need their own review.
Testing Gradient Endpoints Only
A gradient can have good contrast at both endpoints and still create a low-contrast region in the middle. Test the actual area behind the content.
Assuming Color Blindness Means Only Red and Green
Color-vision deficiencies are diverse. A palette that avoids one problematic combination is not automatically accessible to everyone.
Ignoring Focus Indicators
Removing the browser outline without providing a strong replacement can make keyboard navigation difficult. Focus visibility should be treated as part of the color and component design system.
Treating Contrast Scores as the Entire Accessibility Review
Automated contrast checking is important, but it cannot determine whether the interface communicates information effectively without color. Accessibility requires a broader review.
Accessible Color Checklist
- Normal text meets the applicable WCAG contrast requirement.
- Large text is checked against its applicable threshold.
- Important graphical elements have sufficient visual distinction.
- Focus indicators are clearly visible.
- Links are distinguishable from surrounding text.
- Errors and success states do not rely on color alone.
- Charts do not require users to distinguish categories only by hue.
- Text over gradients and images remains readable across the entire content area.
- Dark and light themes have been tested independently.
- Hover, active, disabled and focus states have been checked.
- Color-vision simulations have been reviewed.
- Semantic color roles have been separated from raw color values.
Frequently Asked Questions
What contrast ratio is required for accessible text?
For WCAG 2.x Level AA, normal text generally requires a contrast ratio of at least 4.5:1, while large text generally requires at least 3:1. Level AAA uses higher thresholds.
Is a 4.5:1 contrast ratio always enough?
It satisfies the common WCAG AA threshold for normal text, but contrast alone does not make an interface fully accessible. You also need to consider color dependence, focus states, graphical elements, typography and the context in which the color is used.
Can I use red and green in an accessible interface?
Yes. Red and green are not forbidden. The important point is not to make color the only way users distinguish the two states. Labels, icons, patterns or other cues should communicate the meaning as well.
Are pastel colors accessible?
Pastel colors can be accessible in some combinations, but their relatively low contrast often makes them unsuitable for small text or subtle UI boundaries. Always test the actual foreground and background pair.
Does OKLCH automatically produce accessible colors?
No. OKLCH can make it easier to construct and adjust palettes, but it does not guarantee a WCAG contrast ratio. Important combinations still need to be checked.
Do disabled controls need the same contrast as normal controls?
Some WCAG requirements treat disabled controls differently from active controls. Even when a particular requirement does not apply, disabled states should still be understandable and should not be confused with unavailable information.
How should I make gradients accessible?
Check the actual content against the entire gradient area, not just the gradient endpoints. You can adjust the gradient, add an overlay, change the foreground color or place the content on a separate solid surface.
Helpful Color Accessibility Tools
A WCAG contrast checker and a general color contrast checker are useful for validating foreground and background combinations. They are especially helpful when reviewing text colors, buttons, borders and design-system tokens.
An accessible color palette generator can help create a starting palette around accessibility requirements, while a gradient contrast checker is useful when text or controls appear over gradients. A color blindness simulator can reveal combinations that become difficult to distinguish under different types of color-vision deficiency.
Conclusion
Choosing accessible colors is not about avoiding bright colors, restricting a design to black and white or following a single universal palette. It is about making sure that important visual information remains understandable across different users and viewing conditions.
Start with semantic color roles, test the actual foreground and background combinations, maintain visible focus states and avoid communicating important information through color alone. Pay particular attention to secondary text, form validation, links, charts, gradients and dark-mode interfaces.
Contrast tools and color-vision simulations can catch many problems early, but they work best as part of a broader accessibility workflow. A strong color system combines measurable contrast with clear semantics, redundant visual cues and deliberate component states.