- Diego Gonzalez
- Adam Argyle (Author of the original proposal)
- [Issue tracker]
- [Discussion forum]
[You can generate a Table of Contents for markdown documents using a tool like doctoc.]
CSS Color Module Level 5 introduces new features and among them is the contrast-color functions. This feature returns a <color> of either white or black depending which has better contrast with the color input as a parameter.
While this is useful for dynamic theming, real world use cases often require more complex color combinations than only black and white.
This explainer proposes to expand the contrast-color function to add advanced color contrast testing.
The importance of contrast in web content readability cannot be overstated. With more web applications relying on some sort of smart or dynamic theming, ensuring readability of said content is paramount to making sure the content remains accessible to everyone, including users with visual impairments or color blindness.
- Expand
contrast-colorfunction to more colors other thanwhite/black. - Enable specifying a role for the consulted color (
backgroundorforeground). - Allow a set of options for the contrast color function to choose from.
- Offer more contrast algorithms options to developers, including setting a custom contrast ratio.
The goals stated above allow developers to have a deeper control over contrast in the content, maximising copy readability, and enabling richer, more accessible color palettes that follow brand guidelines with fewer code. These are some resulting use cases:
- Maximise readability for copy with custom colors.
- Smart/dynamic theming in web applications that output adaptive colors for body copy, links and UI elements.
- Making sure that pseudoelements like
::selectionand::cuehave accessible color contrast. - UX elements like profile avatars, chips, badges, tags, complying with good contrast.
The proposed solution is expanding the contrast-color function to accept more parameters that allow to customise its behavior.
The proposed syntax is as follows:
contrast-color() = contrast-color(<color-role>? <color> / <contrast-target> <contrast-algorithm> <color>#{2,})With these being the available parameters:
<color-role>:backgroundorforeground. Specifies the role of the base, known color. It is also necessary for some contrst algorithms.<color>: The base color that an author is looking to contrast against.<contrast-algorithm>: Which contrast calculation method to use (auto,wcap,lstar,apca,weberormichelson).<contrast-target>: Follows theprefers-contrastmedia query syntax. (auto|max|more|less|<custom-contrast-target>). More, less and no-preference (auto) correlate with user preferences. Max is introduced to return black or white.<custom-contrast-target>: the desired contrast target ratio or a percentage. This ratio will fail or pass depending on the used algorithm to calculate contrast. Is is only used when the<custom-contrast-target>is set.
The original proposal shows a dynamic example of these parameters in play.
Solving expanding the contrast function to more colors other than black/white, and including an optional list of colors
If a developer wanted to test contrast between a list of colors over a blue background, to make sure the copy remains as readable as possible, they would use the following code:
body {
background: blue;
color: contrast-color(blue gold, orange, yellow);
}With all the other default parameters this would return the color gold/lch(87.468% 86.602 88.075).
Inclussion of different contrast algorithms (like APCA) require the distinction of foreground and background colors to enhance the results.
p {
color: blue;
background: contrast-color(foreground blue / apca gold, orange, yellow);
}With blue as the base color specified for the foreground, the contrast-color function would return yellow/lch(97.607% 94.712 99.572) as the background color using the specified APCA algorithm.
If the developer chooses to tweak the options to include a contrast target of less, then the resulting color is gold/lch(87.468% 86.602 88.075) instead. The corresponding snippet is written below:
background: contrast-color(foreground blue / less apca gold, orange, yellow);Given a design token background color, choose from 3 design system color tokens that best matches the user's preference.
div {
color: contrast-color(var(--surface-bg) /
var(--text-1), var(--text-2), var(--text-3)
);
}Similarly, for UX elements and pseudoelements like ::selection and ::cue, using the contrast target max to force the result to be black or white.
::selection {
color: contrast-color(var(--highlight) / max);
}
::cue {
color: contrast-color(var(--cue-bg) / max);
}The color contrast is not new, following is the list of alternatives that have existed or have been considered to solve this.
In 2021 Webkit included a color-contrast function that allowed developers to pass as a parameter a list of color candidates to choose from.
Dave Rupert has a workaround for using custom colors (from a design system) together with the CSS contrast-color() function. This relies on the if() function and uses Lea Verou's --contrast-color() workaround, and lets the developer select the correct branded text color from a design system based on button background contrast.
The technique computes a lightness‑based black-or-white reference color, stores it in a typed custom property (--captured-color registered as <color>), and then compares that value inside an inline if() style query to choose between your design system’s text color tokens. This produces contrast‑appropriate, brand‑consistent text colors using only today’s vanilla CSS.
Unfortunately browsers don’t yet support contrast-color() and if() together.
[Highlight any accessibility, internationalization, privacy, and security implications that have been taken into account during the design process.]
[Implementors and other stakeholders may already have publicly stated positions on this work. If you can, list them here with links to evidence as appropriate.]
- [Implementor A] : Positive
- [Stakeholder B] : No signals
- [Implementor C] : Negative
[If appropriate, explain the reasons given by other implementors for their concerns.]
This work is based on a proposal made by Adam Argyle, you can see the initial draft here.
Many thanks for valuable feedback and advice from:
- Dave Rupert
Thanks to the following proposals, projects, libraries, frameworks, and languages for their work on similar problems that influenced this proposal.
- CSS contrast-color() Proposal Explorer by Adam Argyle
- Using your design system colors with contrast-color() by Dave Rupert
- On compliance vs readability: Generating text colors with CSS by Lea Verou