Grow your CSS skills. Land your dream job.

Tinted Images with Multiple Backgrounds

Published by Chris Coyier

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?

Comments

  1. Permalink to comment#

    On a recent pen of mine (which got featured, thanks guys!) I did a trick of making the full-page background image (using one of your previous posts) partially transparent. This allowed me to change the background colour of the page and give the effect that the image was changing colour. I guess doing that was sort of the reverse of doing what you’ve done above. This is a cool effect though, I’ll remember this for later, thanks Chris.

  2. NSDCars5
    Permalink to comment#

    This works in most new browsers, but what about IE?

    Also, how about this? http://codepen.io/NSDCars5/pen/nuzbI

    • MaxArt
      Permalink to comment#

      It works in IE since version 9.

      For IE 8 and older versions, maybe you can do something with the legacy filter property.

    • That technique in your pen has always worked, but it’s relying on markup. You’re just layering elements on top of one another. The technique Chris describes is much more flexible and doesn’t require and change in markup.

  3. Kilian Stinson
    Permalink to comment#

    Isn’t it easier to maintain by using a pseudo-element? So it’s not related to the background-image URL.

    Like this – http://codepen.io/anon/pen/lbfeC

    • NSDCars5
      Permalink to comment#

      You know, that pseudo-element thing, I have to research it. I’ve heard about it, but never used it (I’m pretty new to all this HTML+CSS thing)….

    • Permalink to comment#

      @NSDCars5

      I really recommend you look into it. They can help you pull off some really nice tricks without polluting the HTML markup with elements only existing for the purpose of decoration.

      A good example is “Niet the Dark Absol’s” answer on http://stackoverflow.com/questions/14387690/css-show-only-corner-border

    • Nicholas
      Permalink to comment#

      That’s what I do: Use a pseudo element to add a filter on top of backgrounds/images. Position: absolute with the top, bottom, left, right all set to 0. Then I use rgba to apply the color and transparency.

    • This could seems like a good solutions at first but it starts to break down when you actually have anything inside the container. Any ideas on how to make it more flexible?

      Example

    • Kilian Stinson
      Permalink to comment#

      @Andrew Richardson

      Oops, my fault. I forgot to add position: relative to the container.
      Set position to relative and the width/height of the pseudo-element to 100%.

    • Kilian Stinson
      Permalink to comment#

      @Andrew

      I created a new codepen.

    • Ahh yeah that makes sense. This may be the most versatile option as you can manipulate just the overlay color in any way you want while retaining the background image. Thanks!

    • One could argue that using pseudo elements for decoration is making your CSS less maintainable. You would need to add comments if you’re working on a team. Using multiple background images layered on top of each other is more easy to understand, all in one place and ultimately less CSS to maintain and/or break. Remember, Pseudo elements are still adding elements. It’s just that the browser is creating them.

      Multiple backgrounds seem like the cleaner solution.

      In the long term, less code = more maintainable code.

    • This method can be easily tweaked for some nice hover effects, too.

      .red:after{
      background:rgba(25,55,0,0.2);
      transition: background 200ms ease-out;
      }

      :hover.red:after{
      background:rgba(25,34,23,0.5);
      }

    • Using pseudo-elements is certainly clever but completely inaccessible. Once you add content to the element like Andrew Richardson tried, you can’t select it (unless you select everything – CTRL+A).

      Try selecting the text in your demo with your mouse.

      Agree with Armstrongest, multiple backgrounds is just a plain cleaner solution.

  4. The reason your original syntax doesn’t work is, you’re specifying a background-color and a background-image and images will always stack on top of the colour. By defining a linear-gradient with the same start-end stop values your effectively creating an image of a solid colour block which can be drawn on top of the background image.

    I used this same trick to fake lighting in my CSS 3D demos.

  5. Markus
    Permalink to comment#

    I think the reason that you can’t put a background-color on top is that there simply is only one background-color layer, and it’s always on bottom. In other words, background-color does not apply per layer, it applies once for the whole stack.

    Or, in code: It’s not “background: layer1 color1, layer2 color2, layer3 color3;”, it’s “background: layer1, layer2, layer3 color;”.

  6. Permalink to comment#

    It’s a shame this doesn’t work with css transitions – as the color would (were it supported).
    I suppose that’s a vote for the pseudo-element trick…. buuuut …

    that would require a wrap to size the pseudo overly.
    it could also interfere with text selection …

    something like this : http://codepen.io/anon/pen/CcyaD

  7. Awesome! If only linear-gradient could be used with transition….

    • What propose are you thinking of?

    • In some situations, there is a way that linear-gradients can transition… well sort of. It won’t work with an image underneath, mind you, but here’s a pen which illustrates one way to achieve the effect.

  8. Permalink to comment#

    this is actually cooler than i thought because you can have a gradient tint, instead of just a solid color. yeeeaaah!

    my only question is and im assuming, this can be used with transitions???

    • Because linear gradients are essentially generated images, they won’t transition. You may be able to achieve some kind of an effect with background position, though.

  9. Nicklas
    Permalink to comment#

    This can make some pretty cool effects:

    http://codepen.io/vobpler/full/aljwd

    • Permalink to comment#

      Quite cool actually, that is what i had in mind. now just have to figure out if the transitions can be done on them.

      this is fucking awesome yo!

    • Nicklas
      Permalink to comment#

      I tried to add a transition-duration to it but it didn’t seem to work. I don’t think there’s support for transitioning background images, at least not in Chrome.

    • Benjamin Boxler
      Permalink to comment#

      Unfortunately gradients can’t be transitioned. :(

    • Unfortunately gradients can’t be transitioned. :(

      You can transition background-position though to morph one gradient into another (visually): http://codepen.io/chriscoyier/pen/JgrhE

  10. @Beecher, @Jon Wiedmann and @Farax:

    Although you can’t transition a gradient’s colours you can transition its background-position so, if you make the gradient really, really long (say 2000% high) you fake the colour transition effect by scrolling through the gradient instead.

    Here’s a demo of the effect: http://codepen.io/keithclark/pen/trFch

  11. philtune
    Permalink to comment#

    Hey Chris, I see some others have mentioned it, but my immediate solution would be using pseudo-elements. Since my team has to support IE8, I always shy away from multiple backgrounds (necessitating fallbacks) anyways. I would just wrap an img tag in a classed span or anchor and set the pseudo-class’s z-index.

    I’ve used this concept to create interesting drop-shadows before: make a pseudo-element with a regular box-shadow and throw a transform rotation on there, set the child element (i.e. ) to a higher z-index and you have a gracefully degrading angled dropshadow. (IE8 just won’t show the dropshadow)

  12. Hi Chris,

    Nice tip as usual, just pointed that in your CSS code there is a little mistake on the opacity

    0,45)

    Should be 0.45)

    Perfect on Codepen

    Best regards !!!

  13. Jesse
    Permalink to comment#

    I can imagine the gradient layer being useful for an image gallery, perhaps removed on hover to show focus.

  14. Harry
    Permalink to comment#

    Doesn’t seem to work on chrome on iOS. Haven’t tested in Safari though

  15. Miky
    Permalink to comment#

    But it doesn’t works… – on Android

    (4.1.1 Chrome 18.0 neither in Native browser)

  16. Permalink to comment#

    Great trick. It avoids messing with the stacking order altogether, and as Jesse (above) points out, makes for a great filter with something like an image grid gallery.

  17. mmednik
    Permalink to comment#

    Doesn’t works in webkit (MacOs)

    • NotMyName
      Permalink to comment#

      It also doesn’t work in my room…
      (should we at least know webkit number?)

  18. Nice, Chris! I did something similar a while back, although my approach worked to “fade” the background image, rather than tint it. In theory, one could use a filter to create the same effect.

    It’s also possible to use a variation of the technique to create a “silk-screened” visual effect on images.

  19. sadunaresh
    Permalink to comment#

    Thank You Chris, I was using this pseudo-element trick till yesterday…. this trick really helped me…

  20. Kevin
    Permalink to comment#

    This may be an overloaded question, not quite appropriate here. Anyone know how to get it working with SASS?

    When I put the comma’d attributes into my sass file, it chews it up and spits it out invalid.

    This is what goes into my sass file

    header.featured-img {
      background: 
      /* top, transparent color */ 
      linear-gradient(
        rgba(240, 237, 237, 0.8), 
        rgba(240, 237, 237, 0.8)
      ),
      /* bottom, image */
      url('/* @path featured.jpg */');
    }
    

    And this is what comes out in Chrome. Firefox just doesn’t display it at all, which I’m guessing cause it’s invalid and removes it.

    header.featured-img {
    background: linear, false,
    rgba(240, 237, 237, 0.8),
    rgba(240, 237, 237, 0.8),
    url(../assets/images/featured.jpg);
    }
    

    I’m very new to using SASS so maybe I’m just not able to do this and need to just add this to my CSS file. Anyone know? Thanks.

    • Kevin
      Permalink to comment#

      Also, forgot to mention that I’m using Hammer for my SASS compiling. Not sure if that makes a difference.

      No worries if this question is too complex for posting here. I just thought I’d ask anyway.

    • You’re just trying to get a gradient compiled, right?

      The CSS gradient generator has an option to output to SCSS if that helps.

    • Kevin
      Permalink to comment#

      Ah, yes that looks like it will fix my problem. I didn’t realize there was a sass specific method for doing gradients. Thank you.

  21. Wicked Cool.. This is just amazing. I will use it my new projects ahead. Thanx

    http://www.thesoftwareguy.in

  22. I happened to use this in a project a while ago if anyone wants to see a use of this.

    Includes a nifty transition to full color

  23. The same effect using box-shadow property:

    .test {
      width: 300px;
      height: 200px;
      box-shadow: 0px 200px rgba(235, 13, 13, 0.4) inset;
      background: url(http://s3-us-west-2.amazonaws.com/s.cdpn.io/3/owl1.jpg);
      } 
    

    See the Pen Example by Maxim Aginsky (@maximaginsky) on CodePen

  24. Llewellyn Collins
    Permalink to comment#

    Nice work around. however this doesn’t seem to be working in the demo. At least not for me.

  25. Mike
    Permalink to comment#

    Extremely Cool. Thanks

  26. Paul Stewart
    Permalink to comment#

    Really cool! Just a shame not all browsers can work in harmony doing the same.

  27. The alternative way to reach the effect is to use box-shadow property

    .test {
      width: 300px;
      height: 200px;
      box-shadow: 0px 200px rgba(235, 13, 13, 0.4) inset;
      background: url(http://s3-us-west-2.amazonaws.com/s.cdpn.io/3/owl1.jpg);
      }
    

    Example on CodePan

  28. Lee
    Permalink to comment#

    Alas, Safari requires the prefix…

        background:
           -webkit-linear-gradient(
           rgba(255, 0, 0, 0.45), 
           rgba(255, 0, 0, 0.45)
           ),
           url(../images/_test.jpg) 50% 0 no-repeat;
    
  29. Permalink to comment#

    As for me, I use the multiple background method and I don’t normally use RGBA, I create a PNG. Since I saw this post, I think I’ll be using this instead because it’s much easier. Thanks!

  30. stan
    Permalink to comment#

    I need to identify images without alt tags for my editors so i came up with this technique that is independent of the image..

    if (!$(this).attr('alt') ){$(this).css('opacity','0.4').wrap('<div Style="background:red; width:'+$(this).width()+'px;color:#fff;font-weight:bold ">NO ALT TAG!!</div>');}

    i just wrap the image with a Div, adjust the width to that of the image and add a color background and make the image a little transparent

    this could easily be assigned to a class

  31. Alexis
    Permalink to comment#

    Thanks nice work…

  32. Macy
    Permalink to comment#

    I’m trying to use this for my header image but for some reason when I’m using bourbon it won’t work. Once I comment out all of bourbon, neat, and bitters the picture shows up and the gradient works.

    <

    p>Why? D: D:

  33. Tom

    I have been looking for this for hours, it seemed so simple and I couldn’t figure it out. Thanks so much!

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".