Type study: An all CSS button

This is part of a series of guest posts covering tips and tricks for working with fonts on the web. Today’s post was written by Dan Cederholm of SimpleBits.

A few years ago I gave a talk about why a button made a great place to bring in type from a branding element (such as a logo). My point was that if the type in your logo was an image, and stylish buttons were also often images, then why not align the fonts in both to bring some cohesiveness to the typography. This was probably four years ago, and we’ve come a long way since. Now, in certain situations, CSS can replace the inflexible image buttons we used in the past. Add on top of that the advances made in @font-face and you have yourself a powerful combination for creating a wide variety of interface elements that are reusable and will degrade well in older browsers.

The button is also a great place to showcase many of the new CSS3 properties in one place, which is another reason I’m particularly taken with buttons at the moment. Through the use of box-shadow, text-shadow, border-radius, and CSS gradients, we can create highly polished interface components that don’t require images. Check out the demos from Rogie King and Mark Otto to get a sense of how CSS3 can be used to add dimension to otherwise flat objects.

Let’s build a button, friends.

The markup

I’m going to use a hyperlink in this example, but the styles we’re going to add could just as easily be applied to a <button> or <input> element as well.

<a href="#" class="btn"><span>Press this!</span></a>

Notice the extra <span> element here. Don’t panic; we’ll use that to create an animated effect when pressing the button (more on that later). Trust me, this harmless extra element is worth it.

Adding the styles

Our first step is to add basic styles for background, color, and rounded corners to make this simple little link look more like a button.

I’m adding the vendor-prefixed border-radius properties for WebKit and Mozilla browsers, followed by the real, non-prefixed version for future-proofing. I’m also setting display: inline-block; on the link so that we can animate the clicking of the button later on.

Next, let’s hop on over to Typekit and choose a typeface for the button. As I mentioned earlier, buttons are a great place to reintroduce brand type, and with Typekit we can quickly and easily add a custom font here without using an image.

With the classic Cooper Black font chosen, and the simple Typekit scripts in place, I’ll now apply that font to the button style.

For browsers that currently support it (WebKit and Mozilla), I’m going to override the background color with a CSS gradient that runs from the original blue to a slightly lighter blue. This will make the surface of the button appear slightly concave.

Notice that we’ll keep the original solid background color prior to the gradient rules; browsers that don’t yet support CSS gradients will degrade to this solid color.

￼To make the wonderfully-chunky Cooper Black text look as though it’s sunken into the button a bit, we’ll add a subtle text-shadow with a negative vertical value. That will create a darker shadow just above the text.

So far, we’ve been adding styles to that inner <span> inside the hyperlink, but to finish off the styles that will make the button look three-dimensional, we’ll tack on a few rules to the outer <a> element itself.

I’m repeating the inline-block and border-radius rules here to match the <span>. To add dimension, I’m adding two box-shadows: one for the darker blue that creates the bottom portion of the button, and one for the shadow that the button casts on the white background. Multiple box-shadows can be strung together and delimited with commas in a single rule. Notice I’m adding the vendor-prefixed rules for WebKit and Mozilla, followed by the non-prefixed box-shadow property at the end.

And voilà. A three-dimensional button using CSS3, a custom embedded font via Typekit, and zero images. I think a tasty beverage is in order.

But wait, let’s make things even more interesting and enrich the experience here a bit by animating the button when it’s clicked. Again, we’ll lean on CSS3 to do the animating in browsers that support it, while safely degrading in browsers that don’t.

If we add two new declarations for the :active state (as the button is clicked), we can give the illusion that the button has been depressed.

The first declaration adjusts the box-shadow on the <a> element: dimming it slightly and making it less pronounced (since when depressed, the button is shorter and will cast a smaller shadow).

The second declaration applies a translate transition on the <span> inside the <a>. Translate moves an element from its initial position on the page using x and y coordinates. Here, we’re simply moving the surface portion of the button down 4 pixels. The reason we’re using a transform — rather than say position or adjusting margins — is that we can also apply a transition to that transform, smoothing out the move with a bit of animation. Moving the <span> 4 pixels down hides 4 pixels of the darker box shadow on the <a> element and the effect is complete.

Our last step is to add CSS transitions to smooth out both the shadow-dimming and the translate move. We’ll again use the appropriate vendor prefixes for all the capable browsers (WebKit, Mozilla, and Opera), plus the real properties for the future.

Notice I’ve added two transition stacks here: one on the <a> element, which transitions the subtle shadow change, and one on the <span> element which smooths out the translate transform, making the button push a bit more realistic.

