Tag: color

Wide Gamut Color in CSS with Display-P3

Here’s something I’d never heard of before: Display-P3 support in CSS Color Module Level 4 spec. This is a new color profile supported by certain displays and it introduces a much wider range of colors that we can choose from.

Right now the syntax looks something like this in CSS:

header {   color: rgb(0, 255, 0); /* fallback color */   color: color(display-p3 0 1 0); }

It looks weird, huh? Over on the WebKit blog, Nikita Vasilyev shows how we can see these new colors in Safari’s DevTools:

Back in 2016, Dean Jackson wrote about improving color on the web and why Apple is interested in this much wider color gamut. The general idea is that more accurate colors make for a better and more vibrant user experience.

Also, it looks like all this is only available in Safari right now.

Direct Link to ArticlePermalink

The post Wide Gamut Color in CSS with Display-P3 appeared first on CSS-Tricks.

CSS-Tricks

, , ,

Programming Sass to Create Accessible Color Combinations

We’re all looking for low-hanging fruit to make our sites and apps more accessible. One of the easier things we can do is make sure the colors we use are easy on the eyes. High color contrast is something that benefits everyone. It not only reduces eye strain in general, but is crucial for folks who deal with reduced vision.

So let’s not only use better color combinations in our designs but find a way to make it easier for us to implement high contrasts. There’s one specific strategy we use over at Oomph that lets a Sass function do all the heavy lifting for us. I’ll walk you through how we put that together.

Want to jump right to the code because you already understand everything there is to know about color accessibility? Here you go.

What we mean by “accessible color combinations”

Color contrast is also one of those things we may think we have handled. But there’s more to high color contrasts than eyeballing a design. There are different levels of acceptable criteria that the WCAG has defined as being accessible. It’s actually humbling to crack open the WebAIM Contrast Checker and run a site’s color combinations through it.

My team adheres to WCAG’s Level AA guidelines by default. This means that: 

  • Text that is 24px and larger, or 19px and larger if bold, should have a Color Contrast Ratio (CCR) of 3.0:1.
  • Text that is smaller than 24px should have a CCR of 4.5:1.

If a site needs to adhere to the enhanced guidelines for Level AAA, the requirements are a little higher:

  • Text that is 24px and larger, or 19px and larger if bold, should have a CCR of 4.5:1.
  • Text that is smaller than 24px should have a CCR of 7:1.

Ratios? Huh? Yeah, there’s some math involved here. But the good news is that we don’t need to do it ourselves or even have the same thorough understanding about how they’re calculated the way Stacie Arellano recently shared (which is a must read if you’re into the science of color accessibility).

That’s where Sass comes in. We can leverage it to run difficult mathematical computations that would otherwise fly over many of our heads. But first, I think it’s worth dealing with accessible colors at the design level.

Accessible color palettes start with the designs

That’s correct. The core of the work of creating an accessible color palette starts with the designs. Ideally, any web design ought to consult a tool to verify that any color combinations in use pass the established guidelines — and then tweak the colors that don’t. When our design team does this, they use a tool that we developed internally. It works on a list of colors, testing them over a dark and a light color, as well as providing a way to test other combinations. 

ColorCube provides an overview of an entire color palette, showing how each color performs when paired with white, black, and even each other. It even displays results for WCAG Levels AA and AAA next to each result. The tool was designed to throw a lot of information at the user all at once when evaluating a list of colors.

This is the first thing our team does. I’d venture to guess that many brand colors aren’t chosen with accessibility at the forefront. I often find that those colors need to change when they get translated to a web design. Through education, conversation, and visual samples, we get the client to sign off on the new color palette. I’ll admit: that part can be harder than the actual work of implementing accessible colors combinations.

The Color Contrast Audit: A typical design delivery when working with an existing brand’s color palette. Here, we suggest to stop using the brand color Emerald with white, but use an “Alt” version that is slightly darker instead. 

The problem that I wanted to solve with automation are the edge cases. You can’t fault a designer for missing some instance where two colors combine in an unintended way — it just happens. And those edge cases will come up, whether it is during the build or even a year later when new colors are added to the system.

Developing for accessibility while keeping true to the intent of a color system

The trick when changing colors to meet accessibility requirements is not changing them so much that they don’t look like the same color anymore. A brand that loves its emerald green color is going to want to maintain the intent of that color — it’s “emerald-ness.” To make it pass for accessibility when it is used as text over a white background, we might have to darken the green and increase its saturation. But we still want the color to “read” the same as the original color.

To achieve this, we use the Hue Saturation Lightness (HSL) color model. HSL gives us the ability to keep the hue as it is but adjust the saturation (i.e. increase or decrease color) and lightness (i.e. add more black or more white). The hue is what makes a green that green, or a blue that blue. It is the “soul” of the color, to get a little mystical about it. 

Hue is represented as a color wheel with a value between 0° and 360° — yellow at 60°, green at 120°, cyan at 180°, etc. Saturation is a percentage ranging from 0% (no saturation) to 100% (full saturation). Lightness is also a value that goes from 0% to 100%, where no lightness is at 0%, no black and no white is at 50%, and 100% is all lightness, or very light.

A quick visual of what tweaking a color looks like in our tool:

With HSL, changing the low-contrast green to a higher contrast meant changing the saturation from 63 to 95 and the lightness from 45 to 26 on the left. That’s when the color gets a green check mark in the middle when used with white. The new green still feels like it is in the same family, though, because the Hue remained at 136, which is like the color’s “soul.”

To learn more, play around with the fun HSL visualizer mothereffinghsl.com. But for a more in-depth description of color blindness, WCAG color contrast levels, and the HSL color space, we wrote an in-depth blog post about it.

The use case I want to solve

Designers can adjust colors with the tools that we just reviewed, but so far, no Sass that I have found could do it with mathematical magic. There had to be a way. 

These are some similar approaches I have seen in the wild:

  • An idea by Josh Bader uses CSS variables and colors split into their RGB values to calculate whether white or black is the best accessible color to use in a given situation.
  • Another idea by Facundo Corradini does something similar with HSL values and a very cool “switch function” in CSS.

I didn’t like these approaches. I didn’t want to fallback to white or black. I wanted colors to be maintained but adjusted to be accessible. Additionally, changing colors to their RGB or HSL components and storing them with CSS variables seemed messy and unsustainable for a large codebase.

I wanted to use a preprocessor like Sass to do this: given two colors, automagically adjust one of them so the pair receives a passing WCAG grade. The rules state a few other things to consider as well — size of the text and whether or not the font is bold. The solution had to take this into account. 

In code terms, I wanted to do this:

// Transform this non-passing color pair: .example {   background-color: #444;   color: #0094c2; // a 2.79 contrast ratio when AA requires 4.5   font-size: 1.25rem;   font-weight: normal; } 
 // To this passing color pair: .example {   background-color: #444;   color: #00c0fc; // a 4.61 contrast ratio   font-size: 1.25rem;   font-weight: normal; }

A solution that does this would be able to catch and handle those edge cases we mentioned earlier. Maybe the designer accounted for a brand blue to be used over a light blue, but not a light gray. Maybe the red used in error messages needs to be tweaked for this one form that has a one-off background color. Maybe we want to implement a dark mode feature to the UI without having to retest all the colors again. These are the use cases I had in mind going into this. 

With formulas can come automation

The W3C has provided the community with formulas that help analyze two colors used together. The formula multiplies the RGB channels of both colors by magic numbers (a visual weight based on how humans perceive these color channels) and then divides them to come up with a ratio from 0.0 (no contrast) to 21.0 (all the contrast, only possible with white and black). While imperfect, this is the formula we use right now:

If L1 is the relative luminance of a first color  And L2 is the relative luminance of a second color, then - Color Contrast Ratio = (L1 + 0.05) / (L2 + 0.05) Where - L = 0.2126 * R + 0.7152 * G + 0.0722 * B And - if R sRGB <= 0.03928 then R = R sRGB /12.92 else R = ((R sRGB +0.055)/1.055) ^ 2.4 - if G sRGB <= 0.03928 then G = G sRGB /12.92 else G = ((G sRGB +0.055)/1.055) ^ 2.4 - if B sRGB <= 0.03928 then B = B sRGB /12.92 else B = ((B sRGB +0.055)/1.055) ^ 2.4 And - R sRGB = R 8bit /255 - G sRGB = G 8bit /255 - B sRGB = B 8bit /255

While the formula looks complex, it’s just math right? Well, not so fast. There is a part at the end of a few lines where the value is multiplied by a decimal power — raised to the power of 2.4. Notice that? Turns out that it’s complex math which most programming languages can accomplish — think Javascript’s math.pow() function — but Sass is not powerful enough to do it. 

There’s got to be another way…

Of course there is. It just took some time to find it. 🙂

My first version used a complex series of math calculations that did the work of decimal powers within the limited confines of what Sass can accomplish. Lots of Googling found folks much smarter than me supplying the functions. Unfortunately, calculating only a handful of color contrast combinations increased Sass build times exponentially. So, that means Sass can do it, but that does not mean it should. In production, build times for a large codebase could increase to several minutes. That’s not acceptable. 

