Tinted Images with Multiple Backgrounds

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

The background property in CSS can accept comma separated values. “Multiple” backgrounds, if you will. You can also think of them as layered backgrounds since they have a stacking order. If we layer a transparent color over an image, we can “tint” that image. But it’s not quite as obvious as you might suspect.

My first intuitive guess at doing this would be like this:

/* Warning: this doesn't work */
.tinted-image {
  background: 
    /* top, transparent red */ 
    rgba(255, 0, 0, 0.25),
    /* bottom, image */
    url(image.jpg);
}

Unfortunately, that’s not valid. I’m not entirely sure why. A while back when I whined on Twitter about it I got a variety of ideas/reasons/excuses. None of them rang quite true for me. It’s true you cannot comma-separate background-color, but I don’t think that’s relevant here as I’m comma separating the background shorthand not specifically background-color (not to mention ordering those values the other way around works fine).

I suspect the real reason that was decided is because it would be too easy for authors to screw up. background: green, url(image.jpg); would “fail” in a sense in that it would just flood the background green. Forcing the color to be last makes the color kind of like a “fallback” rather than a layer like any other.

Unfortunately we also can’t simply leave the color at the bottom and adjust the opacity of the image to allow some of that color to come through, because background-opacity isn’t a thing. (Although you can kind of fake it.)

There is a way though!

Instead of using a transparent flood color using rgba() or hsla(), we can use a gradient. Gradients are technically background-images and thus not subject to the rule where they can’t come first (be top) in the stacking order.

/* Working method */
.tinted-image {
  background: 
    /* top, transparent red, faked with gradient */ 
    linear-gradient(
      rgba(255, 0, 0, 0.45), 
      rgba(255, 0, 0, 0.45)
    ),
    /* bottom, image */
    url(image.jpg);
}

See the Pen Tinted Image w/ Multiple Backgrounds by Chris Coyier (@chriscoyier) on CodePen

Hey, whatever works, right?