GreyWyvern.com

Sweet CSS3 buttons that you can't use... yet

Jun 7, 2010

CSS3 is coming, and with it you can create marvelous page effects which were previously only possible with images. And where CSS3 fails, you can use SVG, but that's another story. ;)

The big issue with CSS3 is that Internet Explorer version 8 and earlier support no CSS3, and chances are that many people will be using these browsers for a long time yet. If you want your design to look the same in all browsers, including IE, then you're going to have to put that fantastic new toy which is CSS3 back on the shelf and turn to more reliable means.

There is a secondary issue though, and that is that while everyone is talking about CSS3, the browsers which do crow about supporting it, actually do not support all of it, and much of what they do support is buggy, or does not look at all nice. (One of the reasons for this is that CSS3 is still a work-in-progress.) For example, many new CSS3 effects require sophisticated, Photoshop quality anti-aliasing in order to look pleasing to the human eye. No one wants to look at pixelated borders or text, at least not for very long.

A corollary to the issue above is that, once a browser does implement high-quality anti-aliasing, it may potentially use up a great deal more CPU resources during rendering than we are used to for a mere web page. Page reflows will take more time, making the page seem sluggish and unresponsive.

As an example let's try reproducing a page element in CSS3 which is normally replaced with images: the humble button.

Here we have a nice set of seven buttons:

They display the default styling for buttons within your particular browser environment. However, the nice thing about the <button> element is that its styles are easily overwritten. Let's spruce up these buttons with some pretty tame CSS2.1:

button {
  margin:0px;
  padding:10px;
  background-color:red;
  border:1px solid black;
  border-color:rgba(0,0,0,0.6);
  font-weight:bold;
  color:black;
  color:rgba(0,0,0,0.6);
}
button.green { background-color:green; }
button.blue { background-color:blue; }
button.cyan { background-color:cyan; }
button.magenta { background-color:magenta; }
button.yellow { background-color:yellow; }
button.black { background-color:black; }
button.green, button.blue, button.black {
  color:white;
  color:rgba(255,255,255,0.8);
}
button:active {
  padding:9px;
  margin:1px;
}

Pretty snappy if I do say so myself, but it doesn't have that CSS3 "oomph". The rbga() syntax is actually CSS3, but ignore that for now. Fallbacks to CSS2.1 colours were provided for Internet Explorer, but beyond this point, IE versions 8 and below get left behind. First, we'll add the hallmark of buttons everywhere, rounded corners, using the border-radius property. We'll also throw in an opacity effect on hover.

button {
  . . .
  border-radius:10px;
  -moz-border-radius:10px;
  -webkit-border-radius:10px;
}
. . .
button:hover {
  opacity:0.8;
}
button:active {
  . . .
  border-radius:9px;
  -moz-border-radius:9px;
  -webkit-border-radius:9px;
}

Very nice. You'll notice that I added the border-radius property using both the actual property name, along with the browser specific prefixes -moz- and -webkit-. Using both methods ensures that once Mozilla and WebKit based browsers implement support for the actual property name, our example should still work.

Now here is where things get a little more exciting, by adding one of the coolest new CSS3 properties: box-shadow. First we'll add a nice background shadow.

button {
  . . .
  box-shadow:0px 2px 5px rgba(0,0,0,0.6);
  -moz-box-shadow:0px 2px 5px rgba(0,0,0,0.6);
  -webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.6);
}
. . .
button:active {
  . . .
  box-shadow:none;
  -moz-box-shadow:none;
  -webkit-box-shadow:none;
}

These buttons are looking more slick all the time, but an Opera quirk causes the shadow to render strangely during the hover effects. I assume this is due to a necessary redraw not being called at the right time, but otherwise, all of Opera, Firefox, Safari and Chrome do a pretty good job here.

However, the coolest thing that you may not have known about box-shadow is that it supports multiple shadows within the same declaration. Up to six! Now, you might be thinking: things usually only ever cast one shadow, why have more than one or two? Well, instead of thinking of box-shadow as a literal shadow, try considering it a border-hugging gradient, which can be either inside (inset) or outside (default), of whatever colour you like. Suddenly all kinds of Photoshop-like effects become possible, such as inner glow:

