jfsiii / chromath

JavaScript color conversion and manipulation functions
https://jfsiii.github.io/chromath/
190 stars 13 forks source link

Contrast options #7

Open konsumer opened 9 years ago

konsumer commented 9 years ago

Adds invert, so you can get a color that is perfectly readable over any color, identical to "Image > Adjustments > Invert Color" command in Photoshop. Got function from here.

Adds contrastText so you can get a super-readable text color for any background. Got function from here (getContrastYIQ)

konsumer commented 9 years ago

Not sure what the fail is, I think it's unrelated to what I did: Test. (/home/travis/build/jfsiii/chromath/test/constructor_rgb.js:7:48)

jfsiii commented 9 years ago

I understand the PR, I just felt it was too tailored to one set of values.

For example, if a user doesn't use white or black for their text colors, they don't get the benefits of the function. Or they have to test and see if it returns black or white and map that to what they use for light / dark. In which case, they'd rather just have the yiq value back instead.

That's why I suggested just returning the yiq value. However, as you said, that's really just Y or "perceived brightness". So we could make a function called perceivedBrightness or something, but that lead me into a bikeshed over the other functions required to implement the W3C checks or online tool I linked to.

Implementing rgb2yiq gives us the desired perceivedBrightness value, avoids other issues I mentioned, and adds one more color space to Chromath. All wins.

I only linked to the YIQ article to prevent you from submitting rgb2yiq with just that line from contrastText and going through another round of edits.

Thanks again for the PRs. I appreciate the effort and am glad you find Chromath useful.

konsumer commented 9 years ago

No prob! So, I added proper rgb2yiq & yiq2rgb statics using well-tested matrix transforms. I also added them to the Chromath.convert.x.y(args) statics. I also added a .gitignore because I didn't want to accidently check in bad things when I ran the tests, which I am still unsure why they are failing. Seems very unrelated (it's inverting 255 & 1 in 3 cases in Chromath.rgb2hex.)

I would still argue that the text-contrast thing is useful in Chromath (maybe with another name, like contrastNeutral, or something.) As you can see from the screenshot, it looks really nice. I started using the invert function I PR'd, but found that it often didn't really look that good for text, and didn't improve readability, in many cases. Again, I don't mind maintaining a peerDep "plugin" for that one lil thing, though.

konsumer commented 9 years ago

Ooh! Maybe the best compromise is a function that has optional light & dark colors. Then I could use it as-is (with defaults of white/black) but people who want to use a less dark/light color are free to do so. I could see it not working as well, but it might work better for some people. I could implement using the new YIQ function.

jfsiii commented 9 years ago

I think a 'which color text should I use on this background' function is more a 'user land' than core/library function.

'Which color should I use?' varies by project, color palette, etc. However, the library can help with 'is this combination ok?' or 'what is the relationship (difference, contrast ratio, etc) between these colors?'

For example, if we wanted to make it easier to support WCAG's contrast guidelines we could add functions like:

function luminance(color){
  var c = new Chromath(color);
  var R = (c.r <= 0.03928) ? c.r/12.92 : Math.pow((c.r+0.055)/1.055, 2.4);
  var G = (c.g <= 0.03928) ? c.g/12.92 : Math.pow((c.g+0.055)/1.055, 2.4);
  var B = (c.b <= 0.03928) ? c.b/12.92 : Math.pow((c.b+0.055)/1.055, 2.4);
  var L = 0.2126 * R + 0.7152 * G + 0.0722 * B;
  return L;
}

from http://www.w3.org/TR/WCAG20/#relativeluminancedef

and

function contrast(a, b){
  // sorts low-to-high, numerically (dark to light)
  var sorted = [new Chromath(a), new Chromath(b)].sort();
  var dark = sorted[0], light = sorted[1];
  var L1 = new Chromath(light);
  var L2 = new Chromath(dark);
  return (L1 + 0.05) / (L2 + 0.05);
}

from http://www.w3.org/TR/WCAG20/#contrast-ratiodef

and then 'which color text should I use?' could be assembled from those in a way that fit the style of the project. For example, contrastText could be a function in your project or a simple ternary like:

luminance('rgb(0, 100, 255)') >= luminance('grey') ? Chromath.black : Chromath.white

Looking at these various function got me thinking. Perhaps luminance, contrast and those color brightness & difference functions could go in a Chromath.a11y namespace?