This PostCSS plugin implements a polyfill for the color-contrast()
function, part of the extremely new CSS Color Module Level 6 spec.
Because the color is computed at compile-time, this plugin only supports literal colors (i.e. hex, rgb()
, hsl()
) and does not support var()
or calc()
expressions, currentColor
, etc., and therefore must be placed near the end of the PostCSS chain.
No guarantees are made about the spec-compliance of this plugin, but I tried my best.
selector {
color: color-contrast(<color> vs <color>, <color>[, ...] [to <number> | AA | AA-large | AAA | AAA-large]);
}
I'll call the first color the "background", the list of colors the "foreground" colors, and the number/keyword the "target ratio".
No target ratio: color-contrast(<color> vs <color>, <color>[, ...])
- The foreground color that has the highest contrast ratio with the background color is used.
Target ratio: color-contrast(<color> vs <color>, <color>[, ...] to <number> | AA | AA-large | AAA | AAA-large)
- The first foreground color in the list that has a contrast ratio greater than or equal to the target ratio is used.
- AA-large is the same as
3
. - AA is the same as
4.5
. - AAA-large is the same as
4.5
. - AAA is the same as
7
.
npm install --save-dev postcss-color-contrast
yarn add -D postcss-color-contrast
In your postcss.config.js
file:
module.exports = {
plugins: [
require('postcss-color-contrast'),
],
};
Or programmatically:
const postcss = require('postcss')
postcss([
// ... other plugins here
require('postcss-color-contrast'),
// ... maybe more plugins here
]).process(css)
Additionally, use the colorContrast()
function from JavaScript:
import colorContrast from 'postcss-color-contrast/js'
const colorContrast = require('postcss-color-contrast/js')
// -> colorContrast(backgroundColor, foregroundColors, targetRatio, outputFormat)
// provide colors in any valid CSS color format, or a [r, g, b] array
colorContrast('#0f172a', ['#e5e5e5', '#171717']) // -> '#e5e5e5'
colorContrast('rgb(194, 65, 12)', ['hsl(0, 0%, 90%)', [23, 23, 23]], 'aa') // -> '#ffffff'
// provide an output format (defaults to 'hex')
const bg = '#6ee7b7'
const fg = ['#ecfdf5', '#d1fae5', '#a7f3d0', '#6ee7b7', '#34d399', '#10b981', '#059669', '#047857', '#065f46', '#064e3b']
const target = 'aa'
colorContrast(bg, fg, target) // -> '#065f46'
colorContrast(..., 'hex') // -> '#065f46'
colorContrast(..., 'rgb') // -> 'rgb(6, 95, 70)'
colorContrast(..., 'rgb-array') // -> [6, 95, 70]
colorContrast(..., 'hsl') // -> 'hsl(163, 88%, 20%)' (rounded)
colorContrast(..., 'hsl-array') // -> [163.14606741573033, 0.8811881188118811, 0.19803921568627453] (not rounded)
Arguments to color-contrast()
wrapped onto multiple lines for clarity.
Input | Output | Image |
---|---|---|
.container {
background-color: #0f172a;
color: color-contrast(
#0f172a vs
#e5e5e5, #171717
);
} |
.container {
background-color: #0f172a;
/*
picks the color in the list
(after "vs") that has the
highest contrast ratio with
the first color
*/
color: #e5e5e5;
} |
|
.container {
background-color: #6ee7b7;
color: color-contrast(
#6ee7b7 vs
#ecfdf5, #d1fae5,
#a7f3d0, #6ee7b7,
#34d399, #10b981,
#059669, #047857,
#065f46, #064e3b
to AA
);
} |
.container {
background-color: #6ee7b7;
/*
with a target contrast:
picks the first color
that meets or exceeds
the target contrast ratio
*/
color: #065f46; /* 5.41:1 */
} |
|
.container {
background-color: #c2410c;
color: color-contrast(
#c2410c vs
#e5e5e5, #171717
to AA
);
} |
.container {
background-color: #c2410c;
/*
defaults to black or white
if the target contrast cannot
be met with the provided colors
*/
color: #fff;
} |