button {
  . . .
  box-shadow:0px 2px 5px rgba(0,0,0,0.6),
             inset 0px 0px 7px rgba(255,255,255,1);
  -moz-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                  inset 0px 0px 7px rgba(255,255,255,1);
  -webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                     inset 0px 0px 7px rgba(255,255,255,1);
}
. . .
button.black {
  box-shadow:0px 2px 5px rgba(0,0,0,0.6),
             inset 0px 0px 4px rgba(255,255,255,0.8);
  -moz-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                  inset 0px 0px 4px rgba(255,255,255,0.8);
  -webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                     inset 0px 0px 4px rgba(255,255,255,0.8);
}
. . .
button:active {
  . . .
  box-shadow:inset 0px 0px 7px rgba(255,255,255,1);
  -moz-box-shadow:inset 0px 0px 7px rgba(255,255,255,1);
  -webkit-box-shadow:inset 0px 0px 7px rgba(255,255,255,1);
}
button.black:active {
  box-shadow:inset 0px 0px 4px rgba(255,255,255,0.8);
  -moz-box-shadow:inset 0px 0px 4px rgba(255,255,255,0.8);
  -webkit-box-shadow:inset 0px 0px 4px rgba(255,255,255,0.8);
}

The black button gets a smaller and less-opaque version of the style so it stands out a little less.

That looks pretty sweet already, but even more amazing effects are possible. WebKit and Mozilla are testing out a form of background colour that allows the use of gradients in CSS by specifying two colours within a special syntax. However current support is spotty, and the syntax required differs between these two rendering engines. Fortunately, a vertical background gradient can be reasonably mimicked by an inner box shadow with a blur radius close to the height of the element, along with an offset to match.

button {
  . . .
  box-shadow:0px 2px 5px rgba(0,0,0,0.6),
             inset 0px -20px 40px rgba(0,0,0,0.5),
             inset 0px 0px 7px rgba(255,255,255,1);
  -moz-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                  inset 0px -20px 40px rgba(0,0,0,0.5),
                  inset 0px 0px 7px rgba(255,255,255,1);
  -webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                     inset 0px -20px 40px rgba(0,0,0,0.5),
                     inset 0px 0px 7px rgba(255,255,255,1);
}
. . .
button.black {
  box-shadow:0px 2px 5px rgba(0,0,0,0.6),
             inset 0px 20px 40px rgba(255,255,255,0.25),
             inset 0px 0px 4px rgba(255,255,255,0.8);
  -moz-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                  inset 0px 20px 40px rgba(255,255,255,0.25),
                  inset 0px 0px 4px rgba(255,255,255,0.8);
  -webkit-box-shadow:0px 2px 5px rgba(0,0,0,0.6),
                     inset 0px 20px 40px rgba(255,255,255,0.25),
                     inset 0px 0px 4px rgba(255,255,255,0.8);
}
. . .
button:active {
  . . .
  box-shadow:inset 0px -20px 40px rgba(0,0,0,0.5),
             inset 0px 0px 7px rgba(255,255,255,1);
  -moz-box-shadow:inset 0px -20px 40px rgba(0,0,0,0.5),
                  inset 0px 0px 7px rgba(255,255,255,1);
  -webkit-box-shadow:inset 0px -20px 40px rgba(0,0,0,0.5),
                     inset 0px 0px 7px rgba(255,255,255,1);
}
button.black:active {
  box-shadow:inset 0px 20px 40px rgba(255,255,255,0.25),
             inset 0px 0px 4px rgba(255,255,255,0.8);
  -moz-box-shadow:inset 0px 20px 40px rgba(255,255,255,0.25),
                  inset 0px 0px 4px rgba(255,255,255,0.8);
  -webkit-box-shadow:inset 0px 20px 40px rgba(255,255,255,0.25),
                     inset 0px 0px 4px rgba(255,255,255,0.8);
}