After more Googling, I came across a post from someone who was trying to do a similar thing. They also ran into the lack of exponent support in Sass. They wanted to explore “the possibility of using Newtonian approximation for the fractional parts of the exponent.” I totally understand the impulse (not). Instead, they decided to use a “lookup table.” It’s a genius solution. Rather than doing the math from scratch every time, a lookup table provides all the possible answers pre-calculated. The Sass function retrieves the answer from the list and it’s done.

In their words:

The only part [of the Sass that] involves exponentiation is the per-channel color space conversions done as part of the luminance calculation. [T]here are only 256 possible values for each channel. This means that we can easily create a lookup table.

Now we’re cooking. I had found a more performant direction. 

Usage example

Using the function should be easy and flexible. Given a set of two colors, adjust the first color so it passes the correct contrast value for the given WCAG level when used with the second color. Optional parameters will also take the font size or boldness into account.

// @function a11y-color( //   $ color-to-adjust, //   $ color-that-will-stay-the-same, //   $ wcag-level: 'AA', //   $ font-size: 16, //   $ bold: false // ); 
 // Sass sample usage declaring only what is required .example {   background-color: #444;   color: a11y-color(#0094c2, #444); // a 2.79 contrast ratio when AA requires 4.5 for small text that is not bold } 
 // Compiled CSS results: .example {   background-color: #444;   color: #00c0fc; // which is a 4.61 contrast ratio }

I used a function instead of a mixin because I preferred the output of a single value independent from a CSS rule. With a function, the author can determine which color should change. 

An example with more parameters in place looks like this:

// Sass .example-2 {   background-color: a11y-color(#0094c2, #f0f0f0, 'AAA', 1.25rem, true); // a 3.06 contrast ratio when AAA requires 4.5 for text 19px or larger that is also bold   color: #f0f0f0;   font-size: 1.25rem;   font-weight: bold; } 
 // Compiled CSS results: .example-2 {   background-color: #087597; // a 4.6 contrast ratio   color: #f0f0f0;   font-size: 1.25rem;   font-weight: bold; }

A deeper dive into the heart of the Sass function

To explain the approach, let’s walk through what the final function is doing, line by line. There are lots of helper functions along the way, but the comments and logic in the core function explain the approach:

// Expected: // $ fg as a color that will change // $ bg as a color that will be static and not change // Optional: // $ level, default 'AA'. 'AAA' also accepted // $ size, default 16. PX expected, EM and REM allowed // $ bold, boolean, default false. Whether or not the font is currently bold // @function a11y-color($ fg, $ bg, $ level: 'AA', $ size: 16, $ bold: false) {   // Helper: make sure the font size value is acceptable   $ font-size: validate-font-size($ size);   // Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 results expected   $ ratio: get-ratio($ level, $ font-size, $ bold);   // Calculate the first contrast ratio of the given pair   $ original-contrast: color-contrast($ fg, $ bg);      @if $ original-contrast >= $ ratio {     // If we pass the ratio already, return the original color     @return $ fg;   } @else {     // Doesn't pass. Time to get to work     // Should the color be lightened or darkened?     // Helper: Single color input, 'light' or 'dark' as output     $ fg-lod: light-or-dark($ fg);     $ bg-lod: light-or-dark($ bg); 
     // Set a "step" value to lighten or darken a color     // Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 5%     $ step: 2%;          // Run through some cases where we want to darken, or use a negative step value     @if $ fg-lod == 'light' and $ bg-lod == 'light' {       // Both are light colors, darken the fg (make the step value negative)       $ step: - $ step;     } @else if $ fg-lod == 'dark' and $ bg-lod == 'light' {       // bg is light, fg is dark but does not pass, darken more       $ step: - $ step;     }     // Keeping the rest of the logic here, but our default values do not change, so this logic is not needed     //@else if $ fg-lod == 'light' and $ bg-lod == 'dark' {     //  // bg is dark, fg is light but does not pass, lighten further     //  $ step: $ step;     //} @else if $ fg-lod == 'dark' and $ bg-lod == 'dark' {     //  // Both are dark, so lighten the fg     //  $ step: $ step;     //}          // The magic happens here     // Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false     // This might loop 100 times or more depending on the colors     @while color-contrast($ fg, $ bg) < $ ratio {       // Moving the lightness is most effective, but also moving the saturation by a little bit is nice and helps maintain the "power" of the color       $ fg: scale-color($ fg, $ lightness: $ step, $ saturation: $ step/2);     }     @return $ fg;   } }

The final Sass file

Here’s the entire set of functions! Open this in CodePen to edit the color variables at the top of the file and see the adjustments that the Sass makes:

All helper functions are there as well as the 256-line lookup table. Lots of comments should help folks understand what is going on. 

When an edge case has been encountered, a version in SassMeister with debug output was helpful while I was developing it to see what might be happening. (I changed the main function to a mixin so I can debug the output.) Feel free to poke around at this as well.

Play with this gist on SassMeister.

And finally, the functions have been stripped out of CodePen and put into a GitHub repo. Drop issues into the queue if you run into problems. 

Cool code! But can I use this in production?

Maybe. 

I’d like to say yes, but I’ve been iterating on this thorny problem for a while now. I feel confident in this code but would love more input. Use it on a small project and kick the tires. Let me know how the build time performs. Let me know if you come across edge cases where passing color values are not being supplied. Submit issues to the GutHub repo. Suggest improvements based on other code you’ve seen in the wild. 

I’d love to say that I have Automated All the A11y Things, but I also know it needs to be road-tested before it can be called Production Ready™. I’m excited to introduce it to the world. Thanks for reading and I hope to hear how you are using it real soon.

The post Programming Sass to Create Accessible Color Combinations appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

4 Ways to Animate the Color of a Text Link on Hover

Let’s create a pure CSS effect that changes the color of a text link on hover… but slide that new color in instead of simply swapping colors.

There are four different techniques we can use to do this. Let’s look at those while being mindful of important things, like accessibility, performance, and browser support in mind.

Let’s get started!

Technique 1: Using background-clip: text

At the time of writing, the background-clip: text property is an experimental feature and is not supported in Internet Explorer 11 and below.

This technique involves creating knockout text with a hard stop gradient. The markup consists of a single HTML link (<a>) element to create a hyperlink:

<a href="#">Link Hover</a>

We can start adding styles to the hyperlink. Using overflow: hidden will clip any content outside of the hyperlink during the hover transition:

a {   position: relative;   display: inline-block;   font-size: 2em;   font-weight: 800;   color: royalblue;   overflow: hidden; }

We will need to use a linear gradient with a hard stop at 50% to the starting color we want the link to be as well as the color that it will change to:

a {   /* Same as before */   background: linear-gradient(to right, midnightblue, midnightblue 50%, royalblue 50%); }

Let’s use background-clip to clip the gradient and the text value to display the text. We will also use the background-size and background-position properties to have the starting color appear:

a {   /* Same as before */   background-clip: text;   -webkit-background-clip: text;   -webkit-text-fill-color: transparent;   background-size: 200% 100%;   background-position: 100%; }

Finally, let’s add the transition CSS property and :hover CSS pseudo-class to the hyperlink. To have the link fill from left to right on hover, use the background-position property:

a {   /* Same as before */   transition: background-position 275ms ease; } a:hover {   background-position: 0 100%; }

While this technique does achieve the hover effect, Safari and Chrome will clip text decorations and shadows, meaning they won’t be displayed. Applying text styles, such as an underline, with the text-decoration CSS property will not work. Perhaps consider using other approaches when creating underlines.

Technique 2: Using width/height

This works by using a data attribute containing the same text as the one in the <a> tag and setting the width (filling the text from left-to-right or right-to-left) or height (filling the text from top-to-bottom or bottom-to-top), from 0% to 100% on hover.

Here is the markup:

<a href="#" data-content="Link Hover">Link Hover</a>

The CSS is similar to the previous technique minus the background CSS properties. The text-decoration property will work here:

a {   position: relative;   display: inline-block;   font-size: 2em;   color: royalblue;   font-weight: 800;   text-decoration: underline;   overflow: hidden; }

This is when we need to use the content from the data-content attribute. It will be positioned above the content in the <a> tag. We get to use the nice little trick of copying the text in the data attribute and displaying it via the attr() function on the content property of the element’s ::before pseudo-element.

a::before {   position: absolute;   content: attr(data-content); /* Prints the value of the attribute */   top: 0;   left: 0;   color: midnightblue;   text-decoration: underline;   overflow: hidden;   transition: width 275ms ease; }

To keep the text from wrapping to the next line, white-space: nowrap will be applied. To change the link fill color, set the value for the color CSS property using the ::before pseudo-element and having the width start at 0:

a::before {   /* Same as before */   width: 0;   white-space: nowrap; }

Increase the width to 100% to the ::before pseudo element to complete the text effect on hover:

a:hover::before {   width: 100%; }

While this technique does the trick, using the width or height properties will not produce a performant CSS transition. It is best to use either the transform or opacity properties to achieve a smooth, 60fps transition.

