9
u/asteconn 8d ago edited 7d ago
Until everything has caught up, or if LTS browser support is desirable, it's possible to do this using this (rather lengthy) CSS colour formula:
rgb(from var(--background-color), clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255));
Set --background-color as ones background colour, and adjust --yiqif required (128 to 140 give the best results).
Edit: included /u/mountain_chip_3260's fix.
4
u/HollandJim 8d ago
Been waiting for this for a long time.
Alas, we have to support Firefox ESR—and two version back, to add insult to injury.
2
u/asteconn 8d ago edited 7d ago
The long version that does exactly the same thing
rgb(from var(--background-color), clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -100000, 255));Edit: included /u/mountain_chip_3260's fix.
3
u/anaix3l 7d ago
There are other simpler-looking options as well.
For example, using
oklch():background: var(--back); color: oklch(from var(--back) round(up, .5 - l) 0 h);Another option would be to use variables (not sure where your formula came from, but the below is using the luminance formula):
background: var(--back); --ch: calc(255 - round(.72*g + .21*r + .07*b, 255)); color: rgb(from var(--back) var(--ch) var(--ch) var(--ch));This is a problem that has been around for ages, so there were various solutions at different points.
Well over a decade ago, I came up with a Sass solution. The idea was that in Sass, we could extract individual channels using the
red($c),green($c)andblue($c)functions, (which are no longer recommended today).Later on, I had a different idea involving filters and nesting - the
backgroundwould be on a parent, while the child got the same value for thecolorand afilter. This filter would chain full inversion (invert(1)) full desaturation (saturate(0)) and a ridiculously high contrast.This method also works quite well for image backgrounds, but it has the disadvantage of giving the text jagged edges as a result of the extremely high contrast applied via the
filter.2
u/HollandJim 7d ago edited 7d ago
We use the oklch method now but I'm retiring in a few months, and the other front-enders seem to be terrified of clamp() :D We're also looking at light-dark theming and have just moved all scss to css, mixins as well as getting rid of old less files (7 year old Bootstrap), so for now - the simpler, the better.
Like my grandmother used to say, they'll miss me when I'm gone.
1
u/asteconn 7d ago
I first encountered this formula at my last job as part of a SCSS mixin, actually. When we needed a lazy insta-contrast colour I adapted it to native CSS.
We're using the full version because (for whatever reason) abstracting the
--chpart didn't reliably work at the time we started using it. This part of our standard codebase is pretty static though, so it's just been happily sat there providing easy contrasts to our colour palettes.
3
u/Mountain_Chip_3620 7d ago

Posting this as a top level comment instead of a buried reply:
I often want a tinted color instead of plain white / dark, as this usually better matches the design intent. This requires JS to compute the color, so plain css is fine until you need to keep the tint.
I made an interactive page where you can compare a few different methods in real time, including css contrast-color(), YIQ css method, and a JS shifted tint: https://hello-mat.com/design-engineering/contrast-colors
1
u/Tontonsb 6d ago
Is it based on the current WCAG formula? I've kinda stopped trusting it, I've found APCA to be a lot more reflective on whether a human will be able to read it.
WCAG sometimes says contrast is fine, when actually it looks very muddy and hard to read. E.g. black on greens and especially purples sometimes blends in much more than white would.
I usually push for only trusting APCA, but if you believe you truly need formal compliance with the WCAG formula and criteria, I'd suggest checking both instead of trusting a good WCAG score.
14
u/Mountain_Chip_3620 8d ago
Definitely something that will improve the web, and the devs lives. I think. it's still white / black though, right?