Some of my thoughts, filtered slightly for public consumption.

You Are Probably Misusing Opacity

I have noticed a trend among designers of using opacity/alpha to control the color of text and icons. This is even explicitly recommended by the Material Design specification, Google's official style guide:

Black or white text that is transparent remains legible and vibrant against background color changes. This makes it more flexible than grey text in the same contexts.

The spec gives the example of grey text vs semi-transparent black text on a magenta background. It explains:

Grey text (hex value of #727272) on a white background becomes hard to read if the background color changes to magenta.

This argument might be more convincing if it weren't written in grey[0] text, explicitly violating its own recommendation. The spec also dictates different colors and opacities for light vs dark backgrounds, so it seems they do not entirely buy the background-independence argument. Indeed there are two sides to this story, as the table below illustrates.

Semi-Transparent Black Grey
Is better for light backgrounds. Is better for dark backgrounds.
Is better for light backgrounds. Is better for dark backgrounds.

Based on this alone, I would consider the battle between color and opacity to be a wash. But using opacity comes with some excellent footguns, since there are two ways to set the opacity of text, neither of which do quite what you want. You can set the color property, but this requires overriding the base color as well as the alpha channel[1], so it's unsuitable for CSS classes meant to apply to diverse elements. Alternatively, you can set the opacity attribute, completely avoiding that problem. But opacity doesn't just apply to text—it applies to all content. If your content is just a block of raw text or a single icon this is fine, but if you try to style text or an icon within that block of text, the opacities stack instead of the inner opacity overriding the outer. And if any parent has its color set with an alpha channel, that alpha value stacks as well.

To illustrate the mess this can create, consider the following CSS applied to three nested divs:

.secondary-rgba {
    color: rgba(0, 0, 0, .54);
}
.secondary-opacity {
    opacity: .54;
}
This text has the "secondary-rgba" class, setting its color to black and .54 opaque.
This text has the "secondary-opacity" class, setting its opacity to .54. Because color is inherited and stacks with opacity, it is ~.29 opaque.
This text also has the "secondary-opacity" class. Because its parent's opacity and its inherited color stack with its own opacity, it is ~.16 opaque.

Yes, you really have to read that. At .54 opacity[2], the text is a little straining to read, but not too bad. At .29 it's difficult, and at .16 it's downright frustrating.

There's an even subtler way using opacity can introduce bugs: it triggers a new stacking context. This changes the order in which elements are composited, often causing the semi-opaque element to eclipse other elements, as happens to icons in Twitter's mobile site:

This even bit me when I switched to using a fixed header nav for this blog (on desktop), with the inner divs in the example above eclipsing the nav.

What should we do? My dream is that we can return to readable #000 (or even #333) text everywhere, avoiding all of these problems. Unfortunately, I don't expect designers to abandon faded text any time soon. But maybe we can convince them to at least use color rather than opacity or alpha in their specifications.


  1. ^

    Specifically, a hex value of #616161; I'm not sure where they get #727272, which is not the color of their example, and is almost completely illegible.

  2. ^

    For example, if you create a class .semi-transparent { color: rgba(0, 0, 0, .54); } and use it to override the property color: red on an element, then the resulting element will be semi-transparent black instead of semi-transparent red.

  3. ^

    .54 opacity is the Material Design standard for "secondary" text, whatever that means.