Contrast color adjuster does not work as expected
monospaced opened this issue · 7 comments
Test case: https://github.com/monospaced/post-css-color-mod-function-contrast-test
$ git clone git@github.com:monospaced/post-css-color-mod-function-contrast-test.git
$ cd post-css-color-mod-function-contrast-test
$ yarn start
The resulting CSS in styles.processed.css does not match my understanding of the contrast-adjuster spec (or the behaviour in postcss-color-function).
For example, I would expect
.maxContrastOnWhite {
background-color: rgb(255, 255, 255);
color: color-mod(rgb(255, 255, 255) contrast(100%));
}to be transpiled to
.maxContrastOnWhite {
background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);
}rather than
.maxContrastOnWhite {
background-color: rgb(255, 255, 255);
color: rgb(255, 255, 255);
}I’m not sure why you think the conversion is incorrect. I follow the spec you’ve linked to and the code includes a break down. See https://github.com/jonathantneal/postcss-color-mod-function/blob/master/lib/color.js#L291-L320.
Could you be more specific? Closing until there is something more specific than it being unexpected to you.
It's not just my expectation, the behaviour in my test case forpostcss-color-mod-function is also the exact opposite of how postcss-color-function handles contrast().
So either there is something wrong with my test case, or one of postcss-color-mod-function or postcss-color-function implements contrast incorrectly.
I noticed this when migrating from postcss-next to postcss-preset-env. Simply swapping out postcss-color-function for postcss-color-mod-function resulted in all contrast colors being inverted.
contrast is intended to return a color that provides sufficient contrast with the provided base color.
So (#fff contrast(100%) should return #000, but postcss-color-mod-function returns #fff. It's the exact opposite of what the correct result should be.
Hey @monospaced, I feel your pain, as I tried for a long time to convince the postcss-color-function folks to update their plugin. I did not want to re-write this little behemoth.
When you looked through my source, it included links to the spec. Did I incorrectly implement the spec? Unless I implemented something incorrectly, I can’t maintain backwards compatibility with a plugin that incorrectly implemented the spec to begin with.
I am 100% open to changing the plugin if I have implemented the spec incorrectly, or if the spec changes.
I think you must have incorrectly implemented the spec somehow?
Because according to the spec, this should return #000000, as it's the color with 100% contrast to the supplied base-color #ffffff:
color-mod-function(#ffffff contrast(100%))
But your implementation returns #ffffff, which clearly does not contrast at all with#ffffff, being the same color.
Purely at the level of common sense, I think that has to be wrong, and not the intention of the contrast function as specced.
@jonathantneal Could we please reopen this? I find @monospaced very specific, and the spec backs his comments very clearly:
‘’contrast( ? )‘’
Finds a color that contrasts with the base color suffficiently to satisfy accessibility guidelines, using the definition of “contrast” given by WCAG 2.0 Guideline 1.4.3.The specifies the desired similarity between the base color and the returned color. 0% will return the minimum-contrast color (the closest color to the base color that still contrasts sufficiently), while 100% will return the maximum-contrast color (white or black, whichever contrasts the base color more) Specifying a value less than 0% or greater than 100% is invalid and a syntax error. If omitted, the defaults to 100%.
Quickly commenting here for the next person looking for this.
It seems that the calculation for luminance and thus for contrast ratio is broken.
When using the rgb2luminance() method here : https://github.com/csstools/postcss-color-mod-function/blob/main/lib/color.js#L336
For example:
rgb2luminance(0,0,100)(red) the returned value is4011.4779151491966(should be0.21)rgb2luminance(100,100,100)(white):0(that's correct)rgb2luminance(0,0,100)(black):55560.63594389468(should be1)
Since the value for the luminance should be between 0 and 1, I suspect something weird there.
After some investigation it seems that the channel2luminance function is already incorrect:
https://github.com/csstools/postcss-color-mod-function/blob/main/lib/color.js#L336
The implementation here returns the correct values:
function channel2luminance(value) {
// https://drafts.csswg.org/css-color/#luminance
const ratio = value * 2.55 / 255;
return ratio < 0.04045 ?
ratio / 12.92 :
Math.pow((ratio + 0.055) / 1.055, 2.4);
}
@romainmenke cc ^