When Firefox 4 is released this October, February? browsers that support the CSS3 transition property will make up approximately 30% of the market (providing current Firefox users upgrade). With this in mind, it now becomes viable to consider CSS as an alternative to using JavaScript to achieve fading :hover effects on buttons and other interface elements.
Why CSS?
A popular method for fading background images is the ‘Pufferfish’ technique which uses JQuery to append an additional <div> or <span> element to the HTML and animate the fade on :hover using the opacity property. The main issue with this, apart from the reliance on non-semantic HTML, is that the opacity property is inherited by any children of the element that it is applied to. In other words, if you make a button fade on :hover, then any web text that it contains also vanishes.
Here is an example of this problem.
Dragon Interactive, authors of Pufferfish, got around this by using images for text, but this isn’t ideal as it makes pages harder to update, rules out fun with @font-face, and is not so good for accessibility.
I had a go at solving this a couple of months ago, and sort of gave up as the only solution seemed to involve absolute positioning using buttons of fixed sizes, which isn’t suitable for most layouts. I also couldn’t get Internet Explorer to work with it at all (IE Issues now resolved).
Below is where I’ve got to so far using CSS3 transitions to achieve the same result. I’m much happier with this because it involves no extra HTML and just a few lines of CSS. It’s a work in progress, and still has some caveats, but it seems like a more elegant solution.
Why Background Images?
Recently there has been a bit of discussion as to how CSS3 could reduce or even eliminate the need for images in web page designs. This is due to new properties that enable things such as rounded corners, drop shadows and gradients to be created procedurally with CSS rather than in an image editor. These methods can go a long way but have their limits if working with a design that requires any sort of texture or complex shading. If this is the case, then background images must be used.
Fading between two background images is problematic however. Both JavaScript and CSS3 animations rely on changing numerical values incrementally over time. This works well if you want to move or rotate an element using XY coordinates, or even change a colour using RGB or HSL, but the value for the background-image property is a text string, a URL, so you can’t animate it. opacity has a numeric value, but then you have the issue of web text vanishing.
I looked into inserting additional <span> elements but this doesn’t work so well with CSS as it does with JavaScript, because the :hover pseudo class can not be made to target elements other than those it has selected. To cut a long explanation short, the button text covered up the <span> and prevented it from fading.
Therefore my solution to date codges together a bit of CSS3 transitions with another CSS3 property, rgba, which allows us to animate an element’s opacity without affecting contained elements, but only works on solid colours.
Demo (best viewed in FF4, Chrome or Safari)
Compatibility
- Firefox 4+, Chrome 4+, Safari 4+ – full support
- Firefox 3, Chrome 1-3, Safari 3, Opera 9+*, Internet Explorer 9+ –
rgbasupport only - The rest – fallback
*Note: Opera’s current support for the CSS3 transitions module is partial, and doesn’t as yet extend to working with opacity. Info on Opera’s support for transitions is here.
Tutorial
1. First of all, create a background image in your preferred image editor. Below is the one used in this example:

You’ll notice that this is a sprite, and that it is fairly large to accommodate text resizing. Neither of these techniques are essential to this method, but are advisable for general site performance and accessibility.
2. Create a standard unordered list for the menu.
<ul id="nav"> <li><a>Home</a></li> <li><a>About</a></li> <li><a>Gallery</a></li> <li><a>Contact</a></li> </ul>
3. Style the list in the usual fashion, setting the background image for both the normal and :hover{} states.
#nav {
margin: 0;
padding: 0;
list-style-type: none;
}
#nav li {
display: block;
float: left;
margin: 0 5px;
}
#nav a {
display: block;
padding: 25px 35px;
font-family:Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
color: #fff;
text-decoration: none;
text-align:center;
background: url(../images/bg.jpg) no-repeat center -200px;
}
#nav a:hover {
background: url(../images/bg.jpg) no-repeat center top;
}
So far this should look identical in every browser:

