High-quality interface elements are a great way to add that extra bit of refinement to a website. I’ve been maintaining a repo of CSS3 buttons for the past few months and I’m starting to see them slowly make their way out into the real world. This tutorial is going to give you a deeper understanding of the design thinking that goes into making these buttons and show you how to make them from scratch.

For this tutorial, we’re going to be building the button that Peter Vidani recently named 2010 Button of the Year. As you can see, we’re aiming for an almost exact reproduction of the image-based button that appears on the Apple website:

When designing buttons and other interface elements, it’s important to be consistent about where the light source on your page is coming from. If you look at the button we’re building, you can see that the light source is coming from directly overhead. This means there is a slight highlight at the top edge of the button, the background color goes from lightest at the top to darkest at the bottom, and there is a slight shadow underneath the button. We’ll make sure to keep the light source consistent as we add the hover and active states later on.

Let’s look at the code for the normal state of the button.

First, we use CSS3 background gradients for Mozilla and WebKit, with a solid color for all other browsers.

button { background : #3b88d8 ; background : -moz-linear-gradient ( 0% 100% 90deg , #377ad0 , #52a8e8 ); background : -webkit-gradient ( linear , 0% 0% , 0% 100% , from ( #52a8e8 ), to ( #377ad0 )); }

Next, we add the border color. Keeping in mind the light source concerns we mentioned above, the top border is slightly lighter than the bottom edge, with the left and right edges being somewhere in between these two values.

button { border-top : 1px solid #4081af ; border-right : 1px solid #2e69a3 ; border-bottom : 1px solid #20559a ; border-left : 1px solid #2e69a3 ; }

We then add two box shadows: one to create the slight highlight at the top of the button and another for the drop shadow underneath the bottom.

button { -moz-box-shadow : inset 0 1px 0 0 #72b9eb , 0 1px 2px 0 #b3b3b3 ; -webkit-box-shadow : inset 0 1px 0 0 #72b9eb , 0 1px 2px 0 #b3b3b3 ; }

Finally, the text shadow declaration. Because we want the text to appear slightly inset into the button, we add a slight shadow at the top of the text. If we wanted the text to appear raised, our shadow would have gone at the bottom.

button { text-shadow : 0 -1px 1px #3275bc ; }

For the hover state, we want to imagine that as we move our mouse over the button, we cast a slight shadow on it, causing it to get darker. This means that we need to make the background, border, box shadow and text shadow all a bit darker. We also want to change the cursor style to what you normally see when hovering over a link, to reinforce the clickability of the button.

button :hover { background : #2a81d7 ; background : -moz-linear-gradient ( 0% 100% 90deg , #206bcb , #3e9ee5 ); background : -webkit-gradient ( linear , 0% 0% , 0% 100% , from ( #3e9ee5 ), to ( #206bcb )); border-top : 1px solid #2a73a6 ; border-right : 1px solid #165899 ; border-bottom : 1px solid #07428f ; border-left : 1px solid #165899 ; -moz-box-shadow : inset 0 1px 0 0 #62b1e9 ; -webkit-box-shadow : inset 0 1px 0 0 #62b1e9 ; cursor : pointer ; text-shadow : 0 -1px 1px #1d62ab ; }

For the active state, we want to imagine that we’re depressing the button into the page. This means that the button will darken and have a shadow cast over it. We achieve this affect by adding a darker, larger inset box shadow to the button, along with making other elements of the button darker.

button :active { background : #3282d3 ; border : 1px solid #154c8c ; border-bottom : 1px solid #0e408e ; -moz-box-shadow : inset 0 0 6px 3px #1657b5 , 0 1px 0 0 #fff ; -webkit-box-shadow : inset 0 0 6px 3px #1657b5 , 0 1px 0 0 #fff ; text-shadow : 0 -1px 1px #2361a4 ; }

You make a disabled button like this:

<button disabled= "disabled" > Download iTunes </button>

We add the disabled style to the normal, hover and active states of the button to ensure that it appears fully disabled. We also changed the cursor style to reinforce that no action can be taken. (I tweaked my version a bit to remove the white bottom border found in the original.)

button [ disabled ], button [ disabled ] :hover , button [ disabled ] :active { background : #999 ; background : -webkit-gradient ( linear , 0% 0% , 0% 100% , from ( #dadada ), to ( #f3f3f3 )); border-top : 1px solid #c5c5c5 ; border-right : 1px solid #cecece ; border-bottom : 1px solid #d9d9d9 ; border-left : 1px solid #cecece ; color : #8f8f8f ; box-shadow : none ; -moz-box-shadow : none ; -webkit-box-shadow : none ; cursor : not-allowed ; text-shadow : 0 -1px 1px #ebebeb ; }

Rounded corners in CSS can sometimes look a bit jagged. In WebKit browsers, you can use this line to improve their appearance:

-webkit-background-clip : padding-box ;

You’ll want to add this line after you declare the background, as described in this post.

In Firefox, the button element gets a bit of extra height that you can partially eliminate using this declaration:

button ::-moz-focus-inner { border : 0 ; }

In my experience, buttons in Firefox still end up a bit taller than in WebKit browsers, however.

This almost exact reproduction of the Photoshop button is possible in Chrome and Safari on the Mac, with Firefox being almost there. Chrome on Win/Linux currently has a bug that affects buttons that use both rounded corners and an inset box-shadow. A fix should be coming soon. As expected, in Internet Explorer these buttons will fall back to a lesser-styled, but still functional look.