Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contrast options #7

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open

Contrast options #7

wants to merge 7 commits into from

Conversation

konsumer
Copy link

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 konsumer changed the title added invert Contrast options Feb 18, 2015
@konsumer
Copy link
Author

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)

> '#FF9B00'

*/
Chromath.invert = function (color)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many of the intermediate conversion steps can be replaced by existing Chromath features. Here's how I'd accomplish it using the library as-is:

var intColor = Chromath.toInteger('rgb(0, 100, 255)') // 25855
var intInverted = 0xFFFFFF ^ intColor                 // 16751360
new Chromath(intInverted).toString()                  // "#FF9B00"

Lines 699-702

var c = new Chromath(color);
var innerColor = c.toString();
innerColor = innerColor.substring(1);           // remove #
innerColor = parseInt(innerColor, 16);          // convert to integer

can be replaced with Chromath.toInteger

var intColor = Chromath.toInteger('rgb(0, 100, 255)') // 25855

and 703-706

innerColor = 0xFFFFFF ^ innerColor;             // invert three bytes
innerColor = innerColor.toString(16);           // convert to hex
innerColor = ("000000" + innerColor).slice(-6); // pad with leading zeros
return new Chromath("#" + innerColor);

can use Chromath constructor's support for integer arguments

Leaving something like:

Chromath.invert = function (color) {
    var intColor = Chromath.toInteger(color);
    return new Chromath(0xFFFFFF ^ intColor);
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a function that you are interested in integrating, if it's in that form, instead? I am happy to make a PR for the code you just wrote, if you think that is better...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll merge it with those changes. Sorry I didn't make that clear. Thanks for the PR.

@jfsiii
Copy link
Owner

jfsiii commented Feb 19, 2015

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
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

jfsiii commented Feb 22, 2015

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants