Styling form buttons

By John Faulds

Anyone who's used the web has encountered buttons in forms. Buttons, as with most form controls, can be a bit tricky to style for consistent look cross browser and cross platform as the operating system often has more to do with how they are rendered than the browser itself. Roger Johansson has delved into this issue in more detail.

What I'm going to do here is present a few different options available for styling your buttons.

There's two ways to add a button to your HTML: using either the <input> tag or the <button> tag. The most commonly used is probably the <input> although I think, and will show here, how the <button> can be more flexible.

First, let's look at the two options sans-styling:

<input type="submit" value="Submit" />

<button type="submit" value="Submit">Submit</button>

In most OSes, when you roll over the buttons, there will be a slight colour change, but what else can be done to make them stand out?

I’m going to be using inline styles to illustrate some examples, but ordinarily all these styles would be moved to your stylesheet.

These styles will also apply equally well to both inputs and buttons. If you’ve applied styles to other inputs on your page, you’re going to need some mechanism to override these styles for your input buttons. For browsers, other than Internet Explorer, we can use input[type=submit] { our rules; }, but for full cross browser compatability we either have to use a class on the input in the HTML or use unobtrusive javascript to automatically attach a class to all submit inputs which we then use in the CSS. This is one advantage of using buttons – they have their own tag so can be targetted directly in your stylesheet without the need for any additional classes or IDs.

Changing button colour

We can change the colour of the button’s text and we can make it bold too:

button {
  color: #900;
  font-weight: bold;
}

Making the text bigger

We can, of course, also make the text size much bigger and we could also uppercase the text:

button {
  color: #900;
  font-weight: bold;
  font-size: 150%;
  text-transform: uppercase;
}

We can also change which font is used and the letter-spacing of the text.

Changing the background

All the changes suggested so far deal only with the text and not the background of the button itself. We can change a button’s background or border too, but when this happens, the button will lose its 3D look and become a solid rectangle.

button {
  color: #FFF;
  background-color: #900;
  font-weight: bold;
}

button {
  color: #900;
  border: 1px solid #900;
  font-weight: bold;
}

Hover effects

In his article on styling form widgets, the Man in Blue, Cameron Adams warns against styling buttons in this way and taking away the user’s ‘perceived affordance’ so they will not recognise how they interact with an element, or take longer to do so. It’s certainly a valid point, but a couple of years later, I think web users are more used to different types of buttons and this may not be such a problem as it was.

But to make sure your users understand the button is in fact a button and not just another form input (or any other rectangle on the page), there’s a few things we can do.

In the unstyled example, the button’s background changed colour slightly on rollover. We can do the same thing here:

<button class="btnExample" type="submit" value="Submit"/>Submit</button>

/* CSS */
.btnExample {
  color: #900;
  background: #FF0;
  font-weight: bold;
  border: 1px solid #900;
}

.btnExample:hover {
  color: #FFF;
  background: #900;
}

We could also change the cursor to a hand to make it look more like something that should be pressed:

.btnExample:hover {
  cursor: pointer; /* cursor: hand; for IE5 */
}

As noted in the Man in Blue example above, we can give the button a double-edged border:

.btnExample {
  border: 3px double #FC6;
  border-top-color: #FC9;
  border-left-color: #FC9;
}
.btnExample:hover {
  border-color: #F00 #C30 #C30 #F00;
}

All the :hover examples above require the use of javascript to work in IE6 and lower as those browsers only support :hover on anchors. Some would argue that you shouldn’t mix CSS and javascript to create certain effects, that it should be either one or the other. IE7 is going to support :hover on all elements, so for this type of thing, it’s a case of using javascript as a work-around for incomplete CSS support in older browsers which I think is acceptable.

So far, we’ve just used a solid background-color for the buttons, but we can use background-images just the same.

.btnExample {
  background: #FFC url(/examples/images/buttonbg.png) repeat-x;
}
.btnExample:hover {
  background-color: #900;
  background-position: 0 -24px;
}

Adding images to buttons

So far, we’ve changed the button’s text and background, but we can also add some imagery to the button. Except we can’t use an input; this is where another of the button‘s advantages comes into play. Unlike inputs which are inline-replaced elements, buttons can contain other content which can include images.

<button class="btnExample" type="submit" value="Submit"><img src="button-arrow.gif" width="18" height="18" alt=""/>Submit</button>

/* CSS */
button img {
  margin-right: 5px;
  vertical-align: middle;
}

* html button { width: 90px; }

IE adds extra space to the left and right of the content when an image is placed in the button so I’ve used the Holly Hack to give it a width.

Replacing buttons with images

Now, what if we wanted to go a step further and replace the whole button with an image so that we can give it a different shape or use a particular font of our choosing? The most straightforward way to do this is to use an input with type set to image.

<input type="image" src="submit-button-off.gif" alt="Submit details" />

Note the use of the alt attribute to describe what the input does. This is important for people who browse with images off because otherwise they wouldn’t see the button.

This looks like a button but doesn’t have a rollover effect like a non-styled button or the examples we’ve just been looking at. You’ll notice the name of the image is submit-button-off.gif and that’s because there is also a submit-button-on.gif which we can use for creating a rollover effect with image inputs. As the image is actually in the HTML, the only way to change it with user interaction is to use javascript.

We could attach the onmouseover and onmouseout events to the HTML but unobtrusive javascript is preferable to maintain our separation of content and presentation.

The script below is based on one by Christian Watson but I’ve trimmed it back because I’m not targetting all images and not supporting older browsers.

function buttons() {
  var inputs = document.getElementsByTagName("input");
  var j=0;
  for (j=0; j<inputs.length; j++) {
    if(inputs[j].getAttribute('type') == 'image') {
      var image = inputs[j];
      image.offImage = new Image();
      image.offImage.src = image.src;
      image.onImage = new Image();
      image.onImage.imageElement = image;
      if (navigator.userAgent.toLowerCase().indexOf('safari') != - 1) {
        image.onmouseover = function() { this.src = this.onImage.src; };
        image.onfocus = function() { this.src = this.onImage.src; };
        image.onmouseout = function() { this.src = this.offImage.src; };
        image.onblur = function() { this.src = this.offImage.src; };
      } else {
        image.onImage.onload = function() {
        this.imageElement.onmouseover = function() { this.src = this.onImage.src; };
        this.imageElement.onfocus = function() { this.src = this.onImage.src; };
        this.imageElement.onmouseout = function() { this.src = this.offImage.src; };
        this.imageElement.onblur = function() { this.src = this.offImage.src; };
      };
    }
    image.onImage.src = image.src.replace(/-off\./, '-on.');
    }
  }
}
window.onload=buttons;

The script finds all inputs on the page and then determines if they’re of type=image. It then uses the image’s src to create the variables for both on and off states by simply replacing off with on in the image name. Then it’s just a matter of applying the correct state onmouseover and onmouseout; onfocus and onblur are for keyboard users. (Safari also has a problem with onload for off-screen images which is why the conditional statement sets up a different set of functions for it.) Which gives us this (HTML is same as the previous example):

Using CSS image replacement

There is a non-javascript alternative and this is where the <button> comes into play again. As the button can contain other content, we can use an image replacement technique on it. My preferred method is the Gilder Levin Ryznar Jacoubsen IR technique which I’ve used on my article on combining image replacement with Suckerfish dropdowns.

<button type="submit" value="Submit button" class="IR"
  id="IRbutton"><em></em>Submit button</button>

/* CSS */
.IR {
  position: relative;
  overflow: hidden;
  font-size: 1em;
}

.IR em {
  display: block;
  position: absolute;
  top: 0; left: 0;
  z-index: 1;
}

button#IRbutton {
  background: none;
  border: none;
  float: left;
  display: inline;
}

#IRbutton:hover { cursor: pointer; /* cursor: hand; for IE5 */ }
#IRbutton, #IRbutton em { width: 83px; height: 26px; }
#IRbutton em { background: url(/examples/images/submit-button.gif) no-repeat; }
#IRbutton:hover em, #IRbutton:focus em { background-position: -83px 0; }

/* for ie5.x/mac only */
* html>body .IR {
  position: static;
  overflow: visible;
  font-size: 10px;
}
* html>body .IR em { position: static; }
* html>body #IRbutton em { margin-bottom: -26px; }


 

There’s one notable difference between this and the Gilder Levin Ryznar Jacoubsen method and that’s that the background-image isn’t attached to the parent element, the button, as well. To do so causes the rear image to be visible by a few pixels behind the front image.

The float: left and display: inline on the button itself are also required by Opera 8 & 9 to ensure that they render the image at all (v9) and in the right place (v8) (thanks to Paul O’Brien for the tip).

The actual image for the button contains both on and off states and looks like this:

Using the button’s background-position property we can move the off state out of view and replace it with the on state when the image is hovered over (see above for the extra help that IE needs on this). With images turned off, the user will still see the button’s text making this a better option than image replacement methods that move the text off-screen.

So we can use images that look like buttons but still have our own style about them at the same time as retaining the user interaction element by enabling change of state when the mouse is hovered over the button or it is tabbed into using the keyboard.

Update

Mon, Feb 18, 2008 @ 9:41 am

I’ve begun using JQuery on this site recently and as such have replaced the script above with this:

$(document).ready(function() {
	$('input[type="image"]').hover(
		function () { $(this).attr("src", $(this).attr("src").split('-off').join('-on')); },
		function () { $(this).attr("src", $(this).attr("src").split('-on').join('-off')); }
	);
});