The Juice Journal

Tutorial: Make Aristo’s Buttons in CSS3

Saturday June 26th 2010

Warning: this blog post is about web design technique, intended for web designers and developers only. It could be fatally boring for anyone else.

I’ve been needing an excuse to blog, some ongoing project or motivation, because I’m clearly shit at blogging. While working on an interface for a client site, I stumbled across something interesting: conceptually, the Aristo interface theme can be implemented entirely in CSS. Oddly, as far as I can tell, no one has fully attempted a pure CSS (no images) implementation of the gorgeous theme (designed by Sofa for the Cappuccino framework).

So, here’s my project: I’m implementing the Aristo theme, entirely in pure, juicy CSS3. I’ll be doing it piece by piece, widget by widget, and updating the project’s main site as I go.

Someone will probably sit down and jam out all the rest of the Aristo widgets before I even get to the next, but I’m doing this mainly as my own exercise, so go for it.

Today I’ll publish how to create Aristo’s buttons using only the tidiest of semantic HTML and some pretty intense CSS (including CSS3, you design-lusting nerd, you). I’ll explain step-by-step how to create these sexy buttons, and I’ll conclude with a quandary for you, my colleague in design.

Aristo Buttons in CSS3

First, make sure you have the layered Aristo PSD. Today we’ll just work on recreating those sexy buttons. Essentially, our approach will be to duplicate Aristo’s Photoshop layer styles in CSS.

The HTML

Let’s start with our HTML. We’ll accept no compromises in the form of extra <span>s, <div>s, whatever:

<p><button>More</button><button disabled="disabled">More</button></p>
<p><button class="default">More</button><button class="default" disabled="disabled">More</button></p>

UPDATE: To clarify, this HTML defines four buttons. The <p> tags are just for grouping. All you really need is <button>Love</button>. Pretty sure this code is as clean as it could possibly be.

The Fonts

We want our buttons to be super-flexible, so we’ll size them around whatever text we put in them. That means our first step has to be getting the font and font-size right.

Oddly (sadly, comically, tragically), the Aristo PSD uses Arial as its font. Sad. Comic. Tragic. I say let’s give those people with the super-legible Lucida Grande font a slightly enhanced experience. One of the most awesomest things about CSS is that you can specify a font stack; users with the best font get to use the best font, but others can still get a usable, thought-out reading experience. So here’s my font stack for Aristo:

body {
	font-family: "Lucida Grande", Helvetica, Arial, sans-serif;
	}
button {
	font-size: 14px;
	font-weight: bold;
	}

Users who have Lucida Grande (Mac users, basically) will see things in that font, and users without will see Helvetica, Arial, or a fall-back sans-serif font, in that order of preference. Always remember to test your design with each font in your stack, throughout development.

In your design, you might want to put the font-family declaration on your button rather than body, if you have a different main font.

The Button’s Shape

The next thing we notice is the size and roundedness of Aristo’s buttons. The corners appear to have a 3px radius. I’m particularly fond of allowing users to resize their interface (also makes it more flexible if we want to resize the interface later in development), so we’ll make the rounded corner relative to the button’s font-size. The corners should have a 3px radius when the font-size is 14px, so: 3 ÷ 14 ≅ .214. Our radius will be .214em.

In order to get the right proportions inside the button, we need a bit of padding. I’ve also added some margin, to give the buttons a bit of space. Our button so far:

button {
	margin: 0 .5em .5em .5em;
	padding: .15em 1em;
	font-size: 14px;
	font-weight: bold;
	border-radius: .214em;
	-webkit-border-radius: .214em;
	-moz-border-radius: .214em;
	cursor: pointer;
	}

That gives us something that’s starting to look like a button (though an ugly one):

The Label Text

The button label text has a single pixel of white underneath its letters, giving it that embossed look. We’ll use a single-pixel text-shadow.

