CSS3 Rounded Corners and Gradients (and IE9's Nonsense)

CSS3 Rounded Corners and Gradients (and IE9's Nonsense)

Tutorials css3 ie9 tutorial vendor-prefixes web-design

So I spent most of last night fighting with a client site that "has to look right in IE9" (their words, not mine) and figured I'd write up what I actually did, because I know I'm not the only one still dealing with this mess. CSS3 is great and all, but browser support in November 2011 is still a patchwork quilt, and if you want rounded corners and gradients that actually look consistent across Firefox, Chrome, Safari and IE9, you can't just write one line and call it done.

Rounded corners are basically fine now, but prefix anyway

border-radius works unprefixed in Firefox 4+, Chrome, Safari 5, and yes, IE9. That's the good news. The bad news is there's still a pile of Firefox 3.6 and older WebKit browsers out there (older Android stock browsers especially — don't get me started on Android fragmentation, that's a whole separate rant), so I still write it three times out of habit:

.box {
  -webkit-border-radius: 8px;
  -moz-border-radius: 8px;
  border-radius: 8px;
}

Yeah it's redundant in a modern browser. I don't care, it costs nothing and it saves me from getting a bug report from someone running Firefox 3.6 on a five-year-old work laptop, which happens more than you'd think.

Gradients are where it gets annoying

This is the part that actually ate my evening. There is no unprefixed gradient syntax you can rely on right now. None. You've got -webkit-gradient() (the old ugly syntax), -webkit-linear-gradient() (the newer, saner one), and -moz-linear-gradient() for Firefox. All three have slightly different argument order and syntax, which is exactly as fun as it sounds at 11pm.

Here's roughly what I ended up with for a simple top-to-bottom button gradient:

.button {
  background-color: #4a90d9; /* fallback for everything else */
  background-image: -webkit-gradient(linear, left top, left bottom, from(#6fb1e8), to(#3a7bc8));
  background-image: -webkit-linear-gradient(top, #6fb1e8, #3a7bc8);
  background-image: -moz-linear-gradient(top, #6fb1e8, #3a7bc8);
}

Order matters here — put the plain background-color first so anything that chokes on the gradient syntax just falls back to a flat color instead of rendering nothing.

And then there's IE9

IE9 does not do CSS gradients. At all. No prefix, no filter, nothing built in. If you came from the IE6/7/8 world you probably remember the old filter: progid:DXImageTransform.Microsoft.gradient(...) trick, and technically it still works in IE9 for backward compatibility, but Microsoft's been telling people to stop using it since it's slow and janky and doesn't play nice with rounded corners (it'll draw square over your nice curved edges, which looks awful).

What I actually did was just accept the flat background-color fallback for IE9 and moved on with my life. Client wasn't thrilled, but I made the point that IE9 users are already getting most of the site correctly, rounded corners and all, and a flat blue button instead of a gradient one isn't exactly a crime against usability. If you really need the gradient in old IE, there's a tool called CSS3 PIE that uses a .htc behavior file to fake border-radius, gradients and box-shadow in older IE, but it's a genuine pain to set up correctly and I've had it silently fail on nested elements before. Use it if you have to, but test it thoroughly first.

Quick sanity list

A couple things that bit me and might save you some time:

  • Firefox wants the color stops written the same way as WebKit, but double-check anyway, the parenthesis placement is different enough to break things silently.
  • Always put your solid background-color before the gradient declarations, never after.
  • Test rounded corners with a border, not just background-color, because some older WebKit builds render corner clipping differently when there's a visible border versus not.
  • Don't bother with the unprefixed background: linear-gradient(...) syntax yet. It's coming (it's in the newer spec drafts) but nothing ships it unprefixed right now.

None of this is glamorous work. It's the kind of thing you do once, copy into a Sass mixin or a snippet file, and never think about again until some new mobile browser shows up wanting its own prefix. Which, knowing how this year has gone, is probably next month.