The addition of this pseudo-gradient makes the appearance of these buttons many times more pleasing to the eye! A quick explanation of the inserted shadow is as follows:

  • The shadow is specified as inset so it appears inside (and is delimited by) the element's border.
  • The first length value is the horizontal offset, and since we want the effect horizontally centered, we leave it at 0px.
  • The second length value is the vertical offset. In order to make the effect appear like it is only coming from the bottom, we bump up the offset by -20px. A negative value offsets the shadow up, while a positive offset would move the shadow down.
  • The third length value is the blur radius. We want the blur radius to spread and cover most of the element's height, so we set it at 40px.
  • The final value is the shadow colour, which we set to a half transparent black.
  • For the black button, an inverse shadow was used (white coming from the top) so that the gradient is visible against the black background colour.

At this point we notice that there are some problems with the WebKit based browsers. First of all, Safari 4 on Windows does not support inset shadows of any kind; including an inset shadow in the box-shadow declaration makes outset shadows buggy or disables them completely (the just-released Safari 5 fixes this). While in Chrome for Windows, the inset shadows are not affected by the rounded corners and pointed bits stick out beyond the border. You can sort of hide this issue by reducing the border-radius value, but that still doesn't help Safari 4.

Okay, so three mainstream browsers still support all the crazy CSS3 we've thrown at it so far, Opera, Firefox and Safari 5, so let's go one big step further. We'll give the CSS3 transform property a spin.

button {
  . . .
  transform:rotate(12deg);
  -o-transform:rotate(12deg);
  -moz-transform:rotate(12deg);
  -webkit-transform:rotate(12deg);
}
. . .

Wow! Amazing! Incredible! But... here's where the CSS3 bullet train breaks down in our remaining browsers. In Opera the buttons look wonderful, but the addition of the transform effect makes dynamic effects, like :hover, :active and even scrolling very slow. It's like the browser is redrawing everything from the ground up each time the style changes. This assumption is bolstered by the fact that the strange problem with dynamic box-shadow changes in Opera has disappeared. Apparently the lost redraw is now happening, but it is taking a very long time to do so!

Chrome, which we lost earlier due to the inset shadow bleeding from the button's rounded corners, goes even more wonky here, allowing the shadow to bleed all over the place into a box which is sized big enough to contain the entire rotated button.

Meanwhile, in Firefox and Safari, the small rotation succeeds, but the text on each button fails to get re-anti-aliased to the new orientation. This leaves the text blocky, damaging legibility, and generally making the buttons look unprofessional. Even trying to counter the effect with a bit of text-shadow "blur" doesn't help very much:

button {
  . . .
  text-shadow:rgba(0,0,0,0.3) 0px 0px 2px;
}
. . .
button.green, button.blue, button.black {
  . . .
  text-shadow:rgba(255,255,255,0.4) 0px 0px 4px;
}
. . .

You'd certainly want to use an image instead of the poorly rendered text on these buttons in Firefox and Safari.

So there you go: snazzy CSS3 buttons that aren't quite ready for prime-time use. While you probably can't use buttons using all of this CSS3 yet, hopefully some of the techniques used here can be gleaned and used separately in your designs. You can view the demo page with all the CSS.

To conclude, I'll leave you with one more bit of CSS, just to prove that CSS3 buttons, while manifesting all kinds of failure across all browsers, are still Web 2.0 compliant :D

button {
  . . .
  position:relative;
}
. . .
button::after {
  position:absolute;
  top:-16px;
  right:-12px;
  content:url(version-2-badge.png);
}
. . .

Huzzah!

IE8: Same version equals different rendering? Centering HTML elements larger than their parents

Comments closed

Recent posts

  1. Iguana no Musume / Iguana Girl Aug 2016
  2. What I'd Like To See In The Elder Scrolls VI - Part 2 Aug 2015
  3. What I'd Like To See In The Elder Scrolls VI Jul 2015
  4. Cyprus, and what capitalists want Mar 2013
  5. Let interest rates on housing rise Sep 2012
  6. Archive

Items of Interest

Webcomics Reading List

Good Eats

Twitter RSS 2.0 Valid XHTML 1.0! Copyright © 2017 Brian Huisman AKA GreyWyvern
ContactSite mapSearch