56

I have a div with a background-image. I want to overlay the background-image with an rgba color (rgba(0,0,0,0.1)) when the user hovers the div.

I was wondering if there's a one-div solution (i.e. not with multiple divs, one for the image and one for the color, etc.).

I tried multiple things:

<div class="the-div" id="test-1"></div>
<div class="the-div" id="test-2"></div>
<div class="the-div" id="test-3"></div>

And this CSS:

.the-div {
    background-image: url('the-image');
    margin: 10px;
    width: 200px;
    height: 80px;
}

#test-1:hover {
    background-color: rgba(0,0,0,0.1);
}

#test-2:hover {
    background: url('the-image'), rgba(0,0,0,0.1);
}

#test-3:hover {
    background: rgba(0,0,0,0.1);
}

See this fiddle.

The only option I saw is to make another image, with overlay, preload it using JavaScript and then use .the-div:hover { background: url('the-new-image'); }. However, I'd like a CSS-only solution (neater; less HTTP requests; less harddisk). Is there any?

5 Answers5

53

The solution by PeterVR has the disadvantage that the additional color displays on top of the entire HTML block - meaning that it also shows up on top of div content, not just on top of the background image. This is fine if your div is empty, but if it is not using a linear gradient might be a better solution:

<div class="the-div">Red text</div>

<style type="text/css">
  .the-div
  {
    background-image: url("the-image.png");
    color: #f00;
    margin: 10px;
    width: 200px;
    height: 80px;
  }
  .the-div:hover
  {
    background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("the-image.png");
    background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("the-image.png");
    background-image: -o-linear-gradient(top, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("the-image.png");
    background-image: -ms-linear-gradient(top, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("the-image.png");
    background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.1)), to(rgba(0, 0, 0, 0.1))), url("the-image.png");
    background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.1)), url("the-image.png");
  }
</style>

See fiddle. Too bad that gradient specifications are currently a mess. See compatibility table, the code above should work in any browser with a noteworthy market share - with the exception of MSIE 9.0 and older.

Edit (March 2017): The state of the web got far less messy by now. So the linear-gradient (supported by Firefox and Internet Explorer) and -webkit-linear-gradient (supported by Chrome, Opera and Safari) lines are sufficient, additional prefixed versions are no longer necessary.

Wladimir Palant
  • 56,865
  • 12
  • 98
  • 126
  • You can optimize this slightly by using a base64 encoded 1px by 1px image in the color you want. Just use an encoded png and you can get the transparency and everything. The downside to base64 images is poor readability and quick changes aren't easy. You do avoid having to have 5 extra lines for all of the gradient specifications though. – RedEight May 03 '16 at 17:21
  • @RedEight: The `-moz` and `-ms` versions should no longer be necessary, same goes for the legacy WebKit notation. And I guess that `-o` isn't necessary either, newer Opera versions being based on WebKit. So you really only need two lines. – Wladimir Palant May 03 '16 at 18:51
  • @RedEight Thanks i was thinking the same until i found your answer and now i definitely know it's should be removed – GrowLoad Aug 08 '23 at 17:23
37

Yes, there is a way to do this. You could use a pseudo-element after to position a block on top of your background image. Something like this: http://jsfiddle.net/Pevara/N2U6B/

The css for the :after looks like this:

#the-div:hover:after {
    content: ' ';
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background-color: rgba(0,0,0,.5);
}

edit:
When you want to apply this to a non-empty element, and just get the overlay on the background, you can do so by applying a positive z-index to the element, and a negative one to the :after. Something like this:

#the-div {
    ...
    z-index: 1;
}
#the-div:hover:after {
    ...
    z-index: -1;
}

And the updated fiddle: http://jsfiddle.net/N2U6B/255/

potashin
  • 44,205
  • 11
  • 83
  • 107
Pevara
  • 14,242
  • 1
  • 34
  • 47
  • 5
    It's a nice trick but unfortunately this layer overlays the text as well, not just the background image. So this will only work for empty divs. – Wladimir Palant Dec 11 '13 at 11:11
  • 1
    @WladimirPalant it still works for non-empty elements, if you add a z-index. See the update. – Pevara Jan 24 '15 at 13:37
7

/* 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(https://upload.wikimedia.org/wikipedia/commons/7/73/Lion_waiting_in_Namibia.jpg);
    height: 1280px;
    width: 960px;
    background-size: cover;
}

.tinted-image p {
    color: #fff;
    padding: 100px;
  }
<div class="tinted-image">
  
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laboriosam distinctio, temporibus tempora a eveniet quas  qui veritatis sunt perferendis harum!</p>
  
</div>

source: https://css-tricks.com/tinted-images-multiple-backgrounds/

pgrono
  • 740
  • 7
  • 5
3

Ideally the background property would allow us to layer various backgrounds similar to the background image layering detailed at http://www.css3.info/preview/multiple-backgrounds/. Unfortunately, at least in Chrome (40.0.2214.115), adding an rgba background alongside a url() image background seems to break the property.

The solution I've found is to render the rgba layer as a 1px*1px Base64 encoded image and inline it.

.the-div:hover {
  background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgkAQAABwAGkn5GOoAAAAASUVORK5CYII=), url("the-image.png");
}

for base64 encoded 1*1 pixel images I used http://px64.net/

Here is your jsfiddle with these changes made. http://jsfiddle.net/325Ft/49/ (I also swapped the image to one that still exists on the internet)

RedEight
  • 121
  • 1
  • 5
  • A little jem of an answer you got down here. I ended up using this today because it works in IE9 too. Thanks man. – igneosaur Mar 10 '17 at 16:26
3

I've gotten the following to work:

html {
  background:
      linear-gradient(rgba(0,184,255,0.45),rgba(0,184,255,0.45)),
      url('bgimage.jpg') no-repeat center center fixed;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
}

The above will produce a nice opaque blue overlay.

guero64
  • 1,019
  • 1
  • 12
  • 18