Programming Sass to Create Accessible Color Combinations
One of the easiest ways to improve accessibility to your website and app is to make sure the colors you use are pleasing to the eye. High contrast colors are good for everyone and not only reduce general eye fatigue, but also crucial for users with vision impairment.
Let's not only use better color combinations in our design, but also find an easier way to achieve high contrast. Oomph adopts a specific strategy to let the Sass function do all the heavy lifting. I'll walk you through how we achieve this.
If you already know all about color accessibility and want to jump straight to the code section, click here.
The meaning of "Accessive Function Color Combination"
Color contrast is also one of the things we might think we have handled. However, high color contrast is not just about judging the design by feeling. WCAG defines different levels of acceptable standards that are considered accessible. Open the WebAIM contrast checker and run the website's color combinations, which can actually be humble.
My team follows WCAG's AA-level guidelines by default. This means:
- Text greater than or equal to 24 pixels, or text greater than or equal to 19 pixels, if bolded, has a color contrast ratio (CCR) of 3.0:1.
- The CCR for text less than 24 pixels should be 4.5:1.
If the website needs to follow AA-level enhancement guidelines, the requirements will be higher:
- Text greater than or equal to 24 pixels, or text greater than or equal to 19 pixels, if bolded, has a color contrast ratio (CCR) of 4.5:1.
- The CCR for text less than 24 pixels should be 7:1.
ratio? Um? Yes, there are some math operations involved here. But the good news is that we don’t need to do it ourselves, nor have we had a deep understanding of the calculation method as Stacie Arellano recently shared (this article is a must read if you’re interested in the scientific principles of color assistive functions).
This is where Sass comes in. We can use it to run complex mathematical calculations that otherwise would be difficult for us to understand. But first, I think it's worth dealing with accessibility colors at the design level.
Accessibility palette begins with design
That's right. The core work of creating an accessibility palette starts with design. Ideally, any web design should refer to a tool to verify that any color combination used passes the established guide and then adjust the color that is not passed. Our design team uses tools developed in-house when doing this. It handles color lists, tests them on dark and light colors, and provides ways to test other combinations.
This is the first thing our team has to do. I dare say that many brand colors don’t put accessibility first when choosing. I often find that these colors need to be changed when converting to web design. Through education, dialogue and visual samples, we get clients to approve new color palettes. I admit: This part of the work may be harder than actually implementing the color combination of accessibility.
The problem I want to solve through automation is the extreme situation . You can't blame the designer for missing out on some of the colors that are combined in unexpected ways – this does happen. And these extremes will arise, whether during the build process or when adding new colors to the system a year later.
Develop auxiliary functions while maintaining color system intentions
The trick to change colors to meet accessibility requirements is to not change colors so much that they no longer look the same color. A brand that likes its emerald green color will want to maintain the intention of that color - its "emerald feel". In order to pass accessibility testing when used as text on a white background, we may need to darken green and increase its saturation. But we still want the color to be "same" as the original color.
To do this, we use the Hue Saturation Brightness (HSL) color model. HSL allows us to keep the hue unchanged, but adjust the saturation (i.e., increase or decrease the color) and brightness (i.e., add more black or more white). The hue makes green that green, or makes blue that blue. It is the "soul" of color, which makes us a little mysterious about.
Hue is represented as a color wheel, with values ranging from 0° to 360°—yellow is 60°, green is 120°, cyan is 180°, etc. Saturation is a percentage, ranging from 0% (unsaturation) to 100% (full saturation). Brightness is also a value, ranging from 0% to 100%, where 0% is brightness-free, 50% is black and white, 100% is full brightness, or very bright.
Quick visuals for adjusting colors in our tools:
To learn more, try the fun HSL visualization tool on mothereffinghsl.com. However, for more in-depth color blindness descriptions, WCAG color contrast levels, and HSL color space, we wrote an in-depth blog post.
Use cases I want to solve
Designers can use tools we just reviewed to adjust colors, but so far I haven't found any Sass that can do this with magical math. There must be a way.
Here are some similar methods I've seen in my actual application:
- One idea of Josh Bader is to use CSS variables and split the color into its RGB value to calculate that white or black is the best accessibility color in a given case.
- Another idea from Facundo Corradini is similar to the very cool "switch functions" in HSL values and CSS.
I don't like these methods. I don't want to fall back to white or black. I want the color to remain the same, but to be adjusted to accessible. Additionally, changing the color to its RGB or HSL component and storing it with CSS variables seems confusing and difficult to maintain a large code base.
I want to do this with a preprocessor like Sass: Given two colors, adjust one of the colors automatically so that the pair of colors gets color contrast through the WCAG rating. The rules also specify some other things to consider – the size of the text and whether the font is bold. The solution must take this into account.
From a code perspective, I want to do this:
<code>// 转换此非通过颜色对: .example { background-color: #444; color: #0094c2; // 当AA 需要4.5 时,对比度比为2.79 font-size: 1.25rem; font-weight: normal; } // 到此通过颜色对: .example { background-color: #444; color: #00c0fc; // 对比度比为4.61 font-size: 1.25rem; font-weight: normal; }</code>
The solution to do so is able to capture and handle the extreme situations we mentioned earlier. Perhaps the designer has considered using brand blue above light blue, but has not considered light gray. Perhaps the red used in the error message needs to be adjusted for the form with a single background color. Maybe we want to implement dark mode functionality to the UI without having to retest all colors again. These are the use cases I've considered.
Formulas can bring automation
W3C provides the community with formulas that help analyze the two colors used together. The formula multiplies the RGB channels of both colors by magic numbers (based on how humans perceive the visual weights of these color channels), and divides them to yield a ratio from 0.0 (no contrast) to 21.0 (all contrasts, only black and white). Although not perfect, this is exactly the formula we are currently using:
<code>如果L1 是第一种颜色的相对亮度而L2 是第二种颜色的相对亮度,则- 颜色对比度比= (L1 0.05) / (L2 0.05)其中- L = 0.2126 * R 0.7152 * G 0.0722 * B并且- 如果R sRGB</code> Although the formula looks complicated, it's just math, right? etc. At the end of several lines of code there is a portion of the value multiplied by a decimal power— <em>raised to a power of 2.4</em> . Have you noticed it? It turns out that this is complex math operation that most programming languages can do - think of Javascript's math.pow() function - but Sass isn't capable enough to do that. <h3 id="There-must-be-other-methods">There must be other methods...</h3> <p>Of course there is. It just took some time to find it. ?</p> <p>My first version uses a series of complex mathematical calculations to do the decimal powers that Sass can do. A lot of googling found that people who are much smarter than me provide these functions. Unfortunately, calculating only a small number of color contrast combinations increases Sass build time exponentially. So, this means that Sass can do this, but that doesn't mean it <em>should</em> . In a production environment, the build time of large code bases may increase to several minutes. This is unacceptable.</p> <p>After more googling, I stumbled upon a post about someone trying to do something similar. They also encountered the problem of Sass lacking index support. They want to explore "the possibility of calculating the fractional part of an index using Newton's approximation method." <em>I fully understand this impulse</em> (no) <em>.</em> Instead, they decided to use the “lookup table.” This is a genius solution. Instead of doing math from scratch every time, lookup tables provide all pre-computed possible answers. The Sass function retrieves the answer from the list and it is done.</p> <p>In their words:</p> <blockquote><p>The [only] part of Sass involving exponential operations is the channel-by-channel color space conversion done as part of the brightness calculation. [T] There are only 256 possible values per channel. This means we can easily create lookup tables.</p></blockquote> <p>Now we are starting to act. I found a way to perform better.</p> <h3 id="Usage-example">Usage example</h3> <p>Using this function should be simple and flexible. Given a set of two colors, adjust the first color so that when used with the second color, the correct contrast value is obtained for the given WCAG level. Optional parameters will also take into account text size or thickness.</p> // @function a11y-color( // $color-to-adjust, // $color-that-will-stay-the-same, // $wcag-level: 'AA', // $font-size: 16, // $bold: false // ); // Sass example usage, declare only the required content.example { background-color: #444; color: a11y-color(#0094c2, #444); // When AA requires 4.5, the contrast ratio is 2.79, for small text in non-bold} // Compiled CSS results: .example { background-color: #444; color: #00c0fc; // The contrast ratio is 4.61 } <p>I use functions instead of mixin, because I prefer output of a single value independent of a CSS rule. Using functions, the author can determine which color should be changed.</p> <p>An example containing more parameters is as follows:</p> // Sass .example-2 { background-color: a11y-color(#0094c2, #f0f0f0, 'AAA', 1.25rem, true); // When AAA requires 4.5, the contrast ratio is 3.06. For text greater than or equal to 19 pixels and bold color: #f0f0f0; font-size: 1.25rem; font-weight: bold; } // Compiled CSS results: .example-2 { background-color: #087597; // The contrast ratio is 4.6 color: #f0f0f0; font-size: 1.25rem; font-weight: bold; } <h3 id="Deeply-explore-the-core-of-Sass-function">Deeply explore the core of Sass function</h3> <p>To explain this approach, let's introduce the role of the final function line by line. There are many helper functions in the process, but the comments and logic in the core functions explain this approach:</p> // Expected: // $fg as the color that will be changed // $bg as the color that will remain static and will not be changed // Optional: // $level, default is 'AA'. Also accept 'AAA' // $size, default is 16. Expected to be PX, allowing EM and REM // $bold, boolean value, default is false. Is the font currently bold // @function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false) { // Helper function: Ensure that the font size value is acceptable $font-size: validate-font-size($size); // Helper function: Use level, font size, and bold boolean values to return the correct target ratio. Expected results are 3.0, 4.5, or 7.0 $ratio: get-ratio($level, $font-size, $bold); // Calculate the first contrast ratio of a given pair $original-contrast: color-contrast($fg, $bg); @if $original-contrast >= $ratio { // If we have passed the ratio, return the original color @return $fg; } @else { // Failed. Start working // Should the color be brighter or darker? // Helper function: single color input, output is "light" or "dark" $fg-lod: light-or-dark($fg); $bg-lod: light-or-dark($bg); // Set a "step" value to brighten or darken the color // Note: a higher percentage step means faster compile time, but we may use values above 5% to exceed the required threshold $step: 2%; // Iterate through some cases where we want to darken or use negative step size values @if $fg-lod == 'light' and $bg-lod == 'light' { // Both are light colors, making fg darker (making step size value negative) $step: - $step; } @else if $fg-lod == 'dark' and $bg-lod == 'light' { // bg is light color, fg is dark color, but fails, it becomes darker further $step: - $step; } // Keep the rest of the logic here, but our default value will not change, so this logic is not needed //@else if $fg-lod == 'light' and $bg-lod == 'dark' { // // bg is dark, fg is light, but fails, it will brighten further // $step: $step; //} @else if $fg-lod == 'dark' and $bg-lod == 'dark' { // // Both are dark, thus brightening fg // $step: $step; //} // Magic happens here // Loop with the @while statement until the color combination passes through the ratio we need. Scale the color by our step value until the expression is false // This may loop 100 or more times depending on the color @while color-contrast($fg, $bg) <h3 id="Final-Sass-file">Final Sass file</h3> <p>This is the complete set of functions! Open this file in CodePen to edit the color variables at the top of the file and see the adjustments made by Sass:</p> <p>All helper functions and 256 row lookup tables are here. A large number of comments should help people understand what is going on.</p> <p>When encountering extreme situations, I use the version of SassMeister with debug output during development to see what might happen. (I changed the main function to mixin so that I can debug the output.) Feel free to view this as well.</p> <p data-gist-="" data-height="480" data-theme="tomorrow" style="height: 480px; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1rem 0; padding: 1rem; overflow: auto;"> Try this gist on SassMeister.</p> <p>Finally, these functions have been stripped from CodePen and placed into the GitHub repository. If you have problems, submit the problem in the queue.</p> <h3 id="Great-code-But-can-I-use-it-in-a-production-environment">Great code! But can I use it in a production environment?</h3> <p><em>Maybe.</em></p> <p>I would say yes, but I've been iterating over this problem for a while. I'm confident in this code, but want more input. Use it on a small project and test it. Let me know how build time performs. If you encounter extreme situations where passing color values are not provided, please let me know. Submit an issue to the GutHub repository. Make suggestions for improvement based on other code you see in your actual application.</p> <p>I would say I've <em>automated everything about accessibility</em> , but I also know it needs to be road tested to be called "Production Ready Edition™". I'm glad to introduce it to the whole world. Thanks for reading, I hope to hear how you use it soon.</p>
Note that I cannot provide the actual Sass code block due to the inability to execute the code directly. The above output has tried its best to make pseudo-original original text and maintain the image position and format. If more fine adjustments are required, please provide more specific modification requirements.
The above is the detailed content of Programming Sass to Create Accessible Color Combinations. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

It's out! Congrats to the Vue team for getting it done, I know it was a massive effort and a long time coming. All new docs, as well.

With the recent climb of Bitcoin’s price over 20k $USD, and to it recently breaking 30k, I thought it’s worth taking a deep dive back into creating Ethereum

I had someone write in with this very legit question. Lea just blogged about how you can get valid CSS properties themselves from the browser. That's like this.

I'd say "website" fits better than "mobile app" but I like this framing from Max Lynch:

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

If we need to show documentation to the user directly in the WordPress editor, what is the best way to do it?

There are a number of these desktop apps where the goal is showing your site at different dimensions all at the same time. So you can, for example, be writing

Questions about purple slash areas in Flex layouts When using Flex layouts, you may encounter some confusing phenomena, such as in the developer tools (d...
