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-image
s 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?
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.
This works in most new browsers, but what about IE?
Also, how about this? http://codepen.io/NSDCars5/pen/nuzbI
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.
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
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)….
@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
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
@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%.
@Andrew
I created a new codepen.
http://codepen.io/anon/pen/EClfq
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.
The reason your original syntax doesn’t work is, you’re specifying a
background-color
and abackground-image
and images will always stack on top of the colour. By defining alinear-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.
I don’t think this is accurate:
It’s not the fact that the background is an image or a color, it’s the stacking order. But yep, the gradient trick is exactly what I’ve presented here.
Quite true: “The background color, if present, is painted below all of the other layers.” (w3c).
I wonder how the gradient method compares to generating semi-transparent tinted data-embedded pngs with the framework of choice.
Reading through the Backgrounds and Borders Module it looks like a
background-color
is only valid if it’s the last property defined in the shorthand declaration. So it can only ever exist as the bottom “layer” – I guess this is to keep the syntax inline with CSS2, which states “When the image is available, it is rendered on top of the background color”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;”.
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
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.
http://codepen.io/garypaul/pen/iDcEx
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.
This can make some pretty cool effects:
http://codepen.io/vobpler/full/aljwd
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!
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.
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
@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
thanks, that would do for now i guess.
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)
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 !!!
Thanks Cristian! Fixed. Burying cause no long relevant.
I can imagine the gradient layer being useful for an image gallery, perhaps removed on hover to show focus.
Doesn’t seem to work on chrome on iOS. Haven’t tested in Safari though
But it doesn’t works… – on Android
(4.1.1 Chrome 18.0 neither in Native browser)
BYO prefixes.
I just enabled -prefix-free so perhaps that will help the demo.
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.
Doesn’t works in webkit (MacOs)
It also doesn’t work in my room…
(should we at least know webkit number?)
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.
Thank You Chris, I was using this pseudo-element trick till yesterday…. this trick really helped me…
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
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.
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.
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.
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.
Wicked Cool.. This is just amazing. I will use it my new projects ahead. Thanx
http://www.thesoftwareguy.in
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
The same effect using box-shadow property:
See the Pen Example by Maxim Aginsky (@maximaginsky) on CodePen
Nice work around. however this doesn’t seem to be working in the demo. At least not for me.
Extremely Cool. Thanks
Really cool! Just a shame not all browsers can work in harmony doing the same.
The alternative way to reach the effect is to use box-shadow property
Example on CodePan
Alas, Safari requires the prefix…
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!
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
Thanks nice work…
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:
I have been looking for this for hours, it seemed so simple and I couldn’t figure it out. Thanks so much!