Here’s what we are going to make in this tutorial:

The Base HTML
We will be using a simple unordered list1 within a <nav>
tag to create our buttons.
<nav>
<ul class="nav">
<li><a href="#" class="icon-home"></a></li>
<li><a href="#" class="icon-cog"></a></li>
<li><a href="#" class="icon-cw"></a></li>
<li><a href="#" class="icon-location"></a></li>
</ul>
</nav>
Some Resources
- These icons in the buttons come from the Fontello icon font and web app.
- The textured background is from Subtle Patterns.
CSS for the Icon Font
There are a few ways to go about this, but we’ll go with this:
@font-face {
font-family: 'fontello';
src: url('../fonts/fontello.eot');
src: url('../fonts/fontello.eot?#iefix') format('embedded-opentype'),
url('../fonts/fontello.woff') format('woff'),
url('../fonts/fontello.ttf') format('truetype'),
url('../fonts/fontello.svg#fontello') format('svg');
font-weight: normal; font-style: normal;
}
[class*=" icon-"] {
font-family: 'fontello';
font-style: normal;
font-size: 3em;
speak: none;
}
.icon-home:after { content: "\2302"; }
.icon-cog:after { content: "\2699"; }
.icon-cw:after { content: "\27f3"; }
.icon-location:after { content: "\e724"; }
CSS for the Buttons
The buttons will be arranged horizontally by making the list items inline-block
. We’ll make them butt up against each other with a little negative margin (or see these other techniques).
.nav {
list-style: none;
text-align: center;
}
.nav li {
position: relative;
display: inline-block;
margin-right: -4px;
}
The lovely three dimensionality of the buttons comes from a subtle gradient and bevel and embossing courtesy of a pair of box-shadow
s, one normal and one inset. The width and height of the buttons are identical creating a square, which turns to a circle with border-radius
. The line-height
matches the height and text-align
is set to center, so the icons will be both horizontally and vertically centered.
.nav a {
display: block;
background-color: #f7f7f7;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7);
background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7);
color: #a7a7a7;
margin: 36px;
width: 144px;
height: 144px;
position: relative;
text-align: center;
line-height: 144px;
border-radius: 50%;
box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;
}
We can flatten out the buttons a bit on :hover
to create a pressed look. You may want to save that for :active
and do something else for :hover
, but we’ll leave that up to you.
.nav a:hover{
text-decoration: none;
color: #555;
background: #f5f5f5;
}
At this point our buttons should look like this:

Not bad, but we have a ways to go.
Now on to the fun part, were going to be using pseudo elements to give us some additional elements to work with to create the rest of the effects. To create the horizontal “groove” you see between each button were going to add the pseudo selector :before
to our list items. Using negative z-index we can keep it beneath everything. The double border
give it the inset look. The negative margins we used on the list items earlier are what allow this groove to look continuous. We take advantage of absolute positioning to center the line.
.nav li:before {
content: "";
display: block;
border-top: 1px solid #ddd;
border-bottom: 1px solid #fff;
width: 100%;
height: 1px;
position: absolute;
top: 50%;
z-index: -1;
}
For that extra three-dimensionality, we’ll create a “well” around each anchor that the button sit inside. We can create this again with no additional HTML by using a pseudo element.
.nav a:before {
content: "";
display: block;
background: #fff;
border-top: 2px solid #ddd;
position: absolute;
top: -18px;
left: -18px;
bottom: -18px;
right: -18px;
z-index: -1;
border-radius: 50%;
box-shadow: inset 0px 8px 48px #ddd;
}
The size of the well (a bit larger than the button itself) comes from absolutely positioning itself behind the button and stretching itself out by using negative positioning values (top/bottom/left/right). The 3D look this time comes from a top border
and inset box-shadow
creating an internal shadow.
And we’re done!
That wasn’t so bad was it?

Browser Support
These buttons use a good bit of CSS3 and some pseudo elements which are CSS 2.1. Nothing too overly progressive though. Any version in the last few years of Safari, Chrome, Opera, or Firefox will have no trouble. IE 8 doesn’t do CSS3, so it falls back to this:

