The contents of this post are a few years old now and I can’t recommend them as best practice. If you’re tempted to use this technique then bear in mind the fact that Opera 10.5 and Internet Explorer 9 now support the CSS3 border-radius property. Supporting old versions of Opera is rarely worth the effort so I would use if ($.browser.msie && $.browser.version < 9) to apply this technique to old IE versions only.

Picking up from my first attempt here’s a more methodical approach.

Objectives

  1. Buttons (both <input type="submit"/> and links masquerading as buttons that have rounded corners.
  2. Works in recent versions of Gecko, WebKit, Opera and Internet Explorer
  3. As little extra mark up as possible
  4. Any JavaScript used must be generic and not tied to one particular style of button, i.e. change element.className not element.style

HTML + CSS

Let’s start with the standards based solution that works in Gecko and WebKit based browsers.

<input class="button-rounded" type="submit" value="Go"/>

<a href="#" class="button-rounded right">Go

.button-rounded {
  font: bold 100%/1 Verdana, sans-serif;
  text-decoration: none;
  background-color: #2e4c37;
  color: #fff;
  border: 2px solid #fff;
  border-radius: 4px;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;
}

As you can see from Example 2A this works very nicely in Gecko but has a few issues with padding and line heights in WebKit. If you examine the code of the example you’ll see some extra styles to tweak the padding so that the button and the link look the same (at least in Firefox) and also to produce examples where the buttons are floated (this variation is widely found on the web and also through up some problems during testing).


Add elements via JavaScript

As prevously we’re now going to use JavaScript (in the example code jQuery) to wrap the button in a span and then insert four extra elements into that span. We also add a new class to the button itself.

if($.browser.msie || $.browser.opera)  {
  $('.button-rounded').addClass('wrapped').wrap(
    '<span class="button-rounded"></span>'
  );
  $('span.button-rounded').append(
    '<i class="tl"></i><i class="tr"></i><i class="bl"></i><i class="br"></i>'
  );
};

.button-rounded.wrapped {
  border: none;
  background: transparent none;
  float: none;
  margin: 0;
}
span.button-rounded {
  position: relative;
}
span.button-rounded i {
  position: absolute;
  width: 4px;
  height: 4px;
  background: url(corners.png) no-repeat;
}
span.button-rounded i.tl {
  top: -2px; left: -2px;
  background-position: 0 0;
}
span.button-rounded i.tr {
  top: -2px; right: -2px;
  background-position: -4px 0;
}
span.button-rounded i.bl {
  bottom: -2px; left: -2px;
  background-position: 0 -4px;
}
span.button-rounded i.br {
  bottom: -2px; right: -2px;
  background-position: -4px -4px;
}

This removes most of the styles from the button and applies them to the wrapping span – this helps a lot as spans are more predictable than inputs.

But a quick look at Example 2B reveals that the floated elements don’t seem to be in the right place anymore. We forgot that there are two classes and hence two sets of styles on these buttons.

$('.button-rounded.right').removeClass('right').parent('span').addClass('right');
$('.button-rounded.left').removeClass('left').parent('span').addClass('left');

Okay, Example 2C is a step in the right direction but there are still a number of issues. In IE the floated buttons are two wide and the inline input is missing its bottom border. In Opera the two inline examples have a problem with the location of the right side corners.


Fine Tuning Internet Explorer

Add display: inline-block; to the styles for .button-wrapped. Simple and works brilliantly, Example 2d.


Fine Tuning Opera

As the browser with the smallest market share we’ve left Opera to last and hence it’s been the victim of a few CSS style choices that have been made with IE and Firefox in mind. We could deploy some CSS hacks to feed it different styles but this time I decided to use JavaScript instead.

if ($.browser.opera) {
  $('input.button-rounded').addClass('opPad');
  $('span.button-rounded').each( function() {
    if($(this).css('float') == 'none') {
      $(this).children('i.tr, i.br').addClass('opNoFlo');
    };
  });
};

input.button-rounded.opPad {
  padding: 1px 5px 2px 5px;
}
span.button-rounded i.opNoFlo {
  right: -4px;
}

So there we are, Example 2e.


If you want a hover effect on your buttons just add .button-rounded:hover and change the background and border properties. It may be easier to change the span.button-rounded:hover i background image but it’s probably better practice to change the background-position values instead and use a single corners.png as your CSS sprite holder for both normal and hover states. (Or take the easy way out and just change the text colour as I did in the last example.)

If you have multiple styles of buttons then use contextual selectors to set different CSS whilst keeping the JavaScript common across the entire site. In a real world case I have a common style that gets changed for a few <form id="foo">.


Now for the bad news. I’ve tested this in IE 7, FF 3, Opera 9.6, Safari 3, Chrome 0.4.154.22 on Windows XP. I’ll be testing in IE 6 and IE 8b2 shortly but would appreciate feedback regarding other platforms and browsers.

2 Comments

  1. Stumbled on this JQuery Corner Demo which shows a really nice and flexible way to create rounded corners (or not even rounded) for boxes. The only potential problem here is: is this more CPU cycles than using the image/CSS tricks? Probably worth a very good try if you are already using jQuery on your sites. Regardless, worthy of a bookmark – especially if you need rounded corners at 2am in the morning.

    • Steve Pugh says:

      The link you included didn’t go to anything related to jQuery Corners. I suspect you meant http://www.malsup.com/jquery/corner/ but somehow substituted a link to a poker site instead. Whoops.

      Regarding CPU cycles, as with all these things the efficiency of the selector is the key. If you have just one rounded button per page then $(“#id”) will be as efficient as you can get. With multiple sets then $(“input.class”) is good but $(“#id input.class”) is better if possible. See, for example, http://www.ibm.com/developerworks/web/library/wa-aj-advjquery/

      Adding a few elements to the DOM and switching some classes is probably more efficient than adding the many elements to the DOM that the jQuery Corners method uses.

      Finally, the jQuery Corners method can’t easily create multi-colour corners like the ones used in my example.

Leave a Comment


(will not be published unless you behave like a spammer or a troll)