button {
	…
	color: #4f4f4f;
	text-shadow: 0 1px 1px #fff;
	…
	}

That says that we have a text-shadow that’s offset 0px horizontally, 1px vertically (down), and has a 1px spread. And it’s white. The reason I chose pixel sizing here instead of ems is because this little detail is only meant for a single crisp line underneath text; it shouldn’t resize.

That gives us something that’s starting to look like a button (though an ugly one):

The Borders

The text is nice, but those borders look like crap. In the Aristo PSD, the borders are made up of a gradient. I decided it would be faster and easier to just use three separate border colors, one for top, one for bottom, and one for the sides. I leave it to the reader (and me, I’ll probably come back to it) to use border gradients in the CSS.

button{
	…
	border: 1px solid #acacac;
	border-top-color: #bdbdbd;
	border-bottom-color: #949494;
	…
	}

The Background Gradient

The big thing still missing is the inside coloring, the button’s main gradient. We’ll use the dissimilar -webkit-gradient and -moz-linear-gradient functions (values? properties?) to fill the background with that smooth button look.

button {
	…
	background: #ddd;
	background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#c4c4c4));
	background: -moz-linear-gradient(top, #ededed, #c4c4c4);
	…
	}

We specify the background multiple times, to accommodate different browser capabilities. First we give a solid gray background, for those browsers that have no implementation of CSS gradients. Then, we give WebKit browsers their special -webkit-gradient with its funky syntax. And finally Mozilla-based browsers get their special -moz-linear-gradient with its funky syntax. Internet Explorer has a filter that can do this kind of gradient (funkiest syntax of them all), but I leave that as a tortuous exercise for the reader as well.

Sweeeet. Check out that sexy, sexy button. Ooh, yeah.

The Focus/Hover State

The Aristo PSD has a focus state for the button, with a nice little glow/shadow thing around it. In Cappuccino apps, I think the focus state is only for showing what element the keyboard will interact with (please correct me in the comments if I’m wrong). I think it would be fun if it also showed this state on :hover. Feel free to disagree, but don’t even try to tell me it doesn’t look cool when you mouse over it.

We’ll make the glow with a CSS box-shadow:

button:hover,
button:focus {
	box-shadow: 0 0 7px rgba(0,0,0,0.4);
	-webkit-box-shadow: 0 0 7px rgba(0,0,0,0.4);
	-moz-box-shadow: 0 0 7px rgba(0,0,0,0.4);
	}

Our shadow is offset by 0px horizontally, 0px vertically, and has a spread of 7px, with a transparent gray for color. This gives us a gray glow on all sides of the button.

You can see the box-shadow property is consistent across the browsers that support it. In cases like this, be sure to include the generic, non-prefixed property in addition to the vendor-prefixed versions (-webkit-whatever, -moz-thingy, etc.). This will hopefully make your code work great in (most|all) future browsers. You’ll also be granted 1,000 virgin ponies in the afterlife.

Why pixels instead of ems? In previous projects with lots of box-shadows, I found that resizing shadows can severely increase the computation a browser must endure to display your page. Even if you’ve tested your page and it scrolls and performs just fine at default text size, increasing the size of all shadows on the page can make even normal scrolling laggy. Setting the shadows in px will keep them at their original size, keeping shadow computation at a manageable level.

Here’s what our button looks like so far:

The Transition

Let’s enhance the experience a little more, by making that glow fade in and out, more like a real glow. We’ll use the super-awesome CSS transitions. Man I love me some CSS transitions. So easy to progressively enhance a page/site/app; much easier than using Javascript for every little animated change, plus it can be accelerated in hardware, making CSS transitions as smooth as possible.

button {
	…
	transition: all .2s ease-in-out;
	-webkit-transition: all .2s ease-in-out;
	-moz-transition: all .2s ease-in-out;
	-o-transition: all .2s ease-in-out;
	…
	}

This code says that we’ll smoothly transition all properties over .2s (2/10ths of a second), easing the animation both at the start and finish. We could have animated just the box-shadow, but I decided to use the keyword all to avoid cramming in all those vendor-prefixed box-shadow properties. We would go that route if we needed to change a different element without a transition. But I like transitions.

Looks pretty sweet, huh? Fun to hover over the buttons and see that effect.

The Active State

We’re making a button, and buttons are meant to be pressed. Now we need to make the pressed state (“active” state, in CSS/HTML form parlance). Basically, we just define a different background gradient, change the border colors, and ditch the box-shadow:

button:active {
	background: #ccc;
	background: -webkit-gradient(linear, left top, left bottom, from(#c3c3c3), to(#ebebeb));
	background: -moz-linear-gradient(top, #c3c3c3, #ebebeb);
	border-color: #a6a6a6;
	border-top-color: #939393;
	border-bottom-color: #bcbcbc;
	box-shadow: none;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	-o-box-shadow: none;
	}

Nothing new or thrilling there. But try the button out:

A Default Button

Aristo also has a “default” button style, blue instead of gray. We’ve marked this in our HTML with class="default". To get the default button to be blue instead of gray, we just have to redefine the colors and gradients. Pretty straightforward:

button.default {
	color: #1c4257;
	background: #a3cde3;
	background: -webkit-gradient(linear, left top, left bottom, from(#b9e0f5), to(#85b2cb));
	background: -moz-linear-gradient(top, #b9e0f5, #85b2cb);
	border: 1px solid #759bb1;
	border-top-color: #8ab0c6;
	border-bottom-color: #587e93;
	}

And its hover and active states:

button.default:hover,
button.default:focus {
	box-shadow: 0 0 7px #53a6d5;
	-webkit-box-shadow: 0 0 7px #53a6d5;
	-moz-box-shadow: 0 0 7px #53a6d5;
	-o-box-shadow: 0 0 7px #53a6d5;
	}
button.default:active {
	background: #8abcd7;
	background: -webkit-gradient(linear, left top, left bottom, from(#81afc8), to(#b7def4));
	background: -moz-linear-gradient(top, #81afc8, #b7def4);
	border-color: #6e94a9;
	border-top-color: #567c91;
	border-bottom-color: #88aec4;
	box-shadow: none;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	-o-box-shadow: none;
	}

A Disabled Button

Some buttons need to be disabled. Fortunately, HTML5 buttons can have a disabled attribute. We’ll style these based on that attribute.

button[disabled],
button[disabled]:active {
	color: #a7a7a7;
	background: #efefef;
	background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e1e1e1));
	background: -moz-linear-gradient(top, #f6f6f6, #e1e1e1);
	border-color: #d4d4d4;
	border-top-color: #dedede;
	border-bottom-color: #c9c9c9;
	cursor: default;
	}
button[disabled]:hover,
button[disabled]:focus {
	box-shadow: none;
	-webkit-box-shadow: none;
	-moz-box-shadow: none;
	-o-box-shadow: none;
	}

The Whole Set

Here’s the whole set of Aristo buttons we’ve just styled, together for your button-pushing pleasure.

Notice that you can resize your browser’s font-size, and these buttons will resize right along with it. This also means you can resize all appropriate aspects of buttons on your site just by resizing the button’s font.

The Advanced Single-pixel Detail

… of Doom

There’s one detail our buttons are missing from the original Aristo graphics: each button should have a single-pixel highlight just inside its border along the top or bottom, depending on whether it’s currently pressed. This is a tricky prospect to handle just with CSS. The folks from Sofa used an Inner Shadow layer style in Photoshop to do the trick. Unfortunately, CSS has no inner-shadow property, so we delve into a stranger realm.

We could add an extra <span> to our button and style that, but since I’m not on a deadline for this I figured I’d think it through until I could do it with my previous pristine markup.

I came up with an approach that is funky, clever, and only sort of works. Basically, I use the :after pseudo-element of the button, make it a block, position it absolutely, then give it a 1 pixel border. Kind of crazy, kind of cool.

button {
	position: relative;
	overflow: hidden;
	…
	}
button:after {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 0;
	content: " ";
	border-bottom: 1px solid rgba(255,255,255,0.5);
	}

Kind of crazy, no? Kind of doesn’t work, too. Almost, but there are two issues. The first is that in Mozilla-based browsers the pseudo-element refuses to be positioned relative to the entire button, choosing instead to position relative to the text inside the button. The second downside is that even in WebKit-based browsers the pseudo-element doesn’t respect the border-radius and overflow: hidden. Our single-pixel line happily extends right over the round border and into the empty corner. You can barely notice it at small sizes, but if you bump up your font-size (or the size of your button, or the button’s border-radius) then it becomes painfully apparent. For this reason I’m leaving the :after pseudo-elements as display: none for now, until I or someone else can figure out a way to do this better.

I’d love to hear a better method, so please chime in in the comments if you have ideas!

In the Browsers

Even before hitting “publish”, I can hear a minor storm of “If it doesn’t work in IE6 it’s not practical” comments. If you’re the kind of designer who thinks websites need to look the same in every browser, please feel free to comment, but I’m afraid I fall into the camp that doesn’t want to design for the worst browsers first. Mostly, this is an experiment, but I do intend to use these widgets in production client sites, after more testing to make sure they function properly in all major browsers.

If you have tips on how to make these buttons look great in IE, please chime in!

A Note on Other Aristo Implementations

When I first saw this implementation of Aristo by Alex MacCaw, I hoped it would be a pure CSS version. MacCaw did some excellent (and comprehensive) work, but used images for all the controls. So I set out to do it my way (no images), mostly because I needed an excuse to learn more about CSS gradients.

The Rest of Aristo

I plan to create most or all of the Aristo widgets in CSS, one-by-one, blogging along the way. Feel free to use these in your own projects, as I’ll be releasing the code in an open-source license compatible with the original Aristo license (more details soon). Post a comment here if you’d like to share how you’ve used it.

Hopefully (for both you and me) my future posts about this process will be considerably more brief.

The Full Code

body {
	font-family: "Lucida Grande", Helvetica, Arial, sans-serif;
	}
button {
	position: relative;
	margin: 0 .5em .5em .5em;
	padding: .15em 1em;
	font-size: 14px;
	font-weight: bold;
	color: #4f4f4f;
	text-shadow: 0 1px 1px #fff;
	background: #ddd;
	background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#c4c4c4));
	background: -moz-linear-gradient(top, #ededed, #c4c4c4);
	border: 1px solid #acacac;
	border-top-color: #bdbdbd;
	border-bottom-color: #949494;
	border-radius: .214em;
	-webkit-border-radius: .214em;
	-moz-border-radius: .214em;
	transition: all .2s ease-in-out;
	-webkit-transition: all .2s ease-in-out;
	-moz-transition: all .2s ease-in-out;
	-o-transition: all .2s ease-in-out;
	cursor: pointer;
	}
	button:hover,
	button:focus {
		box-shadow: 0 0 7px rgba(0,0,0,0.4);
		-webkit-box-shadow: 0 0 7px rgba(0,0,0,0.4);
		-moz-box-shadow: 0 0 7px rgba(0,0,0,0.4);
		}
	button:active {
		background: #ccc;
		background: -webkit-gradient(linear, left top, left bottom, from(#c3c3c3), to(#ebebeb));
		background: -moz-linear-gradient(top, #c3c3c3, #ebebeb);
		border-color: #a6a6a6;
		border-top-color: #939393;
		border-bottom-color: #bcbcbc;
		box-shadow: none;
		-webkit-box-shadow: none;
		-moz-box-shadow: none;
		-o-box-shadow: none;
		}
button.default {
	color: #1c4257;
	background: #a3cde3;
	background: -webkit-gradient(linear, left top, left bottom, from(#b9e0f5), to(#85b2cb));
	background: -moz-linear-gradient(top, #b9e0f5, #85b2cb);
	border: 1px solid #759bb1;
	border-top-color: #8ab0c6;
	border-bottom-color: #587e93;
	}
	button.default:hover,
	button.default:focus {
		box-shadow: 0 0 7px #53a6d5;
		-webkit-box-shadow: 0 0 7px #53a6d5;
		-moz-box-shadow: 0 0 7px #53a6d5;
		-o-box-shadow: 0 0 7px #53a6d5;
		}
	button.default:active {
		background: #8abcd7;
		background: -webkit-gradient(linear, left top, left bottom, from(#81afc8), to(#b7def4));
		background: -moz-linear-gradient(top, #81afc8, #b7def4);
		border-color: #6e94a9;
		border-top-color: #567c91;
		border-bottom-color: #88aec4;
		box-shadow: none;
		-webkit-box-shadow: none;
		-moz-box-shadow: none;
		-o-box-shadow: none;
		}
button[disabled],
button[disabled]:active {
	color: #a7a7a7;
	background: #efefef;
	background: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e1e1e1));
	background: -moz-linear-gradient(top, #f6f6f6, #e1e1e1);
	border-color: #d4d4d4;
	border-top-color: #dedede;
	border-bottom-color: #c9c9c9;
	cursor: default;
	}
	button[disabled]:hover,
	button[disabled]:focus {
		box-shadow: none;
		-webkit-box-shadow: none;
		-moz-box-shadow: none;
		-o-box-shadow: none;
		}

You should follow me on Twitter here (@ryanmiglavs).

{love},
{ryan}

5 Comments

1 Andy L

Ah, beautiful! Just what I had been looking for… for months!

Thank you, Ryan! You've just made my day! Truly.

Posted on Friday July 2nd 2010 at 5:33 pm

2 Ryan Miglavs

Hey, Andy, glad you like it! I was looking for the same thing, figured I'd just buckle down and build it.

Stay tuned for the rest of the Aristo widgets and theme elements!

Posted on Friday July 2nd 2010 at 5:38 pm

3 Valerie W.

Very awesome! I'm completely geeking out over this, and can't wait to try it out! Will be sure to post if I come up with any cleverness worth sharing :)

Posted on Saturday July 3rd 2010 at 11:14 pm

4 Robin Berjon

Hi,

very nice CSS hacking, thanks for doing that.

There's a trick you haven't used. I thought someone would have pointed it out by now, but it seems not from the other comments.

There *is* a way of doing the inner shadow, and it's simple. The first part relies on the "inset" keyword that can be applied to shadow definitions. The second part is that you can specify more than one shadow at once on a given element.

You can see it working at: http://berjon.com/tmp/aristo.html

As you can see, where there already was a shadow, I simply appended the inset one as follows:

box-shadow: 0 0 7px #53a6d5, inset 0 1px 0 #fff;

That adds a white inset shadow that isn't blurred (i.e. you just get the line) and is 1px off the border.

Where you had shadow set to none for the active states, I just made it:

box-shadow: inset 0 -1px 0 #fff;

Which is the same but off the bottom. It works in FF and Safari at least, I haven't given others a shot.

Posted on Tuesday July 6th 2010 at 2:37 am

5 Tait Brown

Hey mate, you should really look at contributing to the Aristo jQuery UI theme I manage on GitHub.

http://taitems.tumblr.com/post/482577430/introducing-aristo-a-jquery-ui-theme

Posted on Wednesday July 7th 2010 at 5:11 am

Leave a Comment

No HTML is allowed. All links and new lines will be automagically converted to HTML for you.

= required


© 2010 A Socialist Pear
we ♥ dreamscape cms

XHTML
CSS