4. Now we can add some CSS3 decorations. These will display in Firefox 3.5+, Chrome, Safari 4+ and Opera 10.5+ and Internet Explorer 9+. Older browsers will default to how the buttons look in step (3).
#nav {
margin: 0px;
padding: 0px;
list-style-type: none;
}
#nav li {
display: block;
float: left;
margin: 0 5px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: 1px 1px 1px #666;
-webkit-box-shadow: 1px 1px 1px #666;
-o-box-shadow: 1px 1px 1px #666;
box-shadow: 1px 1px 1px #666;
}
#nav a {
display: block;
padding: 25px 35px;
font-family:Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
color: #fff;
text-decoration: none;
text-shadow: 1px 1px 1px #666;
text-align:center;
background: url(../images/bg.jpg) no-repeat center -200px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 10px;
}
#nav a:hover {
background: url(../images/bg.jpg) no-repeat center top;
}

5. Finally, the code for the fading transition is added. How this works will be explained below:
#nav {
margin: 0px;
padding: 0px;
list-style-type: none;
}
#nav li {
display: block;
float: left;
margin: 0 5px;
background: url(../images/bg.jpg) no-repeat center top;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: 1px 1px 1px #666;
-webkit-box-shadow: 1px 1px 1px #666;
-o-box-shadow: 1px 1px 1px #666;
box-shadow: 1px 1px 1px #666;
}
#nav a {
display: block;
padding: 25px 35px;
font-family:Arial, sans-serif;
font-size: 1.2em;
font-weight: bold;
color: #fff;
text-decoration: none;
text-shadow: 1px 1px 1px #666;
text-align:center;
background: url(../images/bg.jpg) no-repeat center -200px;
background: rgba(80, 125, 200, 0.55);
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 10px;
-webkit-transition: background 0.5s linear;
-moz-transition: background 0.5s linear;
-o-transition: background 0.5s linear;
transition: background 0.5s linear;
}
#nav a:hover {
background: url(../images/bg.jpg) no-repeat center top;
background: rgba(100, 125, 175, 0);
}
Three things have happened here.
Firstly, on line 11 of the code above, an additional background property has been added to #nav li, giving it the same background image as #nav a:hover.
Secondly, additional background properties with rgba values have been set for both #nav a and #nav a:hover. rgba lets us set a solid bluey-grey colour for a background, and also specify an alpha transparency level, from 1 (fully opaque) to 0 (fully transparent).
In this example, the :hover rule set to 0, whereas #nav a is given the value of ’0.55′. This makes the bluey-grey colour semi-opaque and allows the textured image declared in #nav li to show through, appearing darker and greyer than normal.
The difference between these two opacity values can now be animated, giving a fading image effect.
Apart from not being inherited by child elements, the rgba property is useful because if the browser recognises it, the solid colour will be used in place of the background image originally declared in that rule. Yet if the browser doesn’t support rgba, the background image remains intact, allowing you to create fallbacks for older browsers without the use of hacks, conditional comments or browser detection scripts.
Thirdly, the transition properties for each vendor are added on lines 39-42. The syntax in this case is pretty straightforward:
transition: background 0.5s linear;
‘background’ specifies the property that you want to animate.
’0.5s’ states how long you want the animation to last for, in seconds (more intuitive than JS’s milliseconds? – discuss…)
‘linear’ specifies the timing of the transition. It is possible to create ease-in and ease-out timings. The current state of the W3C spec regarding transition timing is here and a useful tutorial/demo by Richard Bradshaw can be found here. Linear is the bog-standard default option.
Here’s a link to a demo of the final result again:
Demo (best viewed in FF4, Chrome or Safari)
Conclusion
The result isn’t a true fade between two background images (the only way to achieve this cross-browser is using JS using this method), but it does give the impression of being so, and allows us to create flexible buttons with a bit of texture, without extra markup. What it can’t do is simulate a fade between two completely different images, oh well :(
The menu can be edited dynamically by simply altering the HTML, which means it can be incorporated into a CMS template, can be enlarged for disabled users and is screen reader and search engine friendly. It also reverts back to a non-fading version if browser support isn’t there, which at the moment will be about 70% of the time, although this will improve as adoption of the transition property increases.


August 6, 2010 at 4:09 pm
nice menu, so simple and clear in explanation
thanks ^^
Pingback: Filling Websites With Unique Content.
August 7, 2010 at 3:17 pm
Great effect and well explained. I like seconds.
August 8, 2010 at 4:25 pm
@Maicon, yes me too :)
Pingback: iWeb Master Class Part 1.2 :Apple On The Longtail
Pingback: Effetto fade sui pulsanti con i css3!
Pingback: Weekly Design News – Resources, Tutorials and Freebies (N.49) :Speckyboy Design Magazine
August 21, 2010 at 9:57 pm
Hi, really appreciate this.. i am wondering can i use this to fade an image in when hovering over ? i work with vBulletin 4 styles and the forum-rows lately ive been using a different color when hovering over.. but would be nice if i could fade an image in on mouse over.
Thanks, bookmarked and will check back soon for help on this, Cheers
August 13, 2010 at 1:13 pm
@ Xbox Kinect. Yes it should be possible. Use the same syntax, but select your forum row element instead of what is outlined in this tutorial. The method does rely on there being one element nested within another (in this case an anchor within a list item) If your forum row doesn’t have that, then you may have to add in an extra or to your markup, so that you can have the image hidden behind a colour.
August 28, 2010 at 8:19 am
Thanks for u r information
its very useful
Pingback: 50 Refreshing CSS Tutorials, Techniques and Resources :Speckyboy Design Magazine
September 27, 2010 at 12:42 pm
Thanks for the nice information. I am sure, I will tweet this to my twitter account. This will help a lot of users.
Pingback: August’s Best Resources for CSS3
Pingback: 60 Awesome Tutorials, Tricks, and Tools that will Make You a CSS Master | Tech King
September 28, 2010 at 7:57 am
another awesome tutorial. it was so nice to read and easy to follow.
October 12, 2010 at 1:14 pm
Great effect and well explained. I like seconds.
October 19, 2010 at 2:14 pm
thanks some great info
October 20, 2010 at 11:50 am
Thanks for the info
November 16, 2010 at 1:42 am
Thanks for the post. I like new properties, I will implement for Mac users, hopefuly Firefox upgrade.
December 1, 2010 at 1:08 pm
Thank you very much for the post.
January 21, 2011 at 2:30 pm
Nice reference before designing background. A good BG needs lots of thinking and work.
January 27, 2011 at 2:55 pm
I keep checking the website for new tuts but this one has been very interesting and knowledge full . GREAT guys keep it up
January 27, 2011 at 11:50 pm
Glad you found it useful. Have been busy of late but some new posts coming up soon, I promise!
March 3, 2011 at 6:02 am
Hey James,
I was able to accomplish a little more of an actual fade with the images. If you were to nest elements inside of the anchor tag, you can fade the opacity in or out on those sub items base on the hover state of your parent.
Putting my text inside a second element inside of the anchor tag (the strong tag) is required so that my background image isn’t fading in over my text. The span tag is placed absolutely inside the anchor tag, and has its width set to 100%, so it will match width wise with whatever you do with the strong tag. The only trick is the height. You could hard code that, but I use a nbsp and match the font size and padding of my strong.
I know its a lot of work, but the effect is beautiful.
http://screencast.com/t/ZU2AxY6fgw
.myButton { display: inline-block; *display: inline; *zoom:1; } .myButton.myStyle_TransparentHover { position: relative; color: #4C4C4C; } .myButton.myStyle_TransparentHover span { border: 1px solid #A7C3CC; color: rgba(0,0,0,0); display: inline-block; *display: inline; *zoom:1; background: transparent url(/i/ui/button/transparent-gradient.png) left top repeat-x; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; padding: 5px 0; -webkit-background-clip: padding-box; position: absolute; top: 0; left: 0; opacity: 0; width: 100%; padding: 5px 0; -moz-transition: opacity .25s linear; -webkit-transition: opacity .25s linear; -o-transition: opacity .25s linear; transition: opacity .25s linear; font-weight: bold; line-height: 1; } html.ie .myButton.myStyle_TransparentHover span { display: none; } .myButton.myStyle_TransparentHover strong { display: inline-block; *display: inline; *zoom:1; padding: 5px 15px; font-weight: bold; position: relative; line-height: 1; } .myButton.myStyle_TransparentHover:hover span { opacity: 1; } html.ie .myButton.myStyle_TransparentHover:hover span { display: inline-block; *display: inline; } .myButton.myStyle_TransparentHover_Arrow span { padding-right: 4px } .myButton.myStyle_TransparentHover_Arrow strong { background: url(/i/ui/list/drop-down/down-arrow.png) right center no-repeat; padding-right: 14px }There’s also some extra code to handle hiding and showing for IE without the transition.
March 3, 2011 at 8:49 am
With some javascript to simulate menu behavior (mostly adding and removing classes on focus, blur, and clicks):
http://screencast.com/t/3hsWv2ncU
You can create some beautiful interfaces with css3 and javascript.
April 17, 2011 at 10:56 pm
Thanks for the tip on using linear, so use to using ease-in-out I forgot to change the transition type.
May 20, 2011 at 8:12 am
Nice work. I appreciate to your work.
July 8, 2011 at 1:04 pm
very nice work..
I am just newbie to web design field….this kind of tutorials help me lot to learn..
Thank you
August 10, 2011 at 8:10 am
Thanks for sharing with us.
September 30, 2011 at 8:00 pm
It´s very usefull, but, I was looking for something that I think doesn´t exists in CSS3:
the same effect, but with differents images:
I tried to add an id to the li element, but it doesnt move at all…
Anyway, CSS3 is cool!
October 1, 2011 at 12:31 am
You can do it, but it currently only works in Firefox: Link
Or you can try JQuery Link
October 1, 2011 at 4:48 pm
Is there a way to do this with a transparent first sprite and a colored second half. The effect would be to mouse over a link and have a background appear and then disappear when the mouse is removed.
October 2, 2011 at 11:04 pm
Not using this technique with a background image.
If you just wanted a solid colour to fade in on hover you can use RGBa and transition between alpha values. Doesn’t work in IE yet.
November 4, 2011 at 4:23 pm
Very useful information there! I just thought of to became your regular reader but the problem is your sweet blog does not seem friendly when it show in KMeleon browser which I use regularly other than the old school IE 6, hope you can get it fix soon, thanks in advance.
December 1, 2011 at 12:18 am
This great, exactly what I was looking for. Just one problem, the buttons turn white after rolling over them in IE9.
IE7/IE8 both degrade nicely, just IE9 in IE9 Standards mode glitches.
December 1, 2011 at 12:21 am
I don’t have time at the moment to try, but I can imagine using keyframes would solve this (given IE10 will display properly, since it will support keyframes).
December 6, 2011 at 8:59 am
I feel like a dummy.. but inspecting the demo, i only see one part of the image is used. I don’t see the reasen u use a sprite.. lost a lot of time with this
December 7, 2011 at 1:36 am
Hi Dave,
The second part of the sprite image is used as a fallback for browsers that don’t support CSS transitions. This isn’t ideal since modern browsers will be loading unnecessary data. Feel free to tidy this up.
January 9, 2012 at 5:51 am
Great, My Problem Solved
January 20, 2012 at 1:37 pm
I’m a beginner on this whole thing of HTML and CSS, my website isn’t even validated by w3c, YET, but I’m working on that..
But I don’t know what I’m missing, I just can’t get the fading effect, it just keeps on that sliding transition…
If you could see what I’m doing wrong, I’d be very thankful.
Here’s the css:
#botao1 {
float:left;
margin:0;
}
#botao1 a {
background:url(images/botao1.gif) no-repeat;
display:block;
height:125px;
width:135px;
border-radius:8px;
transition: background .25s linear;
-moz-transition: background .25s linear;
-webkit-transition: background .25s linear;
-o-transition: background .25s linear;
}
#botao1 a:hover {
background-position:-136px 0px;
border-radius:8px;
}
January 24, 2012 at 12:43 pm
There is a sliding effect because you are changing the position of the background image
background-position:-136pxon hover.You can’t (currently) transition the opacity of a background image with CSS, which is why the technique detailed in this post uses solid colors and RGBa to fake it.
January 25, 2012 at 7:06 pm
ah.. I see, now I got it.
I didn’t notice it was a single image and an overlay color, actually I was wondering what was all that “aRGB” about! xD
Thank u so much, saved me from many future headaches hahaha
By the way, it’s a great tutorial and I’ll try to use it on some other way! :D
thanks again!