CSS buttons are much more efficient
than image-based buttons because they’re entirely text-based. A simple
unordered list is all you need in the XHTML markup—CSS styles take care of the
rest. Furthermore, you don’t need any JavaScript to swap images for a rollover
effect because CSS pseudoclasses let you build separate styles for each of the
states (link, visited, hover, active) of a hyperlink.

The only problem with pure CSS
buttons is that they tend to look rather flat with a solid color background and
a simple border. One solution is to use a hybrid technique that incorporates a
background image behind the CSS-styled text button to give it a 3D effect.
However, reader e-mail prompted me to look for a way to create a 3D button
effect with pure CSS—no images required. I found not one, but two, techniques
for creating a beveled-edge look by styling the borders of a CSS button.

Creating a beveled-edge effect

To give a button a 3D beveled-edge
effect, you need to simulate a light source creating highlights and shadows on
the edges of a raised button. If the light source is above and slightly to the
left of the button, then the top and left sides of the button will be lighter
than the front face, and the bottom and right sides will be darker than the
button face.

So, the secret of the
three-dimensional effect is to use CSS borders to simulate the sides of the
button and to give each border a slightly different color, depending on whether
it represents a highlighted side or a shadowed side. To look realistic, the
borders should have mitered corners, and CSS borders meet that requirement
nicely.

Technique 1: Using inset/outset borders

As it turns out, there is a CSS
border property that is capable of producing a reasonable facsimile of a
beveled edge effect automatically. You don’t need to do anything more than
specify inset or outset as the border-style
attribute for your button styles. The browser handles the details of rendering
the element borders in slightly different shades of the background color to
achieve the desired effect. The outset
attribute simulates the shaded edges of a raised button and the inset attribute simulates a depressed
button by reversing the shading.

Figure A
shows the inset/outset border effect at work. This example is produced with
some very simple code. The XHTML markup is nothing more than an unordered list
containing the button labels and links.

<body>
<div id="buttonA">
    <ul>
        <li><a href="link1.html">Button 1</a></li>
        <li><a href="link2.html">Button 2</a></li>
        <li><a href="link3.html">Button 3</a></li>
    </ul>
</div>
</body>

The CSS styles that make this
technique work are very similar to the styles for a set of plain, flat CSS
buttons. The only additions are the border-style:
outset
and border-style: inset
rules combined with a border-width
setting that’s thick enough to make the effect visible.

body {
    margin: 0px;
    padding: 0px;
}
div#buttonA {
    margin-left: 50px;
}
div#buttonA ul {
    margin: 0px;
    padding: 0px;
    font-family: Verdana, Arial, Helvetica, sans-serif;
    font-size: 12px;
    line-height: 30px;
}
div#buttonA li {
    list-style-type: none;
    height: 30px;
    width: 125px;
    margin: 20px;
    text-align:center;
}
div#buttonA li a {
    height: 100%;
    width: 100%;
    display: block;
    text-decoration: none;
    border-width: 6px;
}
div#buttonA li a:link {
    color: #000000;
    font-weight: bold;
    background-color: #CCCCCC;
    border-style: outset;
}
div#buttonA li a:visited {
    color: #000000;
    font-weight: normal;
    background-color: #CCCCCC;
    border-style: outset;
}
div#buttonA li a:hover {
    font-weight: bold;
    color: #FFFFFF;
    background-color: #999999;
    border-style: outset;
}
div#buttonA li a:active {
    font-weight: bold;
    color: #FFFFFF;
    background-color: #666666;
    border-style: inset;
}

I’ve covered the technique for
creating CSS buttons in previous articles, so I’ll just hit the high points.

The div#buttonA ul rule sets the general text size and spacing, while
the div#buttonA li rule removes the
default bullet from the list items (list-style-type:
none
) and sets the size of the button box. The div#buttonA li a rule makes the entire button clickable (height: 100%; width: 100%; display: block;),
and it’s also a convenient place to set the border thickness (border-width: 6px).

The rest of the styles control the
appearance changes for the various button states. For each pseudoclass (:link,
:visited, :hover, :active), there is a color, font-weight, background-color,
and border-style rule. All the styles use the border-style: outset rule except for the div#buttonA li a:active rule, which includes border-style: inset instead. This gives all the button states a
raised appearance—except when the button is clicked, in which case, it looks
depressed.

There are significant differences in
the way the various browsers render the inset and outset borders. Internet
Explorer creates a more subtle effect, with a highlight along the inner edge of
each border and shading toward the outer edges. Netscape, on the other hand,
renders each border in a solid color, which produces a sharper, less rounded,
look.

Technique 2: Controlling the individual sides

Using the inset/outset border styles
is a quick and easy way to simulate a 3D effect. However, it’s not your only
option. If you don’t like the standard effect, or you’re bothered by the
differences in browser rendering, then you can take control of the border
colors to produce the effect you want.

Instead of using the inset/outset
attributes of the border-style and letting the browser handle the actual color
rendering of each border, you can set the colors for each border individually
with your style rules.

Figure B
shows the results of using a style sheet that specifies the colors of each
button side individually. The markup is the same as in Figure A. Here’s the CSS
code:

body {
    margin: 0px;
    padding: 0px;
}
div#buttonA {
    margin-left: 50px;
}
div#buttonA ul {
    margin: 0px;
    padding: 0px;
    font-family: Verdana, Arial, Helvetica, sans-serif;
    font-size: 12px;
    line-height: 30px;
}
div#buttonA li {
    list-style-type: none;
    height: 30px;
    width: 125px;
    margin: 10px;
    text-align:center;
}
div#buttonA li a {
    text-decoration: none;
    height: 100%;
    width: 100%;
    display: block;
    background-color: #999999;
    border-style: solid;
    border-bottom-color: #333333;
    border-right-color: #555555;
    border-left-color: #BBBBBB;
    border-top-color: #DDDDDD;
}
div#buttonA li a:link {
    color: #000000;
    font-weight: bold;
    background-color: #999999;
    border-style: solid;
    border-bottom-color: #333333;
    border-right-color: #555555;
    border-left-color: #BBBBBB;
    border-top-color: #DDDDDD;
}
div#buttonA li a:visited {
    color: #000000;
    font-weight: normal;
    background-color: #999999;
    border-style: solid;
    border-bottom-color: #333333;
    border-right-color: #555555;
    border-left-color: #BBBBBB;
    border-top-color: #DDDDDD;
}
div#buttonA li a:hover {
    font-weight: bold;
    color: #FFFFFF;
    background-color: #777777;
    border-style: solid;
    border-bottom-color: #333333;
    border-right-color: #555555;
    border-left-color: #BBBBBB;
    border-top-color: #DDDDDD;
}
div#buttonA li a:active {
    font-weight: bold;
    color: #FFFFFF;
    background-color: #666666;
    border-style: solid;
    border-top-color: #333333;
    border-left-color: #555555;
    border-right-color: #BBBBBB;
    border-bottom-color: #DDDDDD;
}

Despite the fact that this block of
code is noticeably longer than the CSS code for the previous example, it’s not
that different. The difference is that we replaced the border-style: outset (or border-style:
inset
) rules with a border-style:
solid
rule, followed by rules to set the colors of each border individually
(border-top-color: #DDDDDD, etc.).

With this technique, you have
complete control over the colors of the sides of your buttons. This means that
it’s up to you to select the right colors to achieve the exact effect you’re
looking for—and, you’ll need to remember to swap the colors around to the
appropriate sides to produce the depressed button look for the :active button
state. The main advantages of taking control of these details are that you can
set separate side and top highlight colors, and the final result is more
consistent across all browsers.