Create Hybird Graphical/CSS Buttons Traditional graphic buttons and the newer buttons you can create with pure CSS-styled text each have their advantages and disadvantages. However, a hybrid technique that uses CSS-styled text over a background image may be the best of both worlds. Graphical buttons vs. CSS Graphic buttons give you great design flexibility but require you to create and load separate image files for each button. If you want to create rollover effects, you must create additional images for each state of each button and preload those images upon page load. CSS buttons are much faster to create and to load because they rely on plain text, simple markup, and a few CSS rules--no images are required. The problem is that pure CSS/text buttons are limited to simple rectangles and flat colors, which severely restricts your design options. Introducing the graphical/CSS hybrid button You're no longer forced to choose between CSS-button speed and graphical-button appearance. Instead, you can replace the plain, solid-color background of a CSS-button with a background image of a button face and superimpose the CSS-styled text as the button's text label. The background image can have whatever texture, shading, bevel, and rounded corner effects you want--just like a graphical button image. The result is a button that has the dimensional appearance of a traditional graphical button, while still retaining almost all the speed advantages of a CSS button. Creating these hybrid buttons is much faster and easier than creating traditional graphic buttons because you only need to create one graphic image for the blank button face. The same image can be used for all the buttons on your page. Even if you want to alter the background image for the various rollover states, you only need to create one image for each state--not separate images for each state of each button. Therefore, the graphics are faster to create and much faster to load. And because the button face image doesn't contain any text, you can probably use higher compression settings than you could with traditional graphic buttons. Except for the substitution of a background image for a flat background color and border, the rest of the technique for creating hybrid buttons is the same as for creating any other CSS button. You'll probably want to use the display: block property to make the entire button area clickable instead of just the linked text of the label button. Here's sample CSS for buttons that use the hybrid technique: div#menu { height: auto; width: 150px; } div#menu li { height: 40px; width: 125px; margin: 10px; font-family: Arial, Helvetica, sans-serif; font-size: small; text-align: center; line-height: 200%; list-style-type: none; background-image: url(images/buttonface-up.jpg); background-repeat: no-repeat; } div#menu li a { display: block; width: 100%; height: 100%; text-decoration: none; } div#menu li a:link { color: blue: } div#menu li a:visited { color: #900; } div#menu li a:hover { font-weight: bold; color: red; } div#menu li a:active { font-weight: bold; color: red; background-image: url(images/buttonface-down.jpg); background-repeat: no-repeat; } Here's the markup for three stacked buttons:
This example uses one background image (buttonface-up.jpg) for the link, visited, and hover button states, and another image (buttonface-down.jpg) for the active state. The background images take the place of the background color and border properties that would normally define the box shape around the button text. In this case, the link, visited, and hover states are defined solely by the text color, but you could alter the background images for each of those states if you want. In order for the technique to work properly, the size of the background image must exactly match the box size as specified in the div#menu li rule, which in this case is 40px X 125px. Also, you need to ensure that the button text fits within the specified box size without causing the box to expand. Just in case an overly long text label does cause the box to expand beyond the specified size, the background-repeat: no-repeat property will prevent the background image from being repeated to fill the expanded box. Now you can create buttons that have the speed of pure CSS text buttons but also posses the dimensional look of traditional graphic buttons. About the only design compromise you must make with this hybrid button technique is to use regular Web fonts for the button text. The small size of button text usually limits the font selection anyway, so that isn't much of a sacrifice. Creating a button by superimposing CSS-styled text over a graphical background image combines the speed and lightweight markup of CSS rollover effects with the rich dimensional appearance of a graphical image of the button face. These graphical/CSS hybrid buttons are faster to create and load than regular graphical buttons because you need to create and load only one image for the blank button face, rather than separate images for each button. The same image serves as the background for all the buttons on your page. The text labels for the buttons are simply CSS-styled text. The problem with preloads One of the few problems with graphical/CSS hybrid buttons is that there are some limitations on rollover effects. The simplest way to employ the technique is to confine your rollover effects to the CSS-styled text and use the same button face image for all of the rollover states. This gives you fast, clean rollover effects, but it somewhat limits your design options. You can also create alternate button images and construct your CSS rules to change the background image for the different rollover states. While this gives you more design flexibility, there's a delay in the appearance of the alternate button images as the browser loads the image file the first time it's used (unless you resort to preloading the alternate button images). The techniques for preloading images are well known and accepted. The problem is that preloading images adds to the time it takes for the page to load and appear in the visitor's browser. Those first seconds of the user experience are critical, and anything you can do to make the initial page load faster improves the site's perceived speed and usability. Using graphical/CSS hybrid buttons reduces the time required for image preloads, but it would be even better if preloads could be eliminated. No-lag rollovers—without preloading images I've found an innovative technique that enables you to have alternate button images for the different rollover states without needing to do any separate preload to any image files. I first encountered this technique in an article on the Web site of Czech Web designer Petr Stanicek. To achieve a typical button rollover effect, you'd normally create three separate images as shown in Figure A—one for the plain button, one for the hover state, and one for the active state. The image file for the plain button face would be part of the initial page load, but the other two images would have to be preloaded separately in order to have them available in the browser's cache when the visitor points to or clicks the button. The no-preload rollover technique works its magic by combining all three button face images into a single image file, as shown in Figure B. Then, instead of specifying different background image files in the CSS rules for each rollover state, you specify a position offset for the composite image. The composite image file loads automatically during the initial page load, so there's no need for preloads; there's also no delay while the browser fetches a different image file in order to render the rollover effect. The combination of an oversized image and background image position offsets enable you to selectively show a different portion of the image file for each button state instead of displaying different image files. The size of the CSS box around the button text determines how much of the background image shows through in the browser. The portion of the image that falls outside the box is clipped and hidden from view. The result is that only one of the three button faces shows at a time. For this technique to work, you must know the exact horizontal and vertical size of the button face and use those dimensions precisely in creating the image file and the CSS rules for the buttons. A working example Listing A is the CSS code for an example of the no-preload rollover technique applied to graphical/CSS hybrid buttons. In this case, the button text is marked up as an unordered list, and the three stacked buttons are contained within a div with the id menu. Here's the HTML for the three buttons: In the working sample code, note that the div#menu li rule specifies the height and width of the button box, as well as the starting properties for the background image and text. Specifying the line-height to match the height of the box assures that the text will be centered vertically. The background-position attribute controls the position of the upper-left corner of the background image relative to the upper-left corner of the box. A value of 0px 0px positions the upper-left corner of the image to match the upper-left corner of the box. In the div#menu li rule, the display: block property, in combination with width: 100% and height: 100%, make the full box area clickable instead of just the text. Finally, in the rules for the individual button states (link pseudo-classes), such as div#menu lia:link, the color and font-weight properties create the text rollover effects, while the background-image and background-position properties do the same for the background image. Setting background-position: 0px -30px for the hover state slides the background image up 30 pixels so that the top button face is hidden and the middle (highlighted) button face appears in the button box. Similarly, setting background-position: 0px -60px for the active state slides the background image up even further to display only the bottom (depressed) button face. The background-image needs to be repeated in each rule. This technique allows you to achieve the dimensional look of traditional graphic buttons, combined with the speed of CSS text buttons that you get from graphical/CSS hybrid buttons. It also lets you have full-fledged graphical rollover effects without delays or image preloads. 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. 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. Controlling 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. You're probably already aware that you can create CSS rollover effects by defining separate styles for each state of the link—link (normal), visited, hover, and active (clicked). And, you probably also know that the order of the CSS styles makes a difference; styles that come later in the CSS code override earlier styles that apply to the same element. The sequence of the styles that create the rollover effect is particularly significant. Let's take a look at how to arrange the link-state styles to support the normal rollover effect without conflicts, and also how to rearrange those styles to achieve variations on the rollover effect. Link states, by the book The typical CSS rollover effect relies on separate styles for each of the four states of a hyperlink. You create styles for the (hyperlink) tag with CSS pseudoclasses to address the states. a:link—normal, unvisited hyperlink a:visited—visited hyperlink a:hover—hyperlink as the visitor's cursor passes over it a:active—clicked hyperlink For the typical CSS rollover effect to work properly, it's important that the CSS styles come in this particular order in the CSS code, whether it's an external stylesheet or style rules embedded in the header of the HTML page. The a:link style comes first because it applies to all links. The a:visited style is next; it overrides the formatting of the a:link style for any visited links. (If the a:link style followed a:visited, the a:link style might override the a:visited style.) The a:hover style is next; it applies only to the link under the visitor's cursor. The a:active style is last so it can override all others when a link is clicked. The following CSS creates the rollover effect shown in Figure A. a:link { color: #0000FF; text-decoration: underline; font-weight: normal; font-style: normal; } a:visited { color: #3399FF; text-decoration: underline; background-color: #FFFFFF; font-weight: normal; font-style: italic; } a:hover { color: #0000FF; text-decoration: underline; background-color: #FFFF00; font-weight: bold; font-style: normal; } a:active { color: #FF0000; text-decoration: none; background-color: #CCCCCC; font-weight: bold; font-style: normal; } Variations on a Theme The sequence of styles in the CSS code establishes how each style takes precedence over others, where more than one style could apply to a specific element. The a:hover style normally follows both the a:link and a:visited styles so the styling for the hover state can apply to both regular and visited links. However, it doesn't have to be that way. You can alter the order of the styles to achieve different effects. Suppose you want a rollover effect on unvisited links, but don't want the effect to apply to visited links. You might think you'd need a script to handle such situational formatting, but all you really need to do is rearrange the CSS code. To remove the rollover effect from visited links, simply move the a:visited style so that it follows the a:hover style instead of preceding it. Changing the order of the styles in the CSS code shown below changes the rollover effect on visited links, as shown in Figure B. a:link { color: #0000FF; text-decoration: underline; font-weight: normal; font-style: normal; } a:hover { color: #0000FF; text-decoration: underline; background-color: #FFFF00; font-weight: bold; font-style: normal; } a:visited { color: #3399FF; text-decoration: underline; background-color: #FFFFFF; font-style: italic; font-weight: normal; } a:active { color: #FF0000; text-decoration: none; background-color: #CCCCCC; font-weight: bold; font-style: normal; } Notice that the a:visited style includes rules to define all the same attributes as the a:hover style. Otherwise, any attributes of the a:hover style that weren't specifically overridden by the a:visited style would continue to appear when the visitor's cursor passed over a visited link. Adding another wrinkle The fact that previous styles continue to apply to an element unless specifically overridden by a subsequent style creates another opportunity for altering the normal rollover effect. Careful selection of the attributes and order of the link-state styles can enable you to create somewhat different rollover effects for unvisited and visited links. For example, simply deleting the background-color: #FFFFFF; rule from the a:visited style in the second code example above would allow the background color from the a:hover style to apply to visited links. The result is a rollover effect for unvisited links that adds a background color and makes the text bold. The rollover for visited links, on the other hand, includes the same background color change, but the text retains the style and weight of the visited links (see Figure C).