w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.44k stars 657 forks source link

[css-color-6] `contrast-color()` syntax proposal #7954

Open argyleink opened 1 year ago

argyleink commented 1 year ago
contrast-color() = contrast-color(<color-role>? <color> / <contrast-target> <contrast-algorithm> <color>#{2,})

<color-role> = background | foreground
<contrast-algorithm> = auto | wcag | lstar | apca | weber | michelson
<contrast-target> = auto | max | more | less | <custom-contrast-target>
<custom-contrast-target> = wcag21 ratio | APCA ratio | Lstar ratio | Weber ratio | Michelson ratio | <percentage>]

This proposal intends to empower the prefers-contrast media query by creating a function that implements the options of the media query, is simple to get started with, but can scale to advanced needs of authors and users. I feel, and others have provided feedback, that the current proposal is becoming unwieldy for the average developer and is lacking an automatic choice feature.

Since contrast is such a visual thing, I needed to build an interactive syntax so I could see and feel out the syntax results. I chose ObservableHQ as it allows creating transparent and inspectable logic and documentation, plus the ability to make things interactive and real time.

Interact with the syntax, read finer details about the implementation, all here: https://observablehq.com/@argyleink/contrast-color

Demo usage of the interactive proposal:

https://user-images.githubusercontent.com/1134620/197878368-1b30e1f2-b7de-496b-83b4-3692cb81cec3.mp4

