An argument against CSS opacity


The CSS opacity property controls the degree that you can see through an element. By default, all elements have an opacity of 1, which means the foreground information is completely solid and you can’t see the content that’s directly behind it. Authors can set that value to any decimal number from 0 (fully transparent) to 1. I’ve seen sites use this to add some extra flair to their designs, such as creating custom hover or focus designs for interactive controls.

.transparent-content {
  opacity: 0.5;
}

Two black buttons with box-shadows over a green background. The second button is at half opacity so that the green background shows through the entire button.

Something significant about the opacity property is that it applies to the entire element, including its child content, focus outlines, and box-shadows. And all of those can affect accessibility!

Opacity differs from the other way an element can be given transparency, which is through setting an alpha channel. The alpha channel is an additional value that you can set when you define colors for parts of an element (such as its background color or text color) using RGB values, hex codes, or the like. The big difference between setting an alpha channel and using the opacity property is that alpha channel only applies transparency to a specific CSS property and not to the whole element.

For instance, in this example, the element’s background color is set using an alpha channel, which means that this element’s background-color will be partially transparent – but this won’t apply any transparency to the rest of the element:

.transparent-background {
  background-color: rgb(0, 0, 0, 0.5);
}

Two black buttons with box-shadows over a green background. The second button has a background color with half opacity so the green background shows through, but the button text and box-shadow is not affected.

What’s the catch?

If you use the opacity property or colors with alpha channels, that can trick automated accessibility tools into thinking that your site has better contrast than it actually does!

Pretty wild, right? You ran a quick scan of your site, and it said that the contrast was great! But in reality, there might be places where your site’s color contrast falls short of what WCAG requires. For context, WCAG (the industry standard for accessibility conformance) has multiple criteria that speak to color contrast.

  • 1.4.3 Contrast (Minimum) says that text on a web page should have at least a 4.5:1 color-contrast ratio if the text’s font size is under 24px, or at least 3:1 if the font’s size is 24px or larger.
  • 1.4.6 Contrast (Enhanced) is a AAA criterion that requires an even higher contrast ratio of 7:1 for text with a font size under 24px, or 4.5:1 for text with a font size of 24px or larger.
  • 1.4.11 Non-Text Contrast was added in WCAG 2.1, and it requires a color-contrast ratio of at least 3:1 for user-interface components and graphics that convey visual information. This applies to things like graphs, charts, and focus indicators.

Demo

Here’s a demo showing an HTML <button> that’s positioned over a block of content with a blue background. The first example uses the opacity property to adjust the button’s color. The second example uses an alpha channel to set the button’s background color. In both examples, there isn’t enough color contrast between the button’s foreground text and the button’s background color.

See the Pen
Opacity Examples
by TPGi (@TPG)
on CodePen.

If you were to run this page through a few automated tools, you’d get very mixed results. The actual contrast ratios are listed below each test button in the CodePen demo. I derived these values by using TPGi’s Colour Contrast Analyser to manually check each color with an eyedropper tool after I zoomed to a point where the pixilation didn’t significantly affect the results.

Tip: If an automated tool doesn’t detect any contrast issues but you’re sure that an element’s contrast isn’t up to snuff, try to get a more accurate reading by zooming the page’s content to reveal more solid color areas in the text – and then use manual color-checking tools. (This works for elements that are set against a solid background, but this technique may be less effective if the element’s over a background image.)

Comparison of automated scan results
ARC Toolkit WAVE Evaluation Tool axe DevTools
Opacity Example Nothing reported Nothing reported Reported a contrast failure but gave a different contrast ratio from manual testing
Alpha Channel Example Reported a warning that a transparent value got in the way of automatically calculating the contrast Nothing reported Nothing reported

Automated tools were consistently tricked in one way or another by the use of transparency. None of the tools that I checked flagged both scenarios. And even when they were flagged, the actual color values that the tools gave didn’t seem reliable. If you’d like to check for yourself, you can open the embedded CodePen (preferably in debug mode so that CodePen’s <iframe> embeds don’t interfere with the scan) and run these tools on your own machine.

What do we do?

I see transparency used fairly often when I test websites’ accessibility. Sometimes it’s used broadly for decoration, while other times it’s used to create custom hover or focus states for interactive controls. Keep in mind that any time users need to perceive content, that content needs to have enough contrast. At the end of the day, if someone cannot read your content, it doesn’t matter whether your automated tools told you the contrast was good. What matters is whether everyone has equal access to your site.

Fortunately, there’s a pretty straightforward solution. Avoid transparency! Does that work for everyone? Great, have a nice day!

Ok… I get that that may not be realistic.

Realistically, there are some things that you can do that’ll make this easier for tools to catch. But it does involve paying attention to what you’re developing. Only use transparent content when you want the background information to be partially visible as an intentional design choice. For example, if you have a block of text over a background image, setting the background to be slightly transparent is a great solution for making sure the text has good contrast while still allowing most of the graphical content to remain visible. Let’s assume a worst-case scenario where you want to use white text (hex #ffffff or RGB 255, 255, 255) over an image that’s very bright. Maybe the image even has pixels that are also pure white. You can place a background color behind the text and set it to a value such as rgb(0, 0, 0, 0.54) to guarantee that the white text will have enough contrast against every point of the image.

An example of white text being used over variable background colors but maintaining sufficient color contrast due to a semi-transparent box behind the text

On the other hand, don’t use transparency as a coding shortcut if transparency isn’t integral to the design. The button example from my CodePen earlier? That could’ve easily been coded using a specific color value for the button’s background. And if the example were to have used that approach, automated tools would’ve picked up on the contrast failures immediately.

button {
  background-color: #4D6F91;
}

A button with black text and a light gray/blue background that has poor contrast

Visually, this button looks the same as the alpha-channel example. The difference is that automated tools can identify the button’s contrast failure.

Go a step further

CSS also has a prefers-contrast media query that you can use to customize the experience for users who’ve requested higher contrast web content. So even if your transparent content passes WCAG, it may be worth including a prefers-contrast media query to remove transparent content for users who may need more than the required minimum 4.5:1 contrast ratio.

@media (prefers-contrast: more) {
  button {
    opacity: 1 !important;
    background-color: black !important;
    color: white !important;
  }
}

Note that prefers-contrast is different from the forced-colors media query, which lets you detect whether the user has engaged a forced-colors mode in their operating system (such as if you had wanted to detect whether the user had engaged Windows’ high-contrast mode).

Tip: If you’d like to see the effects of prefers-contrast without having to change your system settings, you can emulate high-contrast modes in Chrome’s developer tools by selecting that option in the Rendering tab: Open the dev tools’ “…” menu, go to “More tools,” select Rendering, and then scroll down to the dropdown labeled “Emulate CSS media feature prefers-contrast.”

Try it for yourself by opening the CodePen demo and setting the “Emulate CSS media feature prefers-contrast” option to “prefers-contrast: more.” Play around with these settings in this demo or in Hans Hillen’s more thorough CSS Color Media Queries Demo.

Summary

  • Contrast is important for people with various visual disabilities. It also helps people on mobile devices, but I won’t go into that here.
  • Opacity and alpha channels can fool automated accessibility tools into missing contrast failures.
  • Double check any contrast that looks questionable, especially if automated tools didn’t flag it.
  • Use CSS media queries to improve contrast for people who have preferences set in their operating system.
Categories: Technical
Tags: , ,

About Doug Abrams

Doug started his career in front-end development in 2009. In that role he was introduced to Accessibility and quickly became an advocate. In 2020, Doug joined TPGi so he could focus full-time on digital Accessibility, continue his education on the subject, and help others be more inclusive in the way they develop websites.