Open LeaVerou opened 2 years ago
this is such a good point and really nice example of the problem space.
the browser is uniquely positioned to alpha blend, but you're right that how far can that really go? if the 1st parameter was considered the "background", the UA could play out a subtree layout (hopefully a short as it can be) that finds the nearest opaque color for use in alpha blending insomuch to compare the list of colors to. transparent foreground colors are then alpha blended against that subtree composited opaque color result. but just because the browser can, maybe it's too much to ask? there's gotta be a better way.
I suspect the most reliable answer is indeed to assume the first color is a bg and the list is choosing a fg color, do alpha blending if the fg colors are transparent, and do a range if the bg color is transparent. If our assumptions about fg/bg are opposite the actual usage the numbers would end up being different, but not too much iiuc.
assume the first color is a bg and the list is choosing a fg color
We need to provide both - select a bg for a given fg color, and select a fg for a given bg color. Unlike WCAG 2.1, these are not mathematically the same when calculating contrast.
Note that while "use the range for semi-transparent colors" is scary, what you actually need is to use the min contrast. If the min contrast passes, the entire range passes.
The CSS Working Group just discussed transparency
, and agreed to the following:
RESOLVED: When calculating color contrast between pairs of semi transparent colors, if the background is opaque, UAs should alpha blend the foreground on the background and use that as the foreground color. If the background is also semi-transparent, it would be alpha blended first with an opaque version of the foreground
RESOLVED: Amend previous resolution, the canvas color in previous resolution is TBD
RESOLVED: (rather than opaque version of foreground)
possible options for the canvas:
Agenda+ to resolve on color the background is composited on, which is what remains to close this (and is blocking shipping even the reduced contrast-color()
that we resolved to pull into css-color-5).
In the current draft, transparency is ignored when calculating contrast, because it was ignored in WCAG 2.1 as there were no non-opaque colors then.
Notice that APCA does specify blending the text color onto the background color using the text alpha, then computing the L ccontrast of the blended result against the (assumed fully opaque) background. Of course this is made easier because the APCA formula requires specifying which is text and which is background :)
To me, this means that all of the color contrast functions in CSS need to explicitly say which parameter is foreground and which is background (there are various ways to do that, such as keywords or parameter order). It no longer matters that many formulae give the same result if foreground and background get swapped; none of them give the same result once foreground transparency is used to modify the effective foreground color.
To me, this means that all of the color contrast functions in CSS need to explicitly say which parameter is foreground and which is background (there are various ways to do that, such as keywords or parameter order). It no longer matters that many formulae give the same result if foreground and background get swapped; none of them give the same result once foreground transparency is used to modify the effective foreground color.
This is not under debate, we resolved in #7359 that the syntax needs to distinguish foreground and background, but we have not resolved on the exact syntax (and on whether there is a default).
Another option for the default against which to composite non-opaque background would be Canvas
.
Whatever default we pick, there will be cases where it's wrong, so it would be nice to offer a way out. How can the author select what the background is composited on? What if we add a color-over()
function to do alpha compositing? Pretty sure there are other use cases for it too, and it should be relatively easy to implement.
The CSS Working Group just discussed [css-color-6] color-contrast() should take transparency into account
.
I separated the possibility of a compositing function into #8431.
A few brief thoughts on finding the "worst case" for a transparent background: for a start, I think when we're looking for the worst case, we should be looking for the worst case under the assumption that the colors are being used in a surface that's fully opaque, uses source-over compositing and normal blend mode. Cases beyond that aren't the problem of the color contrast algorithm. An example (of the "not the contrast algorithm's problem" case):
<div style="background: black">
<div style="background: black; color: white; mix-blend-mode: darken">
This text is black on black!
</div>
</div>
A very naive possibility for finding the worst case backdrop is to choose an R, G, and B component for the backdrop such that compositing the (partially transparent) background color on top of the backdrop will produce R, G, and B results that are as close as possible to the R, G, and B components of the foreground color.
I think this naive possibility does find the cases where it's possible to get to zero contrast with a particular backdrop, but probably doesn't get to minimal contrast for the cases where you can't get to zero. An example zero-contrast case is:
<div class="backdrop" style="background: rgb(155, 240, 200)">
<div style="background: rgba(255, 140, 0, 0.4); color: rgb(195, 200, 120)">
This text is not visible!
</div>
</div>
Note that this case has (I think) reasonable contrast against a black backdrop (though not against white).
Under WCAG 2.x, it is a failure to specify a text color without also specifying a background color or vice versa: https://www.w3.org/TR/2016/NOTE-WCAG20-TECHS-20161007/F24
By definition, specifying a transparency without specifying the color that transparency exists on top of, is the same as failing to fully specify the color at all.
This does not mean that a given element must have both a foreground and background color, it only means that at some point in the cascade an unambiguous foreground and unambiguous background color can be determined (or none).
What is required, is only one of these cases:
This is true under WCAG2, and I would expect it to remain true under WCAG3 as it would be a mistake for it not to be. However it's easier said than done. See my next post for the deeper dive.
Obviously, compositing over images and compositing over gradients bring up additional and not-well-resolved issues.
Something to point out here, the background color is commonly specified in an element that is much higher up the DOM tree than the text or other foreground color. Most of the time to do the calculation needed for the color-contrast() function, there will be the need to trace up the DOM tree for the corresponding color(s) at the given spatial coordinates.
A background color with transparency is inherently ambiguous, as it requires tracing back not only to the previous background color, if that color is also transparent than multiple steps up the DOM tree, but also, foreground colors, gradients, effects, not to mention what happens when a transparent background of a div is straddling multiple underlying backgrounds, or the more common instance of underlying images, all may have to be considered.
Since the APCA tool was mentioned, one of the reasons it presently only offers alpha for text: It's trivial to use a text color with transparency, if the background color is fully opaque. However it is quite far out of the question to take a transparent background color as there are too many variables without known constraints or rules as to what that transparent background color might be composited over.
In the example below, admittedly a slightly contrived case for illustration purposes, the rasterizer is providing four different background colors behind one single text color, not counting the image of course....
Click for full size
Calculating contrast for each pixel is awfully expensive, but there's something more important that needs to be discussed.
Both the div called "Top Div" and the first explanatory div, have their backgrounds set at 60% opacity or 40% transparency. The BG color for the first one is black with white text, the BG color for "Top Div" is white with black text.
The readability and the apparent opacity is quite different between the two. The linear opacity is being applied to gamma¹ encoded sRGB color values while compositing.
Here is the same example except that foreground and background are reversed for the two top most divs.
Click for full size
Here's an extreme close-up of the upper and lower div's text, Side by side for comparison.
Click image to zoom
And we haven't even talked about other possible transfer modes. The point I'm getting who here is the additional ambiguity that arises from making the background color transparent, when a solid background should be the foundation that text is displayed upon.
What I hoped to illustrate above more than anything else, was that doing automated color with transparent backgrounds has potentially very poor results, unless some reasonable constraints or rules are applied.
There are certainly plenty of good uses for having selectors that are set up with transparent backgrounds for elements in a design system. But the issue that comes up is the one we discussed last year, that of automated adjustment. Automated means that no human is looking at it to evaluate or make the adjustment. As such:
If a human designer is evaluating, that's one thing—but for an automated tool that deals with color appearance, then a means must be provided to replace the eyes of a human designer with that of a standard observer.
If that's not directly possible or practical, then appropriate rules must be in place to constrain the use of transparent backgrounds.
Example of a reasonable rule set:
#ffffff
opacity must be greater than 0.7#a0a0a0
opacity must be greater than 0.9#000000
opacity must be greater than 0.5#909090
opacity must be greater than 0.8Disclaimer: these opacity values have not been thoroughly evaluated, and should not be considered normative, only here as an example for this discussion.
Transparent backgrounds present a lot of problems for any automated color adjustment control. The potential for misuse and abuse is great. If the use of transparent backgrounds is to be permitted in conjunction with automation, reasonable constraints need to be in place.
Thank you for reading
Andy
@Myndex not sure what you are proposing. <color>
values in CSS can have transparency. contrast-color()
accepts <color>
values, therefore it needs to do something reasonable with transparency. Feel free to do all the advocacy you want that people shouldn't do that, the function still needs to return something reasonable.
Maybe just have the function fail (and the line ignored) if any of the parameters are transparent? That way a basic version can be released, with the difficult questions about transparency can be answered later - and at that point references using transparency would start working as per progressive enhancement.
We can't ignore the line, since the exact color being operated on might not be known until used-value time, far after parsing and cascading. So at that point we're stuck with the property value we've got and have to get some color out of the function.
not sure what you are proposing.
Hi Lea @LeaVerou
I was not proposing anything specific per se, just opening the conversation into something important, at least in terms of accessibility.
It also fails APCA-RG¹ and I should expect that it will fail the future WCAG_3.
...it needs to do something reasonable with transparency...
I agree, and that was the point of my post. It needs to do something reasonable. To me, reasonable means that a there are constraints that prevent certain edge cases, and also prevent common cases that result in ambiguous or undefined colors.
So this means error handling.
To me, an error should default in the direction of accessibility, itch probably means that in the case of an error, that it spits out a solid, opaque color as the fallback for the given text. Nevertheless we have to recognize that this could create its own separate accessibility issue if it covers an element that is needed for interaction for instance.
Okay then, maybe if color-contrast() is passed a colour that isn't fully opaque, it treats it as though it is and prints a warning to the dev console (an error maybe even)?
@Myndex This is a general comment, but also applies to many of your replies here: it is not constructive to these conversations to flood them with super long comments that are pages and pages of musings. This is not the right venue for elaborate colorful prose. Please spend the time to shorten your arguments to what is actionable and relevant to the topic at hand. If you are so inclined, you can put additional background information in <details>
or link to it, so that those without the time to dive in (likely the majority of us) can just read what that is immediately relevant, and discussion can continue.
I understand that when you really care about a topic, and have spent a considerable amount of your time studying it, it is often tempting to write lengthy messages dumping all of the info on other people and/or having some fun with what you're writing. In my experience, it actually has the opposite effect. Either it halts the conversation, because people cannot invest that kind of time, or it gets ignored, or it gets skimmed with readers picking up bits here and there. It's much better to condense it yourself, since you know which bits are important much better than others skimming. As for the colorful prose, there's a time and a place, and technical discussions in a WG is not it.
For example, if I'm readingskimming your message above correctly, it could have been condensed to:
Laws do not actually mandate WCAG 2.1 or any other specific specification. They instead mandate actual accessibility, and APCA is strictly superior in that. [source]
The source could even be one of your articles, including all of the above, but this thread is not the place for posting full-blown articles.
@LeaVerou
Thank you for your feedback, if you haven't noticed I actually am using the details/summary
tags more often lately in many of my messages,
Though in the message above, I originally indicated the tangent-break with a header instead.
Based on your commentary I have edited that message to insert the detail/summary tags as you suggest, I hope it meets with your approval.
In the current draft, transparency is ignored when calculating contrast, because it was ignored in WCAG 2.1 as there were no non-opaque colors then.
However, this could result in completely incorrect results.
Consider this example:
color-contrast(white vs rgb(0 0 0 / .1), gray)
. Because transparency is ignored, it would pickrgb(0 0 0 / .1)
as most contrasting, which produces this result:whereas picking
gray
would have produced this much more legible result:At the very least, UAs should perform alpha blending of background and foreground (see https://github.com/w3c/csswg-drafts/issues/7359 ). This would be sufficient when the background is opaque, but it does not solve the problem when the background is also semi-transparent. Ideally, UAs would calculate a range of contrasts and a color would be deemed a winner IFF the minimum possible contrast also passes the desired level (I did some work on calculating said range in WCAG 2.1 10 years ago, not sure if there's a better method these days)
(Issue filed following breakout discussions between @svgeesus, @fantasai, @argyleink and myself)
Resolutions
Aug 2, 2022