{"id":282450,"date":"2019-02-21T09:26:37","date_gmt":"2019-02-21T16:26:37","guid":{"rendered":"http:\/\/css-tricks.com\/?p=282450"},"modified":"2019-10-18T11:06:48","modified_gmt":"2019-10-18T18:06:48","slug":"css-variables-calc-rgb-enforcing-high-contrast-colors","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/css-variables-calc-rgb-enforcing-high-contrast-colors\/","title":{"rendered":"CSS Variables + calc() + rgb() = Enforcing High Contrast Colors"},"content":{"rendered":"

As you may know, the recent updates and additions to CSS are extremely powerful. From Flexbox<\/a> to Grid<\/a>, and \u2014 what we\u2019re concerned about here \u2014 Custom Properties<\/a> (aka<\/abbr> CSS variables), all of which make robust and dynamic layouts and interfaces easier than ever while opening up many other possibilities we used to only dream of.<\/p>\n

The other day, I was thinking that there must be a way to use Custom Properties to color an element’s background while maintaining a contrast with the foreground color that is high enough (using either white or black) to pass WCAG AA accessibility standards<\/a>.<\/p>\n

<\/p>\n

It\u2019s astonishingly efficient to do this in JavaScript with a few lines of code:<\/p>\n

var rgb = [255, 0, 0];\r\n\r\nfunction setForegroundColor() {\r\n  var sum = Math.round(((parseInt(rgb[0]) * 299) + (parseInt(rgb[1]) * 587) + (parseInt(rgb[2]) * 114)) \/ 1000);\r\n  return (sum > 128) ? 'black' : 'white';\r\n}<\/code><\/pre>\n

This takes the red, green and blue (RGB) values of an element\u2019s background color, multiplies them by some special numbers<\/a> (299, 587, and 144, respectively), adds them together, then divides the total by 1,000. When that sum is greater than 128, it will return black; otherwise, we\u2019ll get white. Not too bad.<\/p>\n

The only problem is, when it comes to recreating this in CSS, we don’t have access to a native if<\/code> statement to evaluate the sum. So,how can we replicate this in CSS without one?<\/strong><\/p>\n

Luckily, like HTML, CSS can be very forgiving. If we pass a value greater than 255 into the RGB function, it will get capped at 255. Same goes for numbers lower than 0. Even negative integers will get capped at 0. So, instead of testing whether our sum is greater or less than 128, we subtract 128 from our sum, giving us either a positive or negative integer. Then, if we multiply it by a large negative value (e.g. -1,000), we end up with either very large positive or negative values that we can then pass into the RGB function. Like I said earlier, this will get capped to the browser\u2019s desired values.<\/p>\n

Here is an example using CSS variables:<\/p>\n

:root {\r\n  --red: 28;\r\n  --green: 150;\r\n  --blue: 130;\r\n\r\n  --accessible-color: calc(\r\n    (\r\n      (\r\n        (\r\n          (var(--red) * 299) +\r\n          (var(--green) * 587) +\r\n          (var(--blue) * 114)\r\n        ) \/ 1000\r\n      ) - 128\r\n    ) * -1000\r\n  );\r\n}\r\n\r\n.button {\r\n  color:\r\n    rgb(\r\n      var(--accessible-color),\r\n      var(--accessible-color),\r\n      var(--accessible-color)\r\n    );\r\n  background-color:\r\n    rgb(\r\n      var(--red),\r\n      var(--green),\r\n      var(--blue)\r\n    );\r\n}<\/code><\/pre>\n

If my math is correct (and it’s very possible that it’s not) we get a total of 16,758, which is much greater than 255. Pass this total into the rgb()<\/code> function for all three values, and the browser will set the text color to white.<\/p>\n

Throw in a few range sliders to adjust the color<\/code> values, and there you have it: a dynamic UI<\/abbr> element that can swap text color based on its background-color<\/code> while maintaining a passing grade with WCAG AA.<\/p>\n

\n See the Pen
\n CSS Only Accessible Button<\/a> by Josh Bader (
@joshbader<\/a>)
\n on
CodePen<\/a>.<\/span>\n <\/p>\n

Putting this concept to practical use<\/h3>\n

Below is a Pen showing how this technique can be used to theme a user interface. I have duplicated and moved the --accessible-color<\/code> variable into the specific CSS rules that require it, and to help ensure backgrounds remain accessible based on their foregrounds, I have multiplied the --accessible-color<\/code> variable by -1 in several places. The colors can be changed by using the controls located at the bottom-right. Click the cog\/gear icon to access them.<\/p>\n

\nSee the Pen
\nCSS Variable Accessible UI<\/a> by Josh Bader (
@joshbader<\/a>)
\non
CodePen<\/a>.<\/span>\n<\/p>\n

There are other ways to do this<\/h3>\n

A little while back, Facundo Corradini<\/a> explained how to do something very similar in this post<\/a>. He uses a slightly different calculation in combination with the hsl<\/code> function. He also goes into detail about some of the issues he was having while coming up with the concept:<\/p>\n

\n

Some hues get really problematic (particularly yellows and cyans), as they are displayed way brighter than others (e.g. reds and blues) despite having the same lightness value. In consequence, some colors are treated as dark and given white text despite being extremely bright.<\/p>\n

What in the name of CSS is going on?<\/p>\n<\/blockquote>\n

He goes on to mention that Edge wasn\u2019t capping his large numbers, and during my testing, I noticed that sometimes it was working and other times it was not. If anyone can pinpoint why this might be, feel free to share in the comments.<\/p>\n

Further, Ana Tudor<\/a> explains how using filter<\/code> + mix-blend-mode<\/code> can help contrast text against more complex backgrounds<\/a>. And, when I say complex, I mean complex<\/em>. She even goes so far as to demonstrate how text color can change as pieces of the background color change \u2014 pretty awesome!<\/p>\n

Also, Robin Rendle<\/a> explains how to use mix-blend-mode<\/code> along with pseudo elements to automatically reverse text colors<\/a> based on their background-color<\/code>.<\/p>\n

So, count this as yet another approach to throw into the mix. It\u2019s incredibly awesome that Custom Properties open up these sorts of possibilities for us while allowing us to solve the same problem in a variety of ways.<\/p>\n","protected":false},"excerpt":{"rendered":"

As you may know, the recent updates and additions to CSS are extremely powerful. From Flexbox to Grid, and \u2014 what we\u2019re concerned about here \u2014 Custom Properties (aka CSS variables), all of which make robust and dynamic layouts and interfaces easier than ever while opening up many other possibilities we used to only dream […]<\/p>\n","protected":false},"author":229879,"featured_media":282458,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"A clever concept by Josh Bader using custom properties to enforce contrast compliance for accessibility.","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[466,1431,1219,839,1036,894],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/02\/css-a11y-button.gif?fit=900%2C450&ssl=1","jetpack-related-posts":[{"id":254332,"url":"https:\/\/css-tricks.com\/css-custom-properties-theming\/","url_meta":{"origin":282450,"position":0},"title":"CSS Custom Properties and Theming","date":"May 1, 2017","format":false,"excerpt":"We posted not long ago about the difference between native CSS variables (custom properties) and preprocessor variables. There are a few esoteric things preprocessor variables can do that native variables cannot, but for the most part, native variables can do the same things. But, they are more powerful because of\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":306079,"url":"https:\/\/css-tricks.com\/creating-color-themes-with-custom-properties-hsl-and-a-little-calc\/","url_meta":{"origin":282450,"position":1},"title":"Creating Color Themes With Custom Properties, HSL, and a Little calc()","date":"April 16, 2020","format":false,"excerpt":"Before the advent of CSS custom properties (we might call them \u201cvariables\u201d in this article as that\u2019s the spirit of them), implementing multiple color schemes on the same website usually meant writing separate stylesheets. Definitely not the most maintainable thing in the world. Nowadays, though, we can define variables in\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/03\/teal-shades.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":330632,"url":"https:\/\/css-tricks.com\/custom-properties-as-state\/","url_meta":{"origin":282450,"position":2},"title":"Custom Properties as State","date":"January 5, 2021","format":false,"excerpt":"Here's a fun idea from James Stanley: a CSS file (that presumably updates daily) containing CSS custom properties for \"seasonal\" colors (e.g. spring is greens, fall is oranges). You'd then use the values to theme your site, knowing that those colors change slightly from day to day. This is what\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/01\/birds-clouds.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":274679,"url":"https:\/\/css-tricks.com\/switch-font-color-for-different-backgrounds-with-css\/","url_meta":{"origin":282450,"position":3},"title":"Switch font color for different backgrounds with CSS","date":"August 10, 2018","format":false,"excerpt":"Ever get one of those, \"I can do that with CSS!\" moments while watching someone flex their JavaScript muscles? That\u2019s exactly the feeling I got while watching Dag-Inge Aas & Ida Aalen talk at CSSconf EU 2018. They are based in Norway, where WCAG accessibility is not a just good\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/07\/color-change.gif?fit=800%2C400&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":297433,"url":"https:\/\/css-tricks.com\/designing-accessible-color-systems\/","url_meta":{"origin":282450,"position":4},"title":"Designing accessible color systems","date":"October 21, 2019","format":false,"excerpt":"The team at Stripe explores how they\u2019re refining their color palette to make it more accessible and legible for users across all their products and interfaces. Not only that but the team built a wonderful and yet entirely bonkers app for figuring out the ideal range of colors that they\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/10\/stripe-accessible-colors.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":333595,"url":"https:\/\/css-tricks.com\/a-complete-guide-to-custom-properties\/","url_meta":{"origin":282450,"position":5},"title":"A Complete Guide to Custom Properties","date":"April 27, 2021","format":false,"excerpt":"Everything important and useful to know about CSS Custom Properties. Like that they are often referred to as \"CSS Variables\" but that's not their real name.","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/09\/custom-properties-code.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/02\/css-a11y-button.gif?fit=900%2C450&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/282450"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/229879"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=282450"}],"version-history":[{"count":10,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/282450\/revisions"}],"predecessor-version":[{"id":297503,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/282450\/revisions\/297503"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/282458"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=282450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=282450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=282450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}