CodeinWP CodeinWP

Image Tint With CSS

Update: (May 7, 2013) There are now two specifications that make this sort of thing possible with CSS: Filters and Compositing and Blending.

Image Tint With CSSThe other day Paul Irish posted an article that collected together responses to a question that he and Yehuda Katz asked their Twitter followers.

One of the wish list items a few people mentioned was “CSS blend modes” with a use case of tinting an image on hover (or tinting it statically, then removing the tint on hover or by some other interaction). My immediate thought was: That should be simple enough, shouldn’t it?

But it’s not so simple. This particular feature request is more along the lines of matching the behaviour of some, or all, the blending modes available in traditional image editors like Photoshop. These might include dissolve, darken, multiply, overlay, and saturation.

In this post, I’ll offer a few solutions for mimicking a CSS image tint or semi-transparent color overlay. Each of these solutions has the drawback of requiring either an extra wrapper element or JavaScript.

Method 1: Pseudo-Element

The list of things you can accomplish with pseudo-elements seems endless and they help with a pure CSS example of an image tint.

Here’s the markup:

<figure class="tint">
  <img src="image.jpg" alt="Example" width="400" height="260">
</figure>

And here’s the CSS:

.tint {
  position: relative;
  float: left;
  cursor: pointer;
}

.tint:before {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0,255,255, 0.5);
  transition: background .3s linear;
}

.tint:hover:before {
  background: none;
}

So what’s happening here? Well, first I have a wrapper element (I’m using the figure element but you could use whatever you want, even list items for multiple images). The <figure> element is positioned relatively to prepare the context for the pseudo-element.

Next I place a pseudo-element inside the wrapper element and position it absolutely. Although it appears “before” the image in the source order, it appears on top of the image due to it being absolutely positioned.

The trick to getting the pseudo-element (which has the tinted background) to fill the whole image is to use a little-known positioning trick. You can actually define the dimensions of an element based on top/bottom/left/right positioning. In this case, I’ve made all values zero, which means they will begin at the four edges of the element. Finally, I’ve added a transition and hover state on the pseudo-element.

<

p class=”codepen” data-height=”857″ data-theme-id=”461″ data-default-tab=”result” data-user=”impressivewebs” data-slug-hash=”jONRERJ” style=”height: 857px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;” data-pen-title=”CSS Image Tint with Pseudo-Element”>
See the Pen
CSS Image Tint with Pseudo-Element
by Louis Lazaris (@impressivewebs)
on CodePen.

As you can see in the demo, the tint appears on each image by default and if you roll over an image the tint goes away.

Method 2: Pseudo-Element and JavaScript

This next method might appease the semantic purists a little more, but it’s essentially the same as the solution above, except this time the tint happens on hover. The CSS is basically the same, but there is no extra wrapper element in the HTML. Instead, I’m adding the wrapper element around each image with some JavaScript:

window.onload = function () {
  let myImages = document.querySelectorAll('img');
  for (let i of myImages) {
    let prevImg = i.outerHTML;
    i.outerHTML = '<figure class="tint t' + Array.prototype.indexOf.call(i.parentElement.children, i) + '">' + prevImg + '</figure>'
  }
};

This uses JavaScript’s outerHTML() property to place a wrapper element around each of the images on the page. I’m targeting all the images on the page, but if you only want to target a specific image, then you’d have to adjust the selector.

The demo also adds classes to the images dynamically using the JavaScript.

<

p class=”codepen” data-height=”846″ data-theme-id=”461″ data-default-tab=”result” data-user=”impressivewebs” data-slug-hash=”xxKeGgq” style=”height: 846px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;” data-pen-title=”CSS Image Tint with JavaScript Wrapper Element”>
See the Pen
CSS Image Tint with JavaScript Wrapper Element
by Louis Lazaris (@impressivewebs)
on CodePen.

Method 3: Opacity Change

Finally, this method is very similar to what I’ve done above. It still requires a wrapper around each image. Either way, the CSS would look like this:

img {
  display: block;
  transition: opacity .3s linear;
}

img:hover {
  opacity: .5;
  cursor: pointer;
}

This time it’s pretty simple. Just give the image element a background color and on hover change the image’s opacity so the background color shows through, giving the appearance of an image tint.

<