Using the text-decoration CSS property can allow for different underline styles to appear in the CSS transition. I created a demo showcasing this using the next technique: the clip-path CSS property.

Technique 3: Using clip-path

For this technique, we will be using the clip-path CSS property with a polygon shape. The polygon will have four vertices, with two of them expanding to the right on hover:

The markup is the same as the previous technique. We will use a ::before pseudo-element again, but the CSS is different:

a::before {   position: absolute;   content: attr(data-content);   color: midnightblue;   text-decoration: underline;   clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%);   transition: clip-path 275ms ease; }

Unlike the previous techniques, text-decoration: underline must be declared to the ::before pseudo-element for the color to fill the underline on hover.

Now let’s look into the CSS for the clip-path technique:

clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%);

The polygon’s vertices of the clip-path property are set in percentages to define coordinates by the order written:

  • 0 0 = top left
  • 0 0 = top right
  • 100% 0 = bottom right
  • 0 100% = bottom left

The direction of the fill effect can be changed by modifying the coordinates. Now that we have an idea for the coordinates, we can make the polygon expand to the right on hover:

a:hover::before {   clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }

This technique works pretty well, but note that support for the clip-path property varies between browsers. Creating a CSS transition with clip-path is a better alternative than using the width/height technique; however, it does affect the browser paint.

Technique 4: Using transform

The markup for this technique uses a masking method with a <span> element. Since we will be using duplicated content in a separate element, we will use aria-hidden="true" to improve accessibility — that will hide it from screen readers so the content isn’t read twice:

<a href="#"><span data-content="Link Hover" aria-hidden="true"></span>Link Hover</a>

The CSS for the <span> element contains a transition that will be starting from the left:

span {   position: absolute;   top: 0;   left: 0;   overflow: hidden;   transform: translateX(-100%);   transition: transform 275ms ease; }

Next, we need to get the <span> to slide the right like this:

To do this, we will use the translateX() CSS function and set it to 0:

a:hover span {   transform: translateX(0); }

Then, we will use the ::before pseudo-element for the <span>, again using the data-content attribute we did before. We’ll set the position by translating it 100% along the x-axis.

span::before {    display: inline-block;   content: attr(data-content);   color: midnightblue;   transform: translateX(100%);   transition: transform 275ms ease;   text-decoration: underline; }

Much like the <span> element, the position of the ::before pseudo-element will also be set to  translateX(0):

a:hover span::before {   transform: translateX(0); }

While this technique is the the most cross-browser compatible of the bunch, it requires more markup and CSS to get there. That said, using the transform CSS property is great for performance as it does not trigger repaints and thus produces smooth, 60fps CSS transitions.

There we have it!

We just looked at four different techniques to achieve the same effect. Although each has its pros and cons, you can see that it’s totally possible to slide in a color change on text. It’s a neat little effect that makes links feel a little more interactive.

The post 4 Ways to Animate the Color of a Text Link on Hover appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Understanding Web Accessibility Color Contrast Guidelines and Ratios

What should you do when you get a complaint about the color contrast in your web design? It might seem perfectly fine to you because you’re able to read content throughout the site, but to someone else, it might be a totally different experience. How can put yourself in that person’s shoes to improve their experience?

There are some relatively easy ways to test contrast. For example, you can check the site on your phone or tablet in bright sunlight, or add a CSS filter to mimic a grayscale view). But… you don’t have to trust your eyes. Not everyone has your exact eyes anyway, so your subjective opinion can possibly be a faulty measurement. 

You can mathematically know if two colors have enough contrast between them. 

The W3C has a document called Web Content Accessibility Guidelines (WCAG) 2.1 that covers  successful contrast guidelines. Before we get to the math, we need to know what contrast ratio scores we are aiming to meet or exceed. To get a passing grade (AA), the contrast ratio is 4.5:1 for most body text and 3:1 for larger text. 

How did the W3C arrive at these ratios?

The guidelines were created for anyone using a standard browser, with no additional assistive technology. The contrast ratios that the WCAG suggests were based initially on earlier contrast standards and adjusted to accommodate newer display technologies, like antialiased text, so content would be readable by people with a variety of visual or cognitive difficulties, whether it be due to age, sickness, or other losses of visual acuity.  

We’re basically aiming to make text readable for someone with 20/40 vision, which is equivilent to the vision of someone 80 years old. Visual acuity of 20/40 means you can only read something at 20 feet away that someone with perfect 20/20 vision could read if it was 40 feet away.

So, say your design calls for antialiased text because it looks much smoother on a screen. It actually sacrifices a bit of contrast and ding your ratio. The WCAG goes into more detail on how scoring works.

There are other standards that take contrast in consideration, and the WCAG used some of these considerations to develop their scoring. One is called the Human Factors Engineering of Computer Workstations (ANSI/HFES 100-2007) was published in 2007 and designated as an American standard for ergonomics. It combined and replaced two earlier standards that were created by separate committees. The goal of the combined standard was to accommodate 90% of computer users, and cover many aspects of computer use and ergonomics, including visual displays and contrast. So, that means we have physical screens to consider in our designs.

What does the ratio mean?

The contrast ratio explains the difference between the lightest color brightness and the darkest color brightness in a given range. It’s the relative luminance of each color.

Let’s start with an egregious example of a teal color text on a light gray background. 

<h1>Title of Your Awesome Site</h1>
h1 {   background-color: #1ABC9C;   color: #888888; }
Yikes!

It’s worth calling out that some tools, like WordPress, provide a helpful warning for this when there’s a poorly contrasted text and background combination. In the case of WordPress, a you get notice in the sidebar.

“This color combination may be hard for people to read. Try using a brighter background color and/or a darker text color.”

“OK,” you say. “Perhaps you think that teal on gray color combination is not exactly great, but I can still make out what the content says.“ (I’m glad one of us can because it’s pretty much a muddy gray mess to me.)

The contrast ratio for that fine piece of hypertext is 1.47:1.

I wanted a better understanding of what the contrast scores were actually checking and came to find that it requires the use of mathematics… with a side of understanding the differences between human and computer vision.  This journey taught me about the history of computer vision and a bit about biology, and gave me a small review of some math concepts I haven’t touched since college.

Here’s the equation:

(L1 + 0.05) / (L2 + 0.05)
  • L1 is the relative luminance of the lighter of the colors.
  • L2 is the relative luminance of the darker of the colors.

This seems simple, right? But first we need to determine the relative luminance for each color to get those variables.

OK, back to relative luminance

We mentioned it in passing, but it’s worth going deeper into relative luminance, or the relative brightness of any color expressed into a spectrum between 0 (black) and 1 (white).

To determine the relative luminance for each color, we first need to get the RGB notation for a color. Sometimes we’re working with HEX color values and need to covert that over to RGB. There are online calculators that will do this for us, but there’s solid math happening in the background that makes it happen. Our teal hex color, #1ABC9C, becomes an RGB of 26, 188, 156.

Next, we take each value of the RGB color and divide each one by 255 (the max integer of RGB values) to get a linear value between 0 and 1. 

So now with our teal color it looks like this:

Component Equation Value
Red 26/255 0.10196078
Green 188/255 0.73725490
Blue 156/255 0.61176471

Then we apply gamma correction, which defines the relationship between a pixel’s numerical value and its actual luminance, to each component part of the RGB color. If the linear value of a component is less than .03938, we divide it by 12.92. Otherwise, we add .055 and divide the total by 1.055 and take the result to the power of 2.4.

Our gamma corrected color components from our teal color end up like this:

Component Equation Value
Red ((0.10196078 +.055)/1.055) ^ 2.4 0.01032982
Green ((0.73725490 +.055)/1.055) ^ 2.4 0.50288646
Blue ((0.61176471 +.055)/1.055) ^ 2.4 0.33245154

This part of our equation comes from the formula for determining relative luminance.

We just sort of sped past gamma correction there without talking much about it and what it does. In short, it translates what a computer “sees” into the human perception of brightness. Computers record light directly where twice the photons equals twice the brightness. Human eyes perceive more levels of light in dim conditions and fewer in bright conditions. The digital devices around us make gamma encoding and decoding calculations all the time. It’s used to show us things on the screens that match up to our perception of how things appear to our eyes.

Finally, we multiply the different colors by numbers that signify how bright that color appears to the human eye. That means we determine the luminance of each color by multiplying the red component value by .2126, the green component value by .7152, and the blue component by .0722 before adding all three of those results together. You’ll note that green gets the highest value here,

So, one last time for teal:

Component Equation Value
Red 0.01032982  X 0.2126 0.00219611973
Green 0.50288646  X 0.7152 0.35966439619
Blue 0.33245154  X 0.0722 0.02400300118

…and add them together for luminance!

L1 = 0.00219611973 + 0.35966439619 + 0.02400300118 = 0.38586352

If we do the same to get our L2 value, that gives us 0.24620133.

We finally have the L1 and L2 values we need to calculate contrast. To determine which value is  L1 and and which is L2 , we need to make sure that the larger number (which shows the lighter color) is always L1 and is divided by the smaller/darker color as L2.

Now compare that result with the WCAG success criterias. For standard text size, between 18-22 points, a minimul result of 4.5 will pass with a grade of AA. If our text is larger, then a slightly lower score of  3 will do the job. But to get the highest WCAG grade (AAA), we have to have a contrast ratio result of at least 7. Our lovely combination fails all tests, coming far under 4.5 for regular text or 3 for headline style text. Time to choose some better colors!

I’m so glad we have computers and online tools to do this work for us! Trying to work out the details step-by-step on paper gave me a couple weeks of frustration. It was a lot of me getting things wrong when comparing results to those of automated contrast checkers.

Remember how teachers in school always wanted you to show your math work to prove how you got to the answer? I made something to help us out.

If you view this demo with the console open, you’ll see the math that goes into each step of the calculations. Go ahead, try our two example colors, like #1ABC9C and #888888.

I just want my page to have proper contrast, what do I do?!

There are a variety of accessibility resources that you can can audit your site. Here’s a list I put together, and there’s another list here on CSS-Tricks.

But here are a few tips to get you started.

First, identify areas that are not serving your accessibility needs.

The WAVE accessibility tool is a good place to start. Run your site through that and it will give you contrast results and help identify trouble areas.

Yay, passing scores!

Follow the suggestions of the audit

Use best practices to improve your scores, and remove the errors. Once you identify contrast errors, you can try out some different options right there in the WAVE tool. Click on the color box to pop open a color picker. Then play around until the errors go away, and you’ll know what you can replace in your code.

Run the test again

This way, you can make sure your changes improved things. Congratulations! You just made your product better for all users, not just ones affected by the accessibility errors!

What comes next is up to you!

You can make it easier on yourself and start all new products with the goal of making them accessible. Make accessibility guidelines part of your requirements for both technology and design. You’ll save yourself potentially hundreds of hours of remediation, and potential legal complaints. U.S. government and education websites are required to comply, but other industries are often taken to task for not making their sites equally available for all people.

If you have the option, consider using established and tested frameworks and web libraries (like Bootstrap or Google’s Material Design) that have already figured out optimum contrast theme colors. In many cases, you can take just what you need (like only the CSS) or at least review their color palettes to inform choices. You should still check the contrast though because, while most standard text options in a framework may follow contrast ratio WCAG suggestions, things like alert and message styles may not. (I’m looking at you, Bootstrap!)

Derek Kay has reviewed a list of web frameworks with a focus on accessibility, which I suggest you read if you are looking for more options. The U.S. Web Design System shows one way to solve color/contrast puzzles using their CSS token system that labels colors to make contrast differences super clear), but they also link to several very good resources for improving and understanding contrast.