Some notable things to observe in the proposal and video are:

  1. auto discovery of nearest in hue color match (no color list required) contrast-color(#eee)
  2. background is assumed but foreground can be specified contrast-color(foreground #eee)
  3. browsers choose the default algorithm when one isnt specified contrast-color(#eee)
  4. authors can specify the algorithm, but it's a request, and the browser can fallback to it's default if not equipped with the request algorithm contrast-color(#eee / apca)
  5. the contrast target accepts prefers-contrast media query keywords like more and less and maps them to the algorithm's equivalent of contrast, but if left out of the function (auto) the browser uses the users current contrast preference from the OS contrast-color(#eee / more wcag21) same as contrast-color(#eee / 7 wcag21)
  6. the contrast target accepts max as a keyword, simplifying the request for white or black depending on which contrasts more contrast-color(#eee / max)

The goal is to encourage usage of auto so users preferences are respected and remain dynamic. This means most usage of the function will be short and sweet.

.icon {
  background: #23c9c8;
  color: contrast-color(#23c9c8);
}

But, if experts want to, they can grab the reigns and really specify what they want like:

.icon {
  background: #23c9c8;
  color: contrast-color(background #23c9c8 / 4.5 wcag21 bisque, var(--accent-color), olive, sienna, color(display-p3 0 1 0), maroon);
}

From presenting the previous syntax a few times to groups of devs, staying current with the current issues logged against this contrast function, and wanting the previous WG resolutions on contrast preference handling, this spec proposal attempts to sum as much of it up as it can, while being terse and approachable.

There's plenty of details to talk about, but this proposal feels closest to the desirable developer experience I acquired in feedback. It also aligns functionality more with existing contrast resolutions made by the CSSWG. I'd like to present the proposal and accept questions about details.

The Observable document has a section called Explanations and Reasoning that should help answer a few of the common questions:

Screen Shot 2022-10-25 at 2 10 51 PM

Have fun! I feel like it's quite nice having something visual and playful to work with instead of just reading theoretical syntax over and over. You can go try it out, see results, and discover the syntax you'll want in your project.

argyleink commented 1 year ago

related #7937

argyleink commented 1 year ago

related #7553

tabatkins commented 1 year ago

Some miscellaneous critique:

bramus commented 1 year ago

Wow, that ObservableHQ demo is sweet! 🤩

Not looking at the details/order of the syntax, I do see all the necessary parts are there. It reminds me of this syntax I suggested:

More syntax ideas:

  • contrast-color(foreground red using wcag2(AA) vs blue, white, yellow)
  • contrast-color(foreground red using wcag2(AA) / blue, white, yellow) (to make it more clear that it’s not 4 comma-separated items but something measured against 3 items)

The main components are there, similar to Adam’s proposed syntax. Key difference though is that the <contrast-algorithm> is a function that accepts the wanted <contrast-target> as an argument.

This allows an algo to have more than 1 argument. The (unfinished) wcag3 for example also takes font weight and size into account, which could then be expressed as wcag3(75, font-size, weight).

argyleink commented 1 year ago

This allows an algo to have more than 1 argument. The (unfinished) wcag3 for example also takes font weight and size into account, which could then be expressed as wcag3(75, font-size, weight)

engineering made it sound like they could derive that as they needed and wouldnt require authors to pass it explicitly to fulfill the algo. hopefully authors wont have to know that much about the api surface area of a contrast algorithm to use it right.

something to consider..

The longer this function sits, the more that folks will use cielab to estimate L differences for contrast. [See this post here where the author suggests we don't need color-contrast()](https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl#:~:text=body%20%7B%0A%20%20background%3A%20var(%2D%2Daccent)%3B%0A%20%20/%20We%20do%20not%20need%20to%20detect%20text%20color%20with%20color%2Dcontrast()%0A%20%20%20%20%20because%20OKLCH%20has%20predicted%20contrast.%0A%20%20%20%20%20All%20backgrounds%20with%20L%E2%89%A575%25%20have%20good%20contrast%20with%20black%20text.%20/%0A%20%20color%3A%20black%3B%0A%7D). I've considered this before as well, as relative color syntax allows me to ask for a calculated L difference easily, completely bypassing all these conversations about which contrast algorithm is the best. there's a number of problems with this doing this:

  1. delta 60 is an estimate for roughly wcag ~4.5, so it may be more or less than needed
  2. it doesnt take into account the users contrast preference
  3. it doesnt know foreground from background

All things that contrast-color() should do for authors, like in this proposal.

BUT, this L* calc() ability is about to be in hands before contrast-color(). Even without relative color syntax the math can be done on a per channel basis with custom properties in a cielab space: background: oklch(calc(var(--bg-l) + 50) c h). The sooner we can resolve on this function, the more folks we can save from attempting to bake accessibility into their colors themselves.

argyleink commented 1 year ago

I've considered this before as well, as relative color syntax allows me to ask for a calculated L* difference easily, completely bypassing all these conversations about which contrast algorithm is the best.

https://codepen.io/argyleink/pen/eYKmMmN

Myndex commented 1 year ago

Hi Adam @argyleink

This proposal intends to empower the prefers-contrast media query

I just came across this thread, I am here to answer any questions that might come up or to provide assistance.

As I think you are aware, user personalization is the most important thing that can be done for accessibility. If real effective user personalization can be had, then many other concerns about accessibility become very secondary or even moot.

As such I've been considering some of the needs that could potentially be addressed by appropriate media queries, such as prefers contrast or theme, etc.

Conflicts of Needs

One issue is that what one user needs, another user may find harmful or interferes with their needs. Visual presentation is probably one of the more complicated and common situations where this issue is present.

There are easily 10 basic themes or contrast modes, I put the supporting minutia in the spoiler.

Visual Needs/Themes examples In an ideal world, the user's environment or at least the entire screen luminance would be taken into consideration. As well, the use-cases relating to tasks, i.e. reading long form material, or interacting with forms, have two different needs in terms of contrast and presentation. Some arguably useful luminance and contrast themes: Dark Mode for dark environments Dark mode for light environments Enhanced contrast for dark mode Light mode for bright environments Light mode for dark environments Light mode for the paper reading experience Enhanced spatial contrasts (i.e. weight increase) Luminance contrasts relate to readability for all sighted users, color (hue/colorfulness) relate to distinguishability and discernibility. Reduced color (hue/chroma) contrast (without reducing luminance contrast) Daltonized palette (deutan) Daltonized palette (protan) ### _Reading_ Luminance contrast is critical for visual readability for all sighted users. Excess brightness as in too much luminance relative to the eye's adaptation state in their environment leads to rapid eye fatigue. This is an emerging problem with people that are using HDR displays. "Too much contrast" is a complaint given for what is actually excess brightness. Reducing contrast with a bright background by making the text a lighter gray is exactly the _opposite_ correction needed. More: ["Please Stop sing Grey Text"](https://tangledweb.xyz/please-stop-using-grey-text-3d3e71acfca8)

10 different modes is far too much to ask an author to provide, what is needed is automated theme variations, such as creating a Daltonized palette.

Automated color palette development requires perceptual uniformity in whatever model is being used.


Hi @bramus + @argyleink

which could then be expressed as wcag3(75, font-size, weight)

engineering made it sound like they could derive that as they needed and wouldnt require authors to pass it explicitly to fulfill the algo.

You definitely do not need to send APCA the font size and weight.

If you have one color, you then only need a target Lc value (lightness contrast) to determine the needed second color.

If you don't have an $L^C$ value in mind, you can use font size & weight of the reference font to determine the needed $L^C$ value.

Lc, Weight, and Size In other words, if you have one color, with font size and weight, then you can determine the second color. Or if you have one color and an $L^C$ value you can determine the second color. However, for font size & weight to really work, we need to have a reliable way to set x-height directly, and a reliable way to determine a font's actual weight since there's no standardization here. This is something that we're working on, but it's not something that's gonna be happening anytime soon.

Hi @tabatkins

...almost certainly far too dangerous to allow an auto algorithm, except maybe if it was restricted to only giving black/white.

I agree that automatically selecting some algorithm is fraught with a lot of unexpected behavior. A black and white font flipper could work, but you don't need much of an algorithm for that.

You can pretty much convert to luminance and flip between black and white at $37\ Y$ I discuss this at the FancyFontFlipping repo


Hi @argyleink

the more that folks will use cielab to estimate L* differences for contrast.

Unfortunately $∆L$ is no improvement over WCAG 2.x. Both $L$ and WCAG_2 put center contrast at #777777, and that is not the center of contrast.

I was working with $∆L*$ early in 2019, and found it provided no benefit on its own, however much of that work lead directly to developing SAPC which then developed into APCA.

The tangent is DPS contrast a.k.a. Delta Phi Star, which uses $L*$ as the starting point.

Summary

I hope this post was useful please ping me if you have questions.

Thank you for reading

astearns commented 1 year ago

Presented and discussed in https://github.com/w3c/csswg-drafts/issues/7937#issuecomment-1431788247