Csswg-drafts: [css-color-5] color-contrast() needs resolution logic if 2 or more have the same contrast

Created on 3 Feb 2020  路  14Comments  路  Source: w3c/csswg-drafts

Spec draft
https://drafts.csswg.org/css-color-5/#colorcontrast

Example

foo {
  --bg: hsl(200 50% 80%);
  --purple-in-hsl: hsl(300 100% 25%);
  color: color-contrast(var(--bg) hsl(200 83% 23%), purple, var(--purple-in-hsl));
}

verify contrast scores: [[hsl(200 83% 23%)](https://contrast-ratio.com/#hsl%28200%2083%25%2023%25%29-on-hsl%28200%2C50%25%2C80%25%29), [purple](https://contrast-ratio.com/#purple-on-hsl%28200%2C50%25%2C80%25%29), [var(--purple-in-hsl)](https://contrast-ratio.com/#hsl%28300%20100%25%2025%25%29-on-hsl%28200%2C50%25%2C80%25%29)]

Given purple, hsl(200 83% 23%), & hsl(300 100% 25%) all have the same contrast with --bg, which wins? First match, cascade, your idea?

Closed Accepted by Editor Discretion css-color-5

Most helpful comment

makes me wonder where the implementers will get their scoring algorithm from

The algorithm is given in WCAG 2.1 and the inputs are the relative luminances of the two colors being compared.

In utilities.js I have added that function, for convenience, although the inputs are sRGB only (same as WCAG):

function contrast(RGB1, RGB2) {
    // return WCAG 2.1 contrast ratio
    // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
    // for two sRGB values
    // given as arrays of 0.0 to 1.0

    var L1 = sRGB_to_luminance(RGB1);
    var L2 = sRGB_to_luminance(RGB2);

    if (L1 > L2) return (L1 + 0.05) / (L2 + 0.05);
    return (L2 + 0.05) / (L1 + 0.05);
}

I also have a function sRGB_to_luminance(RGB) in utilities.js, primarily to help writing examples.

Notice that relative luminance is just the Y in CIE XYZ and conversions.js provides sample code to convert any of the predefined colorspaces to XYZ (for the rgb spaces, in two steps: undo gamma correction, then convert linear rgb to XYZ). The same goes for Lab to XYZ (two steps, convert to D50 XYZ then adapt to D65 XYZ) and LCH to XYZ (three steps, first convert LCH to Lab).

And even for calibrated spaces like CMYK etc, if a color profile is provided.

It is always just a case of computing two luminances, Y1 and Y2, then doing the simple ratio (with 5% flare compensation).

Luminance has been defined by an international standard since 1931, we don't need to define it in CSS just document how it is calculated, to help developers.

All 14 comments

Good catch, @argyleink I agree this needs to be specified

Given that the second parameter to color-contrast is a list of alternatives, then similar to other lists in CSS I suggest that the earliest match wins. In other words the logic is:

  • if the list has one item, it wins
  • if the list has two or more items, the first is the temporary winner but the next takes the winning place if it is better, and so on down the list

We also need to specify what happens if the first param is not a valid color, or any item of the second param are not valid colors.

  • thus, a later item with the same score as an earlier item will not win.

@una @LeaVerou should I go ahead and spec "first in list wins" or do you have other ideas on a resolution?

Yeah, since there's no further context to decide ties, going with first-wins seems reasonable.

Not so fast on the closing, Chris :)

We also need to specify what happens if the first param is not a valid color, or any item of the second param are not valid colors.

verify contrast scores: [hsl(200 83% 23%), purple, var(--purple-in-hsl)]

Note that these all give the same contrast of 6.08, when rounded to 3 significant figures as your tool does, @LeaVerou but when a higher precision is used, purple wins. I made that point in the spec text.

first wins, i dig it.
and nice catch on the higher precision contrast score! makes me wonder where the implementers will get their scoring algorithm from and whether or not we need to provide it (or one that works with CIE and offers higher precision) in the spec?

makes me wonder where the implementers will get their scoring algorithm from

The algorithm is given in WCAG 2.1 and the inputs are the relative luminances of the two colors being compared.

In utilities.js I have added that function, for convenience, although the inputs are sRGB only (same as WCAG):

function contrast(RGB1, RGB2) {
    // return WCAG 2.1 contrast ratio
    // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
    // for two sRGB values
    // given as arrays of 0.0 to 1.0

    var L1 = sRGB_to_luminance(RGB1);
    var L2 = sRGB_to_luminance(RGB2);

    if (L1 > L2) return (L1 + 0.05) / (L2 + 0.05);
    return (L2 + 0.05) / (L1 + 0.05);
}

I also have a function sRGB_to_luminance(RGB) in utilities.js, primarily to help writing examples.

Notice that relative luminance is just the Y in CIE XYZ and conversions.js provides sample code to convert any of the predefined colorspaces to XYZ (for the rgb spaces, in two steps: undo gamma correction, then convert linear rgb to XYZ). The same goes for Lab to XYZ (two steps, convert to D50 XYZ then adapt to D65 XYZ) and LCH to XYZ (three steps, first convert LCH to Lab).

And even for calibrated spaces like CMYK etc, if a color profile is provided.

It is always just a case of computing two luminances, Y1 and Y2, then doing the simple ratio (with 5% flare compensation).

Luminance has been defined by an international standard since 1931, we don't need to define it in CSS just document how it is calculated, to help developers.

I should probably add another contrast example where the color list includes display-p3, prophoto-rgb, and lab and lch values.

feels like there's clear resolution on this issue, it's ready to close yeah?

We also need to specify what happens if the first param is not a valid color, or any item of the second param are not valid colors.

Both of those cases are "doesn't match the grammar" and thus are invalid, right?

Yes, I assumed so. Or, if a custom property is involved and the parser can't tell, invalid at computed value time.

Do we need t say either of those things explicitly or is it just assumed?

They're just assumed. If you write something into a property that takes <color> that doesn't match the grammar of anything defined under the <color> nonterminal, it's invalid and the appropriate error-handling happens automatically.

OK in that case this can indeed be closed.

Was this page helpful?
0 / 5 - 0 ratings