We took a deeper dive here than perhaps you ever really need to know, but understanding what a contrast ratio is and what it actually means should help you remember to keep contrast in mind when designing future sites, web apps, and other software.

Having a clearer understanding of what the contrast ratio means helps me to remember who poor contrast can affect, and how to improve web and mobile products overall.

I’m not the ultimate subject expert on contrast, just a very, very curious girl who sometimes has issues reading things on the web with low contrast.

If you have any additional thoughts, corrections or further research to share, please leave a comment and I’ll amend this article! The fuller our understanding of the needs and requirements of our sites is, the better we can plan improvements and ultimately serve the needs of our audiences.

The post Understanding Web Accessibility Color Contrast Guidelines and Ratios appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

The Best Color Functions in CSS?

I’ve said before that HSL is the best color format we have. Most of us aren’t like David DeSandro, who can read hex codes. HSL(a) is Hue, Saturation, Lightness, and alpha, if we need it.

hsl(120, 100%, 40%)

Hue isn’t intuitive, but it’s not that weird. You take a trip around the color wheel from 0 to 360. Saturation is more obvious where 0% has all the color sucked out, like grayscale, and 100% is fully rich color at that hue. Lightness is “normal” at 50% and adds white or black as you go toward 100% and 0%, respectively. I’m sure that’s not the correct scientific or technical way of explaining it, but that’s the brain logic.

There are still issues with using HSL, which Brian Kardell explains in depth. I’m far from a color expert, but I think I see what Brian (and Adam) are saying in that article. Say you have three different colors and they all have the exact same lightness in HSL. That doesn’t mean they are all actually the same lightness. That’s kinda weird, particularly when you’re using this color format as part of a system of colors.

The good news is that there are color features already specced as a CSS Level 4 module that help with this: Lab and LCH. Check out the example from Adam where the colors in Lab have values that reflect their actual lightness much more accurately to how we perceive it.

Brian:

There are color spaces like Lab and LCH which deal with the full spectrum and have qualities like perceptual uniformness. Thus, if we want great color functions for use in real design systems everyone seems to agree that having support to do said math in the Lab/LCH color spaces is the ideal enabling feature.

In the bug ticket for Chrome, Tab thinks these would be almost trivial to implement.

Note that lab()/lch()/gray() can all be eagerly converted into our existing color infrastructure; they don’t introduce any fundamentally new concepts, they’re just a better way to specify colors, more closely associated with how our eyes actually function rather than being closely tied to how rgb pixels function.

The conversion functions to turn it into rgb are a little bit of code, but it’s just some exponentials and a bit of matrix multiplication, and it’s well-documented in the spec.

This should be a GoodFirstBug sort of thing, I think.

The post The Best Color Functions in CSS? appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

So Many Color Links

There’s been a run of tools, articles, and resources about color lately. Please allow me to close a few tabs by rounding them up here for your enjoyment.

Curated colors in context

Happy Hues demonstrates a bunch of color palettes in the context of the site itself. That’s a nice way to do it, because choosing nice colors isn’t enough — it’s all about context. It can go bad, as the Refactoring UI blog demonstrates.

Dynamic, Date-Based Color with JavaScript, HSL, and CSS Variables

Rob Weychert shows off how he created date-based color schemes (so that every single day of the past 30 years would have a unique color scheme, each day looking slightly different than the day before).

Calculating Color: Dynamic Color Theming with Pure CSS.

Una Kravets creates color themes just with CSS. No JavaScript. No CSS preprocessing. Just Custom Properties, HSL colors, and some calc() in Calculating Color: Dynamic Color Theming with Pure CSS.

Color Tools

We’ve tweeted about color tools a lot. We’ve even threaded them up from time-to-time.

Visualizing Every Pantone Color of the Year

Adam Fuhrer took 20 years of top Pantone colors and matched them with wonderful photos. I love that the photos link to the personal sites of the actual photographers. It weirdly reminds me that you can browse Dribbble by color.

A Handy Sass-Powered Tool for Making Balanced Color Palettes

Stephanie Eckles blogged about using Sass to do math on colors to calculate and graph their luminance, saturation, and lightness, which can give you a by-the-numbers look to see if your color scheme is cohesive or not.

Leonardo

Leonardo is an interactive color palette tool that helps interpolate colors and generate variations based on contrast ratio.

Color Puns

Nice.

The post So Many Color Links appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A Handy Sass-Powered Tool for Making Balanced Color Palettes

For those who may not come from a design background, selecting a color palette is often based on personal preferences. Choosing colors might be done with an online color tool, sampling from an image, “borrowing” from favorite brands, or just sort of randomly picking from a color wheel until a palette “just feels right.”

Our goal is to better understand what makes a palette “feel right” by exploring key color attributes with Sass color functions. By the end, you will become more familiar with:

  • The value of graphing a palette’s luminance, lightness, and saturation to assist in building balanced palettes
  • The importance of building accessible contrast checking into your tools
  • Advanced Sass functions to extend for your own explorations, including a CodePen you can manipulate and fork

What you’ll ultimately find, however, is that color on the web is a battle of hardware versus human perception.

What makes color graphing useful

You may be familiar with ways of declaring colors in stylesheets, such as RGB and RGBA values, HSL and HSLA values, and HEX codes.

rbg(102,51,153) rbga(102,51,153, 0.6) hsl(270, 50%, 40%) hsla(270, 50%, 40%, 0.6) #663399

Those values give devices instructions on how to render color. Deeper attributes of a color can be exposed programmatically and leveraged to understand how a color relates to a broader palette.

The value of graphing color attributes is that we get a more complete picture of the relationship between colors. This reveals why a collection of colors may or may not feel right together. Graphing multiple color attributes helps hint at what adjustments can be made to create a more harmonious palette. We’ll look into examples of how to determine what to change in a later section.

Two useful measurements we can readily obtain using built-in Sass color functions are lightness and saturation.

  • Lightness refers to the mix of white or black with the color.
  • Saturation refers to the intensity of a color, with 100% saturation resulting in the purest color (no grey present).
$ color: rebeccapurple;  @debug lightness($ color); // 40%  @debug saturation($ color); // 50%;

However, luminance may arguably be the most useful color attribute. Luminance, as represented in our tool, is calculated using the WCAG formula which assumes an sRGB color space. Luminance is used in the contrast calculations, and as a grander concept, also aims to get closer to quantifying the human perception of relative brightness to assess color relationships. This means that a tighter luminance value range among a palette is likely to be perceived as more balanced to the human eye. But machines are fallible, and there are exceptions to this rule that you may encounter as you manipulate palette values. For more extensive information on luminance, and a unique color space called CIELAB that aims to even more accurately represent the human perception of color uniformity, see the links at the end of this article.