IE 7 is similar only without the “groove” line as it doesn’t do pseudo elements. Also not a big deal.
1 Unordered lists may not actually be ideal for navigation. We still used them here because we needed the extra element.
Very smart and beautiful buttons!
I’ve been making 3D buttons like that for a while, but that well just makes it that much better!
Totally dig it…. how would we go about and make these responsive? Can we go percentage sizes? Or is it not even worth it… I ‘hear’ a fiddle coming on….
Smooth… but I just wanna press them! :)
I wonder why the
active
state is missing, I think it would be a great addition.Fontello is a great piece of open-source software. :)
Re: Fontello. Wish I could get access to the their website. Not loading right now (June 13/9am): fontello.com. Just glad I’m not a time sensitive project right about now.
Nice, but not accessible !
Not for crawlers, nor for users who have deactivated CSS,
who use a text browser or a screen reader.
Agreed! Empty a tags are horrible for usability and SEO. Put link text inside a span which has something like the HTML5 boilerplate’s visuallyhidden class to maintain both accessibility and visual appeal.
Well, you can still insert title for “a” element ro nomral text between and and just set text-indent: -9999px or whatever. I see no problem for seo. This buttons are as good as buttons with images.
These are great! But they are SORELY missing the Active state!!!!
In my IE8 not only does the effect not come through, the glyphs are different!
Bill,
Chris has already mentioned in his article that the browser support doesn’t work in IE8, however there are ways around it, just use a separate stylesheet for anything under IE9. Honestly you should really be upgrading to IE9 anyway, or just using a better browser full stop, like: safari or chrome.
Rob.
Honestly you should really be upgrading to IE9 anyway, or just using a better browser full stop, like: safari or chrome.
Come on, did you stop to think that perhaps Bill is already using a perfectly capable browser? He is, after all on this website to begin with. I would think it safe to assume he might be keeping his clients/customers in mind when testing older browsers.
It reminds me of an incident that happened last weekend at the FrontEnd design conference in St. Pete; a speaker who specialized more in “bleeding edge” browser usage was asked what he suggested to devs/designers who needed to support older browsers. His answer was to suggest that those users upgrade. Not very practical for an e-commerce site (where I want them to convert and could care less what browser they use), or most other websites that want to cater to a large audience. Sadly, some folks seem to forget we are building this “web” for more then just designers/devs, we also have to keep others in mind who might not spend 8+ hours a day behind a computer.
Tommy,
I see where you are coming from and the point you are making is a good one. Maybe I worded what I was saying wrong, I wasn’t implying that we should bodge our work and hope people just use better browsers. I think that every site needs to be able to work in all browsers, hence why I suggest a separate stylesheet for users in a lower generation of IE.
The little comment at the bottom of my point was not to say that developers shouldn’t take into account the problems we face with browser support, it was just a suggestion to Bill personally. He probably already does have a perfectly capable browser and it was probably foolish to say he needed to upgrade; however my intention wasn’t to degrade the importance of browser support.
Yea, I was expecting some awesome
:active
state.Was thinking the same thing—Unleash the awesome!!!
Are you sure about that IE screenshot? The demo buttons don’t seem to render at all in IE7 (testing in Adobe Browser Lab and Skytap virtual machine).
@Scott
It’s normal, IE7 doesn’t support :before and :after pseudo-elements.
I’ve too been using this button style for quite a while, I’d previously been serving them as images from the original Photoshop designs…
Great post as always Chris. Beautiful work.
Just re-read the original post, guess I should re-credit! Great work Brandon :)
I like this, very simple. But for better accessibility with screen readers, wouldn’t it be better to have the text for the navigation there, with a text-indent:-9999? :)
Will the this work in IE with <a href=”http://selectivizr.com”> selectivizr</a>?
You might enjoy some of these eye-candy code samples:
https://code.google.com/p/css3-graphics/
https://code.google.com/p/css-shaders-graphics/
https://code.google.com/p/jquery-css3-graphics/
Great job on this, Brandon.
Here’s a fiddle with a simple :active state, which some are asking for:
http://jsfiddle.net/kTnMJ/
And from my testing, the icons work fine in IE8 and the buttons are square, as the screenshot in the article shows, but IE7 doesn’t work. I think it’s because IE7 doesn’t correctly support inline-block (but I really didn’t investigate it much).
Nice, thanks Louis!
I took this, moved it over to CodePen and addressed the issues of accessibility people were mentioning.
http://codepen.io/chriscoyier/pen/46/3
ala https://css-tricks.com/html-for-icon-font-usage/
I think just adding a border to the button when it is in active state does not add much to the interface, in my opinion when the element receives shadow, the look that suits the active state is to apply the same shadow from the normal state, but inverted.
http://codepen.io/JCMais/pen/1/4
@chris:
Couldn’t help but laugh at the fact that you starred your own comment. :)
@jonathan:
I’m not convinced that the shadow should be inverted like that. It looks okay, but it almost looks like the button is being pushed too far down. I think I prefer mine, but maybe a compromise between the two (with not such a big shadow on active) would work best.
Yahhh I normally don’t but I extra-wanted people to see it because it addresses an accessibility issue that lots of people brought up.
I also think it should have some kind of effect on hover – perhaps it can go into a “pressed down” state.
They look great, and they look good on IE9 too, except for the gradient which is not a big deal. They do not look good on older IE versions, but who cares?
DUDE! These are niiiice… Going to see if I can work on the reverse for black buttons! SWAG!
Wow, very impressive, definitely worth using on future sites. The only problem I see is that some of the icons seem to be a little out of place, but that’s something easily fixed. Thanks for sharing.
Very nice, even if IE7 doesn’t support :before and :after pseudo-elements. Besides, IE7 is just about dead according to the w3schools.com… http://www.w3schools.com/browsers/browsers_explorer.asp
Try telling real world clients that IE7 is dead. Why would you assume that your client base has a similar browser profile to that of W3schools visitors?
If you really need IE 7 support just don’t use :before, put the icon in the markup. Or use some JavaScript (not ideal, IE 7 is already slow) to read the data attribute and put it in the markup.
I understand how pseudo elements work, but I’ve not used ’em very much. This is a great demonstration for that. What I would like a tad more explanation about is what the construct and effect of this element is exactly:
Thanks! :-)
I believe that is a CSS3 Attribute Selector used to identify any class “containing” the prefix/attribute ” icon-“.
That confirms my assumption. I mean, that’s what I thought it was doing based on the classes that followed. I love stuff like this that helps reduce coding. I just gotta remember and get used to implementing it. :-)
I use this a bit for namespacing components in my css rather than having multiple classes e.g. “icon icon-home”..The “icon-” example you already see here but also for items such as “list-“, “btn-” or “pod-“. etc.
I set my base styles in the [class*=”btn-“] declaration and then write my overrides or extensions in the subsequent declarations e.g. “btn-primary” .
There are some considerations with this though. You have to make sure no other classes contain the keyword you use the “*=” selector on. Also the “*=” selector is probably one of the least performant of the CSS selectors. But in fairness if you are worried about performance you are probably better off focusing your efforts elsewhere, e.g. gzip, minifying or optimising images, as tweaking CSS selectors is more micro-optimisation.
The pesudo tell you that the elements with the named class began with the icon-
It is not working in IE 7-8 do we have any fallback code for IE?
Pseudo elements like :before and :after and even :nth-child(1) will work in IE7 if you use selectivizr.
:nth-child, yes, :before/:after, no
Ooops, you’re right. I forgot I changed the layout a bit to adjust.
BTW, just wanted to say thanks. I watched one of your screen casts a while back where you redid a friend’s site, a trumpet player I think, which got me into WP(before it was all CI). I became a regular visitor to your site, bought your book, watched your vid on lynda.com a thousand times and now what was a part time gig (which I did more for fun than anything really), is seriously threatening to turn into a full time job. I got four sites in the pipeline right now!
That’s really amazing style based on pure CSS.
And nowadays, I encounter many cases about IE (IE really sucks, IMHO). It’s not a big deal, lol
DUDE! These are niiiice…
That’s awesome!
Side note: display:block isn’t necessary when using absolute positioning. Element is no longer part of the page flow.
Might help save a couple of css lines ;)
Well done, you guy,that i have a question that the code ” .
nav a:before {
position: absolute;
top: -18px;
left: -18px;
bottom: -18px;
right: -18px;
} It seems the four positoning param make a width and height both of which was calculated by the a element plus the margin for the nav a:before pesudo element.
So am i right?
I have a question concerning this example code:
.icon-home:after { content: “\2302”; }
.icon-cog:after { content: “\2699”; }
.icon-cw:after { content: “\d7c60”; }
.icon-location:after { content: “\e724”; }
I see how the icons are being generated using the specific “{ content: “\2302″; }” (as above). This woud be the icons on the buttons themselves for example.
How would I replace the icons from http://www.fontello.com; Do they give you the numerical content Id # once downloaded or how does that work?
I would appreciate your help in this matter.