p class=”codepen” data-height=”974″ data-theme-id=”461″ data-default-tab=”result” data-user=”impressivewebs” data-slug-hash=”bGbJdYy” style=”height: 974px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;” data-pen-title=”CSS Image Tint with Opacity Property”>
See the Pen
CSS Image Tint with Opacity Property
by Louis Lazaris (@impressivewebs)
on CodePen.

Can You Think of Another Way?

I’m sure I haven’t exhausted all the possibilities for doing this kind of effect. Can you think of another way to do an image tint effect with CSS?

46 Responses

  1. Ana says:

    If you have the image as a background image, it can be done with RGBA borders & box-sizing and there is no need for wrappers.
    Like this.
    Of course, some JavaScript will be needed for IE7-8.

    • Very nice! :)

      Edit: One quick note here. While this solution may be fine in some circumstances, it doesn’t really solve the problem I’m trying to address here (which I probably should have made more clear in the article). We want to be able to “tint” or otherwise change an image that actually appears in the source of the page with a regular image tag. Using a background image, the image is not accessible and if it’s mandatory content (and not just decoration) then it breaks the rule of keeping content images within the HTML.

      • Ana says:

        I now did it with img tags and I’m thinking about even more effects using gradients for border-image.

        The method that I’ve played with in the past was something like this. I used a div instead of a pseudo-element because I wanted to use transitions.

        • Yes, that’s much better.

          The style changes are really over the top, so not my favourite, but I like that you did it with regular images. I’ll have to examine that a little more when I get some time, because it’s interesting how you’re using a background image on the img element itself.

        • Anton says:

          Crazy cool effect!

          On topic, these are all just color overlays. I would use a canvas or svg overlay for a real tint.

  2. Beben Koben says:

    WOW used figure attribute on image tagging…
    ty ty ty for tut’s master ;)

  3. Carey says:

    There are some interesting things you can do with SVG filters, like http://jsfiddle.net/carey/eqFJf/, but I can only get that working in Firefox.

    Are you sure you can’t have a wrapper element like <a> on each image and use a solution like Ana’s or http://jsfiddle.net/carey/2m8kU/? Hover works better on links in older versions of IE, so my second example works in IE7.

  4. nonames says:

    jQuery:

    
    $(document).ready(function(){
    $("div.activer img").css("opacity","0");
    $("div.activer p").css("opacity","0");
    
    $("div.container , div.container2").hover(
    function() {
    $(this).find('div.animer , .animer img , .animer p').stop().animate({"opacity": "0" , "width": "306" , "height": "306"}, "slow");
    $(this).find('div.activer , .activer img , .activer p').stop().animate({"opacity": "1" , "width": "306" , "height": "306" }, "slow");
    },
    function() {
    
    $(this).find('div.animer , .animer img , .animer p').stop().animate({"opacity": "1" , "width": "206" , "height": "206"}, "slow");
    $(this).find('div.activer , .activer img , .activer p').stop().animate({"opacity": "0" , "width": "206" , "height": "206"}, "slow");
    });
     
    });
    

    no css3 required.

    Animates opacity, and scale of img.Was designed to perform a blending between 2 elements, blending in size and opacity.

    More propertys can be added and animated, just add them to the animate function, for starters, start with this:

    
    $(document).ready(function(){
    $("img.hidden").css("opacity","0");
    
    $("img.hidden , img.shown").hover(
    function() {
    $( "img.shown").stop().animate({"opacity": "0"}, "slow");
    $("img.hidden").stop().animate({"opacity": "1" }, "slow");
    },
    function() {
    
    $( "img.shown").stop().animate({"opacity": "1"}, "slow");
    $("img.hidden").stop().animate({"opacity": "0" }, "slow");
    });
     
    });
    

    Where .hidden and .shown are the class tag (img src=”bla.jpg” class=”shown” /) assignated to each image.

    the class hidden will define the image that is initially transparent, and that the hover (placing the mouse over it) will trigger if you place the cursor over 1 or another, it doesnt matter (because of this: $(“img.hidden , img.shown”).hover) while the class shown will vanish to transparent, the .stop elements make the animation flow in and out as you mous over and out the elements, it means that the animation doesn’t have to be completed each time that you hover in and out and it just switches in real time.

    btw, for placing 1 image over theother you canjust use position relative and force one intop of theother, that will give you a blending betweeen img1 and 2.

  5. Wesley Terry says:

    That is a great and simple solution using RGBA and psuedo elements. I think the simplest solution is always best, and if polyfils don’t work for the older browser support then it degrades fine. Well done!

  6. William says:

    Great solution, can you teach me how to display text inside the picture on hover? Thanks!

  7. pomeh says:

    This is nice, thanks :)

    There are CSS3 filters that are quite similar to this, but not exactly for the same purpose right now. It also you for example to apply sepia/blur/hue-rotate/contrast/etc effects to any element on the page. This is very recent feature but it looks very promising ! Here are some demos if you want to see it in action (latest Chrome build needed for now):
    http://html5-demos.appspot.com/static/css/filters/index.html
    http://girliemac.com/blog/2011/12/21/quick-fun-css3-filter-effects/

    Cheers

  8. Ana says:

    I’ve played a bit today with some variations of the third method (opacity change), but using either inset box shadows or background gradients on the containing element.

  9. shedesigns says:

    Hi – I really found this helpful – thanks! I’m hoping to use your first method but is there no way for the image to remain a link? It’s kind of frustrating to have a rollover that doesn’t link – or am i missing something?

    • If you need it to be a link, then I would suggest using method #2 or method #3. The first one has the pseudo-element overlaying it, so it won’t work as a link. It’s not supposed to change on hover; that’s just how I did it to make it clear.

  10. artym says:

    how i can add external link to tint image?
    link with this doesn’t work:
    HTML:

    
    <a href="anylink" rel="nofollow"></a>
    

    CSS:

    
    .untint1 { position: relative;cursor: pointer;}
    .untint1:before {
        margin: 0 auto;
        content: "";
        text-align:right;
        color:#ffffff;
        display: block;
        position: absolute;
        top: 0;
        bottom: 0;
        left:0;
        right: 0;
        background: rgba(30,30,30, 0.0);
    }
    
    .untint1:hover:before {
        background: rgba(30,30,30, 0.6);
        -moz-transition: all .5s linear;
        -webkit-transition: all .5s linear;
        -ms-transition: all .5s linear;
        -o-transition: all .5s linear;
        transition: all .5s linear;
        content:"anytext";
    }
    

    =((

    • It’s probably because the overlay pseudo-element is blocking the click area.

      I don’t think there’s a way around it. Unless maybe you place another element on top of the tinted image (with absolute positioning and z-index), and then use JavaScript or another link element to trigger the link. But that would be really messy, and probably not worth it. You’re better off doing the image tint in Photoshop and not even bothering with the pseudo-element or other CSS method.

  11. Roberto Jr. says:

    Here’s a good solution to people that want to link the images. I had that problem, so did that: http://jsfiddle.net/robertolux/nSUqV/ :DD

  12. CJ says:

    I needed to create a blue duotone effect (similar to sepia except blue).

    After some research and head-scratching I combined the grayscale filter (with SVG desaturate fallback) AND a transparent blue overlay (as described above).

    The result was spot on and more than satisfied both the designer and the client.

    Thanks for this post – it pointed me in the right direction.

    CJ

  13. Sam says:

    Nice !

    Could somebody explain me how to do that in WordPress though, I’m having difficulties doing it with WP TwentyTwelve ?

    • Sam says:

      Wait ! I did it, I was having trouble with placing the element (it had to be in content.php…!).
      Anyway, great tutorial, thanks !

  14. Adal says:

    Great article! For simplicity’s sake I added a slight shadow to my image with the following markup:

    HTML:

    <figure> <img /> </figure> 

    CSS:

    figure { display: inline-block; background-color: black; }
    figure > * { opacity: .95; } 
  15. Aaron says:

    Whole crap your code worked first time, I nearly passed out from the surprise!

  16. Perla says:

    HI, I have a fixed navigation and I did the images tints but when I go down on my page the images with the tint go over my navigation…. do you know why?

    • Yes, it’s probably because of creating a new formatting context. Which one of the above solutions did you use?

      The way to fix it is to use z-index, but I have to know which code you used so I can advise further.

  17. perla says:

    Hi Louis,

    Thanks for your help.

    I am doing an assignment, and I want to use the tint on my home page.

    My images have links, but in the moment I use the tint to the images the tint doesn’t respect my links… do you know why ?

    …..
    .tint1 {
    position: relative;
    float: left;
    width:286px;
    height:340px;
    cursor: pointer;
    }
    .tint1:before {
    content: “”;
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(117,73,142, 0.5);
    -moz-transition: background .3s linear;
    -webkit-transition: background .3s linear;
    -o-transition: background .3s linear;
    transition: background .3s linear;
    }
    .tint1:hover:before{
    background: none;

    }

    • Yes, it’s because the links have pseudo-element overlays. When you hover over the image, the pseudo-element (e.g. “:before”) blocks the link on the image. On my demo page, I’m using the CSS “cursor” property to make it look like they are links, but they’re not.

      To fix that, you’ll have to use Method 3 in my examples above, or maybe try Ana’s example.

  18. perla says:

    Hi Louis,

    Thanks for responding to my questions =), I tried the Method 3, and it is working, but I really like the other way when the tint appear firs because the picture with the tint looks better but I need the link… I will continue to try and see ..
    thanks

    • That’s no problem, you can make it appear first by putting the “opacity: 0.5” property on the “img” selector, rather than on the “img:hover” selector.

      Also, I noticed this method #3 doesn’t work correctly in Chrome anymore. Interesting.

      EDIT: That was strange, because for some reason I had a black background set on the image elements, which seemed to cause the problem. I’ve removed that and it’s better now in Chrome:

      http://jsbin.com/ruxak/1/edit?html,css,js,output

  19. csssssssssss says:

    is it possible to add a delay to the fadeout?

    • csssssssssss says:

      on method 2………………………………………………………

    • Yes, just do the following:

      .tint:before {
         /* other stuff here... */
          transition: all .3s linear 1s;
      }
      

      Notice I’ve added “1s”, which is the transition delay. Here is a demo.

      • csssssssssss says:

        Thx for the reply, this is not really what I mean and I try it too.
        I mean when mouse gets hover, it need 0.3sec as you shown in your example and than when mouse left the img, a delay happen for example 5s and than the transition start again for 0.3sec and make the hover hidden.

        Sorry for my bad english hope you understand what I mean.

        • csssssssssss says:

          I think it’s only possible with javascript, but I’m a tottaly newbie with JS.
          It would be nice if you can help me.

        • Yes, you can do that too:

          http://jsbin.com/xesuguhaco/1/edit

          Hover over the first image, then hover off. The way to do this is to have a different transition delay for hover on vs. hover off. I discuss this concept in this article:

          http://www.impressivewebs.com/subtleties-css3-transitions/

        • Just to be clear, here is the full code that’s relevant to this:

          
          .tint:before {
               /* other stuff here... */
              transition: all .3s linear 5s;
            }
            
          .tint:hover:before {
              background: rgba(0,255,255, 0.5);
              transition: all .3s linear;
          }
          

          Notice the static element has a transition set (this is the “off” and “on” state) and the “hover” class also has a transition, which is the “on” state, and that overrides the previous “on” state. It’s kind of hard to get at first, but you just need to know that you have to define each one in order to get a different delay for “on” vs. “off”.

  20. Logan says:

    Hi

    I created a row, with a background image.
    I would like to add the tint to the background image…but when I use the methods above, I end up with the tent behind the content.

    this is my html code:

    and this is the CSS code:

    *#dm *.dmBody section.u_1120461620 {
    background-color : rgba(255, 255, 255, 0.9) !important;
    background-image : url(https://irp-cdn.multiscreensite.com/7bd0d4ee/dms3rep/multi/VFConnected_Farmers_Fred_April2015_SalaLewis_Verve-16_RGB_HI-5760×3840.jpg) !important;
    background-position : center right !important;
    background-repeat : no-repeat !important;
    background-size : cover !important;
    }

    I just need a white overlay on the image so I can make the content more visible

  21. Niels says:

    Thanks for this. I’ve been looking for a way to apply a color overlay to an image tag. :)

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field and deep URLs are not allowed in the "Website" field. If you use keywords or deep URLs, your comment or URL will be removed. No foul language, please. Thank you for cooperating.

Markdown in use! Use `backticks` for inline code snippets and triple backticks at start and end for code blocks. You can also indent a code block four spaces. And no need to escape HTML, just type it correctly but make sure it's inside code delimeters (backticks or triple backticks).