Additionally, color contrast is exceptionally important for accessibility, particularly in terms of legibility and distinguishing UI elements, which can be calculated programmatically. That’s important in that it means tooling can test for passing values. It also means algorithms can, for example, return an appropriate text color when passed in the background color. So our tool will incorporate contrast checking as an additional way to gauge how to adjust your palette.

The functions demonstrated in this project can be extracted for helping plan a contrast-safe design system palette, or baked into a Sass framework that allows defining a custom theme.

Sass as a palette building tool

Sass provides several traditional programming features that make it perfect for our needs, such as creating and iterating through arrays and manipulating values with custom functions. When coupled with an online IDE, like CodePen, that has real-time processing, we can essentially create a web app to solve specific problems such as building a color palette.

Here is a preview of the tool we’re going to be using:

See the Pen
Sass Color Palette Grapher
by Stephanie Eckles (@5t3ph)
on CodePen.

Features of the Sass palette builder

  • It outputs an aspect ratio-controlled responsive graph for accurate plot point placement and value comparing.
  • It leverages the result of Sass color functions and math calculations to correctly plot points on a 0–100% scale.
  • It generates a gradient to provide a more traditional “swatch” view.
  • It uses built-in Sass functions to extract saturation and lightness values.
  • It creates luminance and contrast functions (forked from Material Web Components in addition to linking in required precomputed linear color channel values).
  • It returns appropriate text color for a given background, with a settings variable to change the ratio used.
  • It provides functions to uniformly scale saturation and lightness across a given palette.

Using the palette builder

To begin, you may wish to swap from among the provided example palettes to get a feel for how the graph values change for different types of color ranges. Simply copy a palette variable name and swap it for $ default as the value of the $ palette variable which can be found under the comment SWAP THE PALETTE VARIABLE.

Next, try switching the $ contrastThreshold variable value between the predefined ratios, especially if you are less familiar with ensuring contrast passes WCAG guidelines.

Then try to adjust the $ palette-scale-lightness or $ palette-scale-saturation values. Those feed into the palette function and uniformly scale those measurements across the palette (up to the individual color’s limit).

Finally, have a go at adding your own palette, or swap out some colors within the examples. The tool is a great way to explore Sass color functions to adjust particular attributes of a color, some of which are demonstrated in the $ default palette.

Interpreting the graphs and creating balanced, accessible palettes

The graphing tool defaults to displaying luminance due to it being the most reliable indicator of a balanced palette, as we discussed earlier. Depending on your needs, saturation and lightness can be useful metrics on their own, but mostly they are signalers that can help point to what needs adjusting to bring a palette’s luminance more in alignment. An exception may be creating a lightness scale based on each value in your established palette. You can swap to the $ stripeBlue example for that.

The $ default palette is actually in need of adjustment to get closer to balanced luminance:

The $ default palette’s luminance graph

A palette that shows well-balanced luminance is the sample from Stripe ($ stripe):

The $ stripe palette luminance graph

Here’s where the tool invites a mind shift. Instead of manipulating a color wheel, it leverages Sass functions to programmatically adjust color attributes.

Check the saturation graph to see if you have room to play with the intensity of the color. My recommended adjustment is to wrap your color value with the scale-color function and pass an adjusted $ saturation value, e.g. example: scale-color(#41b880, $ saturation: 60%). The advantage of scale-color is that it fluidly adjusts the value based on the given percent.

Lightness can help explain why two colors feel different by assigning a value to their brightness measured against mixing them with white or black. In the $ default palette, the change-color function is used for purple to align it’s relative $ lightness value with the computed lightness() of the value used for the red.

The scale-color function also allows bundling both an adjusted $ saturation and $ lightness value, which is often the most useful. Note that provided percents can be negative.

By making use of Sass functions and checking the saturation and lightness graphs, the $ defaultBalancedLuminance achieves balanced luminance. This palette also uses the map-get function to copy values from the $ default palette and apply further adjustments instead of overwriting them, which is handy for testing multiple variations such as perhaps a hue shift across a palette.

The $ defaultBalancedLuminance luminance graph

Take a minute to explore other available color functions.

http://jackiebalzer.com/color offers an excellent web app to review effects of Sass and Compass color functions.

Contrast comes into play when considering how the palette colors will actually be used in a UI. The tool defaults to the AA contrast most appropriate for all text: 4.5. If you are building for a light UI, then consider that any color used on text should achieve appropriate contrast with white when adjusting against luminance, indicated by the center color of the plot point.

Tip: The graph is set up with a transparent background, so you can add a background rule on body if you are developing for a darker UI.

Further reading

Color is an expansive topic and this article only hits the aspects related to Sass functions. But to truly understand how to create harmonious color systems, I recommend the following resources:

  • Color Spaces – is a super impressive deep-dive with interactive models of various color spaces and how they are computed.
  • Understanding Colors and Luminance – A beginner-friendly overview from MDN on color and luminance and their relationship to accessibility.
  • Perpetually Uniform Color Spaces – More information on perceptually uniform color systems, with an intro the tool HSLuv that converts values from the more familiar HSL color space to the luminance-tuned CIELUV color space.
  • Accessible Color Systems – A case study from Stripe about their experience building an accessible color system by creating custom tooling (which inspired this exploration and article).
  • A Nerd’s Guide to Color on the Web – This is a fantastic exploration of the mechanics of color on the web, available right here on CSS-Tricks.
  • Tanaguru Contrast Finder – An incredible tool to help if you are struggling to adjust colors to achieve accessible contrast.
  • ColorBox – A web app from Lyft that further explores color scales through graphing.
  • Designing Systematic Colors – Describes Mineral UI‘s exceptional effort to create color ramps to support consistent theming via a luminance-honed palette.
  • How we designed the new color palettes in Tableau 10 – Tableau exposed features of their custom tool that helped them create a refreshed palette based on CIELAB, including an approachable overview of that color space.

The post A Handy Sass-Powered Tool for Making Balanced Color Palettes appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Finally, it Will Be Easy to Change the Color of List Bullets

In my germinating years, the general advice was this:

<ul>   <li><span>List item</span></li>   <!-- ... --> </ul>
li { color: red; } /* bullet */ li span (color: black; } /* text */

Not terrible, but not great. You’re “resetting” everything at the span level, so it gets more complicated the more you do.

Things are getting much easier. Let’s take a walk through this world getting more modern as we go.


An alternative was to rip off the default list styling and replace it with a pseudo-element.

ul {   list-style: none; }  li::before {   content: "• ";   color: red; }

If we need to count, we could do that with CSS counters.

ol {   list-style: none;   counter-reset: my-awesome-counter; }  ol li {   counter-increment: my-awesome-counter; }  ol li::before {   content: counter(my-awesome-counter) ". ";   color: red; }

Quick aside here: this doesn’t help with the color, but you can specify what character to use for the bullet by setting a string, like:

ul {   list-style-type: '✽ '; }

This is as of Firefox 39 (2015) and Chrome 79 (which comes out Dec 9, 2019).

For ordered lists, there is a ton of language-specific options. And those language styles work for CSS counters as well, which you can learn more about in Hui Jing’s deep dive.

See the Pen
Random CSS counters playground
by Chen Hui Jing (@huijing)
on CodePen.


But all the while, we only wanted to select the stupid bullet (or whatever it is) and style it. Now we are starting to be able to do just that.

As of Firefox 68 (July 2019), you can do like:

li::marker {   color: red;   content: "►"; }

…which, as you can see, changes the color and the bullet thing That is definitely the cleanest and easiest way to go, so it’s nice to see progress.

Tejas demonstrates:

See the Pen
::marker example
by Tejas (@tejask)
on CodePen.

Manuel Matuzović notes that if you set an element to a list-item display type, you can use markers on them as well.

h2 {   display: list-item; }  h2::marker {   color: orange;   content: "☞"; }

Even Safari has support at the time of this writing, so we should lean on Chrome here.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Opera Firefox IE Edge Safari
No No 68 No No 11.1

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
11.3-11.4 No No No No No

The post Finally, it Will Be Easy to Change the Color of List Bullets appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Designing accessible color systems

The team at Stripe explores how they’re 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 needed.

We built a web interface to allow us to visualize and manipulate our color system using perceptually uniform color models. The tool gave us an immediate feedback loop while we were iterating on our colors—we could see the effect of every change.

This tool is…whoa! I would love to learn a bit more about why they built this though as it looks like it wouldn’t have been a particularly quick and easy thing to put together. I wonder if that team has to support a wide-range of colors for their charts or data-visualization UI (as complex charts can often require a much larger range of colors for comparing bits of data effectively). Either way, this is pretty inspiring work.

This somewhat harkens to a couple of techniques for enforcing accessible colors, including one that uses custom properties with calc() and rgb by Josh Bader, and another by Facundo Corradini that also uses custom properties but with hsl with conditional statements.

Direct Link to ArticlePermalink

The post Designing accessible color systems appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Color Inputs: A Deep Dive into Cross-Browser Differences

In this article, we’ll be taking a look at the structure inside <input type='color'> elements, browser inconsistencies, why they look a certain way in a certain browser, and how to dig into it. Having a good understanding of this input allows us to evaluate whether a certain cross-browser look can be achieved and how to do so with a minimum amount of effort and code.

Here’s exactly what we’re talking about:

But before we dive into this, we need to get into…

Accessibility issues!

We’ve got a huge problem here: for those who completely rely on a keyboard, this input doesn’t work as it should in Safari and in Firefox on Windows, but it does work in Firefox on Mac and Linux (which I only tested on Fedora, so feel free to yell at me in the comments if it doesn’t work for you using another distribution).

In Firefox on Windows, we can Tab to the input to focus it, press Enter to bring up a dialog… which we then cannot navigate with the keyboard!

I’ve tried tabbing, arrow keys, and every other key available on the keyboard… nothing! I could at least close the dialog with good old Alt + F4. Later, in the bug ticket I found for this on Bugzilla, I also discovered a workaround: Alt + Tab to another window, then Alt + Tab back and the picker dialog can be navigated with the keyboard.

Things are even worse in Safari. The input isn’t even focusable (bug ticket) if VoiceOver isn’t on. And even when using VoiceOver, tabbing through the dialog the inputs opens is impossible.

If you’d like to use <input type='color'> on an actual website, please let browsers know this is something that needs to be solved!

How to look inside

In Chrome, we need to bring up DevTools, go to Settings and, in the Preferences section under Elements, check the Show user agent shadow DOM option.

How to view the structure inside an input in Chrome.

Then, when we return to inspect our element, we can see inside its shadow DOM.

In Firefox, we need to go to about:config and ensure the devtools.inspector.showAllAnonymousContent flag is set to true.

How to view the structure inside an input in Firefox.

Then, we close the DevTools and, when we inspect our input again, we can see inside our input.

Sadly, we don’t seem to have an option for this in pre-Chromium Edge.

The structure inside

The structure revealed in DevTools differs from browser to browser, just like it does for range inputs.

In Chrome, at the top of the shadow DOM, we have a <div> wrapper that we can access using ::-webkit-color-swatch-wrapper.

Inside it, we have another <div> we can access with ::-webkit-color-swatch.

Screenshot of Chrome DevTools showing the shadow DOM of the <input type='color'>. Right at the top, we have a div which is the swatch wrapper and can be accessed using ::-webkit-color-swatch-wrapper. Inside it, there’s another div which is the swatch and can be accessed using ::-webkit-color-swatch. This div has the background-color set to the value of the parent color input.”/><figcaption>Inner structure in Chrome.</figcaption></figure>
<p>In Firefox, we only see one <code><div></code>, but it’s not labeled in any way, so how do we access it?</p>
<p>On a hunch, given this <code><div></code> has the <code>background-color</code> set to the input’s <code>value</code> attribute, just like the <code>::-webkit-color-swatch</code> component, I tried <code>::-moz-color-swatch</code>. And it turns out it works!</p>
<figure><img src=this issue from 2016. Pre-Chromium Edge apparently doesn’t allow us to style whatever is inside this input. Well, that’s a bummer.

How to look at the browser styles

In all browsers, we have the option of not applying any styles of our own and then looking at the computed styles.

In Chrome and Firefox, we can also see the user agent stylesheet rule sets that are affecting the currently selected element (though we need to explicitly enable this in Firefox, as seen in the previous section).

Screenshot collage of Chrome DevTools and Firefox DevTools showing where to look for user agent styles: Elements > Styles in Chrome and Inspector > Styles in Firefox.”/><figcaption>Checking browser styles in Chrome and Firefox.</figcaption></figure>
<p>This is oftentimes more helpful than the computed styles, but there are exceptions and we should still always check the computed values as well.</p>
<p>In Firefox, we can also see the CSS file for the <code>form</code> elements at <code>view-source:resource://gre-resources/forms.css</code>.</p>
<figure><img src=
Checking browser styles in Firefox.

The input element itself

We’ll now be taking a look at the default values of a few properties in various browsers in order to get a clear picture of what we’d really need to set explicitly in order to get a custom cross-browser result.

The first property I always think about checking when it comes to <input> elements is box-sizing. The initial value of this property is border-box in Firefox, but content-box in Chrome and Edge.

Comparative screenshots of DevTools in the three browsers showing the computed values of box-sizing for the actual input.
The box-sizing values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

We can see that Firefox is setting it to border-box on <input type='color'>, but it looks like Chrome isn’t setting it at all, so it’s left with the initial value of content-box (and I suspect the same is true for Edge).

In any event, what it all means is that, if we are to have a border or a padding on this element, we also need to explicitly set box-sizing so that we get a consistent result across all these browsers.

The font property value is different for every browser, but since we don’t have text inside this input, all we really care about is the font-size, which is consistent across all browsers I’ve checked: 13.33(33)px. This is a value that really looks like it came from dividing 40px by 3, at least in Chrome.

Comparative screenshots of DevTools in the three browsers showing the font values for the actual input.
The font values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

This is a situation where the computed styles are more useful for Firefox, because if we look at the browser styles, we don’t get much in terms of useful information:

Screenshot of what we get if we look at the browser styles where the font was set for Firefox. The value for the font is -moz-field, which is an alias for the look of a native text field. Expanding this to check the longhands shows us empty instead of actual values.
Sometimes the browser styles are pretty much useless (Firefox screenshot).

The margin is also consistent across all these browsers, computing to 0.

Comparative screenshots of DevTools in the three browsers showing the margin values for the actual input.
The margin values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

The border is different for every single browser. In both Chrome and Edge, we have a solid 1px one, but the border-color is different (rgb(169, 169, 169) for Chrome and rgb(112, 112, 112) for Edge). In Firefox, the border is an outset 2px one, with a border-color of… ThreeDLightShadow?!

Comparative screenshots of DevTools in the three browsers showing the border values for the actual input.
The border values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

What’s the deal with ThreeDLightShadow? If it doesn’t sound familiar, don’t worry! It’s a (now deprecated) CSS2 system value, which Firefox on Windows shows me to be rgb(227, 227, 227) in the Computed styles tab.

Screenshot of Computed panel search in Firefox on Windows, showing that the ThreeDLightShadow keyword computes to rgb(227, 227, 227).
Computed border-color for <input type='color'> in Firefox on Windows.

Note that in Firefox (at least on Windows), the operating system zoom level (SettingsSystemDisplayScale and LayoutChange the size of text, apps and other items) is going to influence the computed value of the border-width, even though this doesn’t seem to happen for any other property I’ve checked and it seems to be partially related to the border-style.

Screenshot showing the Windows display settings window with the zoom level options dropdown opened.
Zoom level options on Windows.

The strangest thing is the computed border-width values for various zoom levels don’t seem to make any sense. If we keep the initial border-style: outset, we have:

  • 1.6px for 125%
  • 2px for 150%
  • 1.7px for 175%
  • 1.5px for 200%
  • 1.8px for 225%
  • 1.6px for 250%
  • 1.66667px for 300%

If we set border-style: solid, we have a computed border-width of 2px, exactly as it was set, for zoom values that are multiples of 50% and the exact same computed values as for border-style: outset for all the other zoom levels.

The padding is the same for Chrome and Edge (1px 2px), while Firefox is the odd one out again.

Comparative screenshots of DevTools in the three browsers showing the padding values for the actual input.
The padding values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

It may look like the Firefox padding is 1px. That’s what it is set to and there’s no indication of anything overriding it — if a property is overridden, then it’s shown as grey and with a strike-through.

Screenshot of Firefox DevTools highlighting how the border set on input[type='color'] overrides the one set on input and the look (grey + strike-through) of overridden properties.
Spotting overrides in Firefox.

But the computed value is actually 0 8px! Moreover, this is a value that doesn’t depend on the operating system zoom level. So, what the hairy heck is going on?!

Screenshot of Firefox DevTools showing how the computed padding value on <input type='color'> isn’t the one that was set on input, even if no override seems to be happening.”/><figcaption>Computed value for <code>padding</code> in Firefox doesn’t match the value that was set on input.</figcaption></figure>
<p>Now, if you’ve actually tried inspecting a color input, took a close look at the styles set on it, and your brain works differently than mine (meaning you do read what’s in front of you and don’t just scan for the one thing that interests you, completely ignoring everything else…) then you’ve probably noticed there is something overriding the <code>1px</code> padding (and <a href=should be marked as such) — the flow-relative padding!

Screenshot of Firefox DevTools showing the flow-relative padding overriding the old padding due to higher specificity of selector (input[type='color'] vs. input).
Flow-relative padding overrides in Firefox.

Dang, who knew those properties with lots of letters were actually relevant? Thanks to Zoltan for noticing and letting me know. Otherwise, it probably would have taken me two more days to figure this one out.

This raises the question of whether the same kind of override couldn’t happen in other browsers and/or for other properties.

Edge doesn’t support CSS logical properties, so the answer is a “no” in that corner.

In Chrome, none of the logical properties for margin, border or padding are set explicitly for <input type='color'>, so we have no override.

Concerning other properties in Firefox, we could have found ourselves in the same situation for margin or for border, but with these two, it just so happens the flow-relative properties haven’t been explicitly set for our input, so again, there’s no override.

Even so, it’s definitely something to watch out for in the future!

Moving on to dimensions, our input’s width is 44px in Chrome and Edge and 64px in Firefox.

Comparative screenshots of DevTools in the three browsers showing the width values for the actual input.
The width values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

Its height is 23px in all three browsers.

Comparative screenshots of DevTools in the three browsers showing the height values for the actual input.
The height values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

Note that, since Chrome and Edge have a box-sizing of content-box, their width and height values do not include the padding or border. However, since Firefox has box-sizing set to border-box, its dimensions include the padding and border.

Comparative screenshots of DevTools in the three browsers showing the layout boxes.
The layout boxes for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

This means the content-box is 44pxx23px in Chrome and Edge and 44xpxx19px in Firefox, the padding-box is 48pxx25 in Chrome and Edge and 60pxx19px in Firefox and the border-box is 50pxx27px in Chrome and Edge and 64pxx23 in Firefox.

We can clearly see how the dimensions were set in Chrome and I’d assume they were set in the same direct way in Edge as well, even if Edge doesn’t allow us to trace this stuff. Firefox doesn’t show these dimensions as having been explicitly set and doesn’t even allow us to trace where they came from in the Computed tab (as it does for other properties like border, for example). But if we look at all the styles that have been set on input[type='color'], we discover the dimensions have been set as flow-relative ones (inline-size and block-size).

Screenshot of the Firefox user agent styles showing flow relative dimensions being set on input[type='color'].
How <input type='color'> dimensions have been set in Firefox.

The final property we check for the normal state of the actual input is background. Here, Edge is the only browser to have a background-image (set to a top to bottom gradient), while Chrome and Firefox both have a background-color set to ButtonFace (another deprecated CSS2 system value). The strange thing is this should be rgb(240, 240, 240) (according to this resource), but its computed value in Chrome is rgb(221, 221, 221).

Comparative screenshots of DevTools in the three browsers showing the background values for the actual input.
The background values for <input type='color'> compared in Chrome, Firefox and Edge (from top-to-bottom).

What’s even stranger is that, if we actually look at our input in Chrome, it sure does look like it has a gradient background! If we screenshot it and then use a picker, we get that it has a top to bottom gradient from #f8f8f8 to #ddd.

Screenshot of the input in Chrome. A very light, almost white, grey to another light, but still darker grey gradient from top to bottom can be seen as the background, not the solid background-color indicated by DevTools.
What the actual input looks like in Chrome. It appears to have a gradient, in spite of the info we get from DevTools telling us it doesn’t.

Also, note that changing just the background-color (or another property not related to dimensions like border-radius) in Edge also changes the background-image, background-origin, border-color or border-style.

Animated gif. Shows the background-image, background-origin, border-color, border-style before and after changing the seemingly unrelated background-color - their values don't get preserved following this change.
Edge: side-effects of changing background-color.

Other states

We can take a look at the styles applied for a bunch of other states of an element by clicking the :hov button in the Styles panel for Chrome and Firefox and the a: button in the same Styles panel for Edge. This reveals a section where we can check the desired state(s).

Screenshot collage highlighting the buttons that bring up the states panel in Chrome, Firefox and Edge.
Taking a look at other states in Chrome, Firefox, Edge (from top to bottom).

Note that, in Firefox, checking a class only visually applies the user styles on the selected element, not the browser styles. So, if we check :hover for example, we won’t see the :hover styles applied on our element. We can however see the user agent styles matching the selected state for our selected element shown in DevTools.

Also, we cannot test for all states like this and let’s start with such a state.

:disabled

In order to see how styles change in this state, we need to manually add the disabled attribute to our <input type='color'> element.

Hmm… not much changes in any browser!

In Chrome, we see the background-color is slightly different (rgb(235, 235, 228) in the :disabled state versus rgb(221, 221, 221) in the normal state).

Chrome DevTools screenshot showing the background being set to rgb(235, 235, 228) for a :disabled input.
Chrome :disabled styling.

But the difference is only clear looking at the info in DevTools. Visually, I can tell tell there’s a slight difference between an input that’s :disabled and one that’s not if they’re side-by-side, but if I didn’t know beforehand, I couldn’t tell which is which just by looking at them, and if I just saw one, I couldn’t tell whether it’s enabled or not without clicking it.

Disabled and enabled input side by side in Chrome. There is a slight difference in background-color, but it's pretty much impossible to tell which is which just by looking at them.
Disabled (left) versus enabled (right) <input type='color'> in Chrome.

In Firefox, we have the exact same values set for the :disabled state as for the normal state (well, except for the cursor, which realistically, isn’t going to produce different results save for exceptional cases anyway). What gives, Firefox?!

Comparison of styles set in Firefox for <input type='color'> in its normal state and its :disabled state. The padding and border set in the :disabled case are exactly the same as those set in the normal case.”/><figcaption>Firefox <code>:disabled</code> (top) versus normal (bottom) styling.</figcaption></figure>
<p>In Edge, both the <code>border-color</code> and the <code>background</code> gradient are different.</p>
<figure><img src=
Edge :disabled styling (by checking computed styles).

We have the following styles for the normal state:

border-color: rgb(112, 112, 112); background-image: linear-gradient(rgb(236, 236, 236), rgb(213, 213, 213));

And for the :disabled state:

border-color: rgb(186, 186, 186); background-image: linear-gradient(rgb(237, 237, 237), rgb(229, 229, 229));

Clearly different if we look at the code and visually better than Chrome, though it still may not be quite enough:

Disabled and enabled input side by side in Edge. There is a slight difference in background-image and a bigger difference in border-color, but it still may be difficult to tell whether an input is enabled or not at first sight without having a reference to compare.
Disabled (left) versus enabled (right) <input type='color'> in Edge.
:focus

This is one state we can test by toggling the DevTools pseudo-classes. Well, in theory. In practice, it doesn’t really help us in all browsers.

Starting with Chrome, we can see that we have an outline in this state and the outline-color computes to rgb(77, 144, 254), which is some kind of blue.

Chrome DevTools screenshot showing an outline for an input having <code>:focus</code>.”/><figcaption>Chrome <code>:focus</code> styling.</figcaption></figure>
<p>Pretty straightforward and easy to spot.</p>
<p>Moving on to Firefox, things start to get hairy! Unlike Chrome, toggling the <code>:focus</code> pseudo-class from DevTools does nothing on the input element, though by focusing it (by tab click), the <code>border</code> becomes blue and we get a <code>dotted</code> rectangle within — but there’s no indication in DevTools regarding what is happening.</p>
<figure><img src=
What happens in Firefox when tabbing to our input to :focus it.

If we check Firefox’s forms.css, it provides an explanation for the dotted rectangle. This is the dotted border of a pseudo-element, ::-moz-focus-inner (a pseudo-element which, for some reason, isn’t shown in DevTools inside our input as ::-moz-color-swatch is). This border is initially transparent and then becomes visible when the input is focused — the pseudo-class used here (:-moz-focusring) is pretty much an old Firefox version of the new standard (:focus-visible), which is currently only supported by Chrome behind the Experimental Web Platform features flag.

Firefox DevTools screenshot where the inner dotted rectangle on :focus comes from: it is set as a transparent border on the ::-moz-focus-inner pseudo-element and it becomes visible when the input should have a noticeable :focus indicator.
Firefox: where the inner dotted rectangle on :focus comes from.

What about the blue border? Well, it appears this one isn’t set by a stylesheet, but at an OS level instead. The good news is we can override all these styles should we choose to do so.

In Edge, we’re faced with a similar situation. Nothing happens when toggling the :focus pseudo-class from DevTools, but if we actually tab to our input to focus it, we can see an inner dotted rectangle.

Animated gif. Shows how, on :focus, our input gets an inner dotted rectangle.
What happens in Edge when tabbing to our input to :focus it.

Even though I have no way of knowing for sure, I suspect that, just like in Firefox, this inner rectangle is due to a pseudo-element that becomes visible on :focus.

:hover

In Chrome, toggling this pseudo-class doesn’t reveal any :hover-specific styles in DevTools. Furthermore, actually hovering the input doesn’t appear to change anything visually. So it looks like Chrome really doesn’t have any :hover-specific styles?

In Firefox, toggling the :hover pseudo-class from DevTools reveals a new rule in the styles panel:

Screenshot of Firefox DevTools showing the rule set that shows up for the :hover state.
Firefox :hover styling as seen in DevTools.

When actually hovering the input, we see the background turns light blue and the border blue, so the first thought would be that light blue is the -moz-buttonhoverface value and that the blue border is again set at an OS level, just like in the :focus case.

Animated gif. Shows that, on actually hovering our <input type='color'>, it gets a light blue background and a blue border.”/><figcaption>What actually happens in Firefox on <code>:hover</code>.</figcaption></figure>
<p>However, if we look at the computed styles, we see the same <code>background</code> we have in the normal state, so that blue <code>background</code> is probably really set at an OS level as well, in spite of having that rule in the <code>forms.css</code> stylesheet.</p>
<figure><img src=
Firefox: computed background-color of an <input type='color'> on :hover.

In Edge, toggling the :hover pseudo-class from DevTools gives our input a light blue (rgb(166, 244, 255)) background and a blue (rgb(38, 160, 218)) border, whose exact values we can find in the Computed tab:

Screenshot of Edge DevTools showing the computed value for background-color and border-color in the :hover state.
Edge: computed background-color and border-color of an <input type='color'> on :hover.
:active

Checking the :active state in the Chrome DevTools does nothing visually and shows no specific rules in the Styles panel. However, if we actually click our input, we see that the background gradient that doesn’t even show up in DevTools in the normal state gets reversed.

Screenshot of the input in :active state in Chrome. A very light, almost white, grey to another light, but still darker grey gradient from bottom to top can be seen as the background, not the solid background-color indicated by DevTools.
What the actual input looks like in Chrome in the :active state. It appears to have a gradient (reversed from the normal state), in spite of the info we get from DevTools telling us it doesn’t.

In Firefox DevTools, toggling the :active state on does nothing, but if we also toggle the :hover state on, then we get a rule set that changes the inline padding (the block padding is set to the same value of 0 it has in all other states), the border-style and sets the background-color back to our old friend ButtonFace.

Screenshot of Firefox DevTools showing the rule set that shows up for the :active state.
Firefox :active styling as seen in DevTools.

In practice, however, the only thing that matches the info we get from DevTools is the inline shift given by the change in logical padding. The background becomes a lighter blue than the :hover state and the border is blue. Both of these changes are probably happening at an OS level as well.

Animated gif. Shows that, on actually clicking our <input type='color'>, it gets a light blue background and a blue border in addition to sliding 1 pixel in the inline direction as a result of changing the inline padding.”/><figcaption>What actually happens in Firefox in an <code>:active</code> state.</figcaption></figure>
<p>In Edge, activating the <code>:active</code> class from DevTools gives us the exact same styles we have for the <code>:hover</code> state. However, if we have both the <code>:hover</code> and the <code>:active</code> states on, things change a bit. We still have a light blue <code>background</code> and a blue <code>border</code>, but both are darker now (<code>rgb(52, 180, 227)</code> for the <code>background-color</code> and <code>rgb(0, 137, 180)</code> for the <code>border-color</code>):</p>
<figure><img src=
The computed background-color and border-color of an <input type='color'> on :active viewed in Edge.

This is the takeaway: if we want a consistent cross-browser results for <input type='color'>, we should define our own clearly distinguishable styles for all these states ourselves because, fortunately, almost all the browser defaults — except for the inner rectangle we get in Edge on :focus — can be overridden.

The swatch wrapper

This is a component we only see in Chrome, so if we want a cross-browser result, we should probably ensure it doesn’t affect the swatch inside — this means ensuring it has no margin, border, padding or background and that its dimensions equal those of the actual input’s content-box.

In order to know whether we need to mess with these properties (and maybe others as a result) or not, let’s see what the browser defaults are for them.

Fortunately, we have no margin or border, so we don’t need to worry about these.

Chrome DevTools screenshot showing the margin and border values for the swatch wrapper.
The margin and border values for the swatch wrapper in Chrome.

We do however have a non-zero padding (of 4px 2px), so this is something we’ll need to zero out if we want to achieve a consistent cross-browser result.

Chrome DevTools screenshot showing the padding values for the swatch wrapper.
The padding values for the swatch wrapper in Chrome.

The dimensions are both conveniently set to 100%, which means we won’t need to mess with them.

Chrome DevTools screenshot showing the size values for the swatch wrapper.
The size values for the swatch wrapper in Chrome.

Something we need to note here is that we have box-sizing set to border-box, so the padding gets subtracted from the dimensions set on this wrapper.

Chrome DevTools screenshot showing the <code>box-sizing</code> value for the swatch wrapper.”/><figcaption>The <code>box-sizing</code> value for the swatch wrapper in Chrome.</figcaption></figure>
<p>This means that while the <code>padding-box</code>, <code>border-box</code> and <code>margin-box</code> of our wrapper (all equal because we have no <code>margin</code> or <code>border</code>) are identical to the <code>content-box</code> of the actual <code><input type='color'></code> (which is <code>44px</code>x<code>23px</code> in Chrome), getting the wrapper’s <code>content-box</code> involves subtracting the <code>padding</code> from these dimensions. It results that this box is <code>40px</code>x<code>15px</code>.</p>
<figure><img src=
The box model for the swatch wrapper in Chrome.

The background is set to transparent, so that’s another property we don’t need to worry about resetting.

Chrome DevTools screenshot showing the background values for the swatch wrapper.
The background values for the swatch wrapper in Chrome.

There’s one more property set on this element that caught my attention: display. It has a value of flex, which means its children are flex items.

Chrome DevTools screenshot showing the display value for the swatch wrapper.
The display value for the swatch wrapper in Chrome.

The swatch

This is a component we can style in Chrome and Firefox. Sadly, Edge doesn’t expose it to allow us to style it, so we cannot change properties we might want to, such as border, border-radius or box-shadow.

The box-sizing property is one we need to set explicitly if we plan on giving the swatch a border or a padding because its value is content-box in Chrome, but border-box in Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of box-sizing for the swatch component.
The box-sizing values for the swatch viewed in Chrome (top) and Firefox (bottom).

Fortunately, the font-size is inherited from the input itself so it’s the same.

Comparative screenshots of DevTools in the two browsers showing the computed values of font-size for the swatch component.
The font-size values for the swatch viewed in Chrome (top) and Firefox (bottom).

The margin computes to 0 in both Chrome and Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of margin for the swatch component.
The margin values for the swatch viewed in Chrome (top) and Firefox (bottom).

This is because most margins haven’t been set, so they end up being 0 which is the default for <div> elements. However, Firefox is setting the inline margins to auto and we’ll be getting to why that computes to 0 in just a little moment.

Screenshot of Firefox DevTools.
The inline margin for the swatch being set to auto in Firefox.

The border is solid 1px in both browsers. The only thing that differs is the border-color, which is rgb(119, 119, 119) in Chrome and grey (or rgb(128, 128, 128), so slightly lighter) in Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of border for the swatch component.
The border values for the swatch viewed in Chrome (top) and Firefox (bottom).

Note that the computed border-width in Firefox (at least on Windows) depends on the OS zoom level, just as it is in the case of the actual input.

The padding is luckily 0 in both Chrome and Firefox.

Comparative screenshots of DevTools in the two browsers showing the computed values of padding for the swatch component.
The padding values for the swatch viewed in Chrome (top) and Firefox (bottom).

The dimensions end up being exactly what we’d expect to find, assuming the swatch covers its parent’s entire content-box.

Comparative screenshots of DevTools in the two browsers showing the box model for the swatch component.
The box model for the swatch viewed in Chrome (top) and Firefox (bottom).

In Chrome, the swatch parent is the <div> wrapper we saw earlier, whose content-box is 4pxx15px. This is equal to the margin-box and the border-box of the swatch (which coincide as we have no margin). Since the padding is 0, the content-box and the padding-box for the swatch are identical and, subtracting the 1px border, we get dimensions that are 38pxx13px.

In Firefox, the swatch parent is the actual input, whose content-box is 44pxx19px one. This is equal to the margin-box and the border-box of the swatch (which coincide as we have no margin). Since the padding is 0, the content-box and the padding-box for the swatch are identical and, subtracting the 1px border, we get that their dimensions are 42pxx17px.

In Firefox, we see that the swatch is made to cover its parent’s content-box by having both its dimensions set to 100%.

Comparative screenshots of DevTools in the two browsers showing the size values for the swatch component.
The size values for the swatch viewed in Chrome (top) and Firefox (bottom).

This is the reason why the auto value for the inline margin computes to 0.

But what about Chrome? We cannot see any actual dimensions being set. Well, this result is due to the flex layout and the fact that the swatch is a flex item that’s made to stretch such that it covers its parent’s content-box.

Chrome DevTools screenshot showing the flex value for the swatch wrapper.
The flex value for the swatch wrapper in Chrome.

Final thoughts

Phew, we covered a lot of ground here! While it may seem exhaustive to dig this deep into one specific element, this is the sort of exercise that illustrates how difficult cross-browser support can be. We have our own styles, user agent styles and operating system styles to traverse and some of those are always going to be what they are. But, as we discussed at the very top, this winds up being an accessibility issue at the end of the day, and something to really consider when it comes to implementing a practical, functional application of a color input.

Remember, a lot of this is ripe territory to reach out to browser vendors and let them know how they can update their implementations based on your reported use cases. Here are the three tickets I mentioned earlier where you can either chime in or reference to create a new ticket:

The post Color Inputs: A Deep Dive into Cross-Browser Differences appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]