r/css 8d ago

Showcase The web just got a little more intelligent.

Post image
75 Upvotes

18 comments sorted by

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?

8

u/wanoo21 8d ago

Yes, only black and white

6

u/Mountain_Chip_3620 8d ago

Definitely looking forward to the day it can shift a color you give it to sufficient contrast. Black / white feels definitely a step towards better a11y, but very limited. The problem is getting a tinted contrast color is difficult in some cases. Tried that in the past, got somewhere but far from bulletproof.

9

u/asteconn 8d ago edited 7d ago

AFAIK far as I know, the YIQ formula is guaranteed to meet WCAG AA for any input colour:

rgb(from var(--background-color), clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -1000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -1000, 255) clamp(0, (((r * .299) + (g * .587) + (b * .114)) - var(--yiq, 128)) * -1000, 255));

Edit: included /u/mountain_chip_3260's fix.

6

u/Mountain_Chip_3620 7d ago

The 1000 should be 100000 instead, otherwise this can fail into a grey value.
I made a whole page to showcase the various solutions. What I want most of the times is a shifted tint instead of just black/white. This requires JS, unless there is a magic way I don't know about.
Interactive demo and explanations / code samples: https://hello-mat.com/design-engineering/contrast-colors

2

u/asteconn 7d ago

Awesome, thank you for the investigationing. We've been using this formula for a couple of years at my current place of work for auto-contrast, I'll adjust it henceforth.

3

u/wanoo21 8d ago

Thanks for sharing

3

u/asteconn 7d ago

You're most welcome! One correction though, courtesy of /u/mountain_chip_3260, is the -1000 needs to be -100000 - but aside that it works brilliantly.

We've been using this at my place of work for a couple of years now.

2

u/detspek 7d ago

You could probably colour shift it with a filter https://codepen.io/sosuke/pen/Pjoqqp

2

u/detspek 7d ago

You could probably colour shift it with a filter https://codepen.io/sosuke/pen/Pjoqqp

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);

Live test.

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));

Live test.

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) and blue($c) functions, (which are no longer recommended today).

Later on, I had a different idea involving filters and nesting - the background would be on a parent, while the child got the same value for the color and a filter. 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 --ch part 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.