Dmitry Fadeyev
January 10, 2012

Styling Button Links With CSS3

A fair while back I wrote a post on pressed button states using CSS, which was a tutorial on how to implement a pressed down button effect on custom styled links. In that post I used images to achieve the visual effect. Now that we have good CSS3 support, that method is really out of date. In this post I’ll show you how to achieve the same effect using CSS3, as well as how I’d go about styling the rest of the button.

Step 1: the button

We’ll assume you have a custom styled link that looks like a button. This is used for links outside of forms that you want to give weight to, for example: a signup link. There are different ways of doing this, including turning the inline link into a block, but let’s look at a simple solution that just uses padding.

Let’s assume we have a link with a class of “button-link”. Here’s the CSS code we’d use to make it look more like a button:

.button-link {
    padding: 10px 15px;
    background: #4479BA;
    color: #FFF;
}

The code above will give you a link that looks like this:

Button Link

It’s kind of flat and boring. We can make it look more like a button by adding a few more styles, like rounded corners, borders, a text shadow and inner and outer shadows. I’m not going to use it here, but you can also use a gradient background instead of a single color, which will make the button stand out even more. The point here isn’t to provide copy-paste code, but to show you how to use CSS3 styles to construct your buttons:

.button-link {
    [...]
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    border: solid 1px #20538D;
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
    -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
}

Here’s what the styled button looks like, it’s starting to look a lot better:

Button Link

You can see how the individual styles add up to create the look of a button. First, we round off the corners. We then add a border around a button and a semi-transparent black text shadow to make the text look inset.

Finally we add two box shadows, a semi-transparent white one inset right at the top of the button, and a black one outside. The white one works as a 1 pixel highlight to make the button pop out. The black one at the bottom is hardly visible, but adds a nice final touch.

Step 2: the hover state

We’ve now got a link that looks like a button, but the hover state is still the same as that of a normal link. For buttons you will typically see a change in background color as the hover state. We style the hover state using the “:hover” pseudo-class.

Because people may be navigating with a keyboard and not a mouse, we also share this style with the “:focus” pseudo-class, which will highlight the button when tabbing through links:

.button-link:hover, .button-link:focus {
    background: #356094;
    border: solid 1px #2A4E77;
    text-decoration: none;
}

Here I’ve set a darker background, a darker border and removed the underline decoration. I also want to smooth out the transition effect so I’m going to add a transition-duration property to the original selector like this:

.button-link {
    [...]
    -webkit-transition-duration: 0.2s;
    -moz-transition-duration: 0.2s;
    transition-duration: 0.2s;
}

The reason I’m adding the transition-duration property to the original selector rather than to the hover pseudo-class is because the hover one will only work when you hover over the button, not when the mouse cursor leaves it. Adding it to the original ensures the duration applies to both cases: when the cursor hovers over the button, and when it leaves it.

The hover effect will now look as follows (hover over the button):

Button Link

Step 3: the active state

The last step is to add an effect to the button when the user clicks on it. We want a satisfying pressed down look, so we’ll be using an inset shadow for it. I will also darken the background and the border a little more.

The active state can be styled using the “:active” pseudo-class like this:

.button-link:active {
    -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    background: #2E5481;
    border: solid 1px #203E5F;
}

Note that the box-shadow no longer includes the outer portion (the bit after the comma in the original). This is because the button is now pressed, so it will not be making a shadow outside. Instead we turn the inset white shadow into a dark one, and make it larger to indicate the pressed down state.

Try clicking on the button below:

Button Link

Optional: disable text select

Being able to select text on the button link will sometimes results in a text highlight if the user clicks on the button a couple of times. This doesn’t look good. Since the user won’t need to copy and paste the label, we can disable text selection like this:

.button-link {
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
}

Thanks to Régis in the comments for this suggestion. You can try it out on the button in the above example.

That’s it

And we’re done. We’ve got our link styled as a button using CSS3, together with hover and active states. Here’s all the code combined:

.button-link {
    padding: 10px 15px;
    background: #4479BA;
    color: #FFF;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    border: solid 1px #20538D;
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
    -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);
    -webkit-transition-duration: 0.2s;
    -moz-transition-duration: 0.2s;
    transition-duration: 0.2s;
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
}
.button-link:hover {
    background: #356094;
    border: solid 1px #2A4E77;
    text-decoration: none;
}
.button-link:active {
    -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);
    background: #2E5481;
    border: solid 1px #203E5F;
}

Final note

Browser specific code makes this longer than it should be, so I recommend using a CSS pre-processor like LESS or SASS, which will let you set up mixins for common use cases like gradients, rounded corners and box shadows that you can drop in using just a line of code.


Your comments