This is most excellent, Dan. Your work is flawless and inspiring.
Was just working on a button similar to this the other day, but nowhere NEAR as slick, but I didn’t have the internal span inside the a. Was trying to keep it as clean as possible in html. Was actually tinkering with some of this outside of the a in an list item.

This is a wickedly awesome button, and it’s really amazing what you can do with just CSS3 these days. Great work!

In case anyone’s wondering, all the button features work great in Chrome and Safari (WebKit-based). Firefox doesn’t have the smooth transition effect. Opera (9.8, I should upgrade) shows the visual styles, but doesn’t have an alternate (pressed) state at all. IE 7/8 show a flat, square box with the custom font–usable and gracefully degraded (at least IMO).

I have tried to use all-CSS buttons before, but most of my clients have complained about how they degrade in IE. With square buttons, I can usually get away with it, but with rounded ones I usually end up using a CSS sprite method. Life will be good when CSS3 support is a little more universal.

In theory, this effect should be possible without a nested span. I had a quick play with it but unfortunately, in Chrome at least, the transitions on things like padding/margin/box-shadow don’t execute quite right and the button appears to “jump” by a pixel during the transition.

It’s probably possible to get it working with positioning rather than translate, but I wanted to mention the transform route here as an alternative. Especially if you run into problems using positioning for reasons not associated with the button itself.

Had a little play around with this to see what other ways there might be. I see why you opted for the translate(); should have checked in Firefox 4 before commenting because it doesn’t appear to transition positional offsets other than “left”. Shame.

There’s this alternative method using pseudo-elements (so the HTML doesn’t need the extra span). Works best in Firefox 4, which supports transitions on pseudo-elements, but looks ok in Chrome too.

[@Dan Mall, @CSS Webstandard-Blog Once you actually try to recreate this with pseudo-elements, you’ll see it’s actually not that easy to do.]

Another alternative (no pseudo-elements necessary) is to transition the other box-shadow and apply the transform to the link itself. Works perfectly in Firefox 4, but Chrome’s transitions look a bit buggy so the effect is a little ugly. Something like this:

How do you handle the shadow on the white having .2S delay on the click and return. you need 0s on the initial click down state but .2s delay in returning in order to synchronize the shadow to the button span animation.

Early in the article you mention that these styles can also be used on “button” or “input” tags as well. Placing an “a”, “button”, and “input” tags side by side using the styles renders different results. Namely, it appears the widths will behave differently and the border on the “button” and “input” elements affect the height differently. Maybe I’m just doing something wrong?

@Nicolas Funny story. I tried pseudo-elements for the new United Pixelworkers navigation, but the inability of any browser except Firefox 4 beta to handle the transitions was a deal-breaker. Support for CSS transitions on background images would have been heavenly.

Nice article. It amazes me to think back to all the kludges and hacks that were necessary to make this sort of thing work in the dark ages (last decade…). How far we’ve come!

It’s worth pointing out that the Internet Explorer 9 RC renders the button the same as Firefox, with the box shadow, borders, and font face. It does not have the text shadow or gradient. Those aside, it’s pretty good. However, it doesn’t respond to a click – there’s no “push” action.

You may want to check out our type rendering series, which goes into detail about cross-browser and operating system rendering issues. Also, all of the fonts tagged “paragraph” on Typekit have been manually hinted to render well, even in Win XP (where ClearType is disabled by default).

The example you cite as cleaner only swapped the normal and depressed states without any transition effect at all. Your example provides slightly cleaner HTML by throwing out a feature.

Furthermore, your “quicker” example has at least as many, if not more, lines of CSS as Dan’s.

I challenge you to come up with the same effect and appearance as Dan has without the span. If you can, we’d all love to see it. If you can’t, then you’d better be careful before you get back on your high horse or you’ll slip off again.

Here’s one thing I’ve found really useful: every browser that supports gradients also supports RGBA colors and multiple backgrounds, so instead of specifying your gradients with solid hex colors, place a translucent black or white gradient “mask” over a single solid background color. This makes for nice reusable code where you can swap out just one isolated color value at the end and get a different color buttona. So, instead of this:

SO COOL, I love the button, you could do something cool on hover like maybe get rid of the drop shadow to make it look like it’s being pressed too… maybe even use css transforms to make it a little smaller, not sure if that’d look cheesy or not but there’s lots of possibilities.

In the demo this code add light colored square corenrs at the bottom of the span taking away from the rounded corner feel. If you remove the code you get the nice round corners sans the light colored square corners.