29 Mar

how the border-radius hack works

Okay; very late on Sunday, I posted about a curved borders hack to end all (or most) curved border hacks. I was a bit bushed after all the work, so was too tired to explain the hack.

First, a short description of it, for all you people that didn’t read the post:

This hack is a javascript & php script which you simply link to in your <head> section as you would any other external javascript. From then on, you write plain CSS. If you want to give an element a curved corner, then simply specify it in the CSS!

p{
 background:#666;
 padding:10px;
 border:2px solid #333;
 border-radius:30px;
 border-top-left-radius:0;
}

The above would render such as this:

example image

This will work in practically every browser. Don’t believe me? Here’s the test page, and here’s the CSS for that page.

As for native ability – as far as I know, there is only one single browser that can do curved borders.

That browser is, of course, Firefox; the greatest browser in the world.

Unfortunately, not even that great god of a browser gets it all right:

example image

In the example image (which is rendered using Firefox’s native rendering engine – try for your self (Mozilla browsers only)), you can see that the background image is leaking.

I’m currently compiling a build of Mozilla to see if I can get working on a fix for that (unlikely, as my C++ is woeful, but I will try…).

Anyway – look in the example with your own browser and you will see that your own browser can now display it properly.

So, how was it done?

It was a three-step process:

  1. get the css and parse it.
  2. for each element which has a border-radius, remove the border from the element, correcting the size of the element by adding the missing border widths to the elements, and giving the element a custom background image which fakes the border.
  3. create the image

It’s surprising (and there’s probably a universal law that explains it), but the shorter a problem’s description, the more difficult it usually is. Of the three items above, the easiest was the second.

Grabbing the CSS

I spent a week researching how to grab CSS straight from the source before the browser parses it. I gave up after a good read through the W3C’s DOM specs turned up nothing.

Suddenly, the answer was clear: XMLHttpRequest! Why not just grab the actual source, instead of trying to access it through some non-existant DOM interface.

Actually, once I thought of the solution, it was pretty simple… it’s just that I spent so long trying to figure it out that I was damned frustrated.

Parsing the CSS and fixing the elements

Nyeh – read the source and figure it out for yourself đŸ™‚ It’s pretty self-explanatory.

Dean Edwards, as usually, was a life saver. His cssQuery code made it a cinch to grab arrays of each affected element.

The background image

I’m not really a graphical coder, so writing the background painter involved a bit of thought for me.

Basically, the solution, though, was pretty simple again, after I’d worked it out.

You need to paint two images – the first one is a mask, and the second is the border. The mask is for figuring out what should be transparent and what should not.

First, you build the mask. This involves painting a rectangle in a certain colour (eg; black), and then painting the border in another color (white). If the element is supposed to have a background image or colour, then you need to fill in the space between the borders.

Then, you paint the real image. This involves first painting a rectangle in the element’s background colour (if specified), then tiling the background (again if specified).

Once the two images are done, you just go through the mask, a pixel at a time, and whenever a “transparent” indicator (black, in this example) is found, the corresponding pixel in the real image is painted “clear”.

Conclusion

As far as I know, this is a pretty foolproof method – if JavaScript is not enabled, then the browser simply relies on its own rendering capabilities.

There are improvements that could be made – the script has not been tested with multiple colours, and I haven’t coded any border styles (dashed, dotted, etc) into it.

If there is interest in the script, then I may improve on it.

3 thoughts on “how the border-radius hack works

  1. Fascinating… it took several seconds to render the sample page with Firefox 1.0.2, 2.8GHz P4, 512MB RAM, and a very fast LAN. It wins the prize for innovative rounded corner solutions but I’ll stick with images until IE catches on and does it like Firefox.

  2. I copied the code over the my server exactally to test it out but keep getting this error:

    Error: invalid flag after regular expression
    Source File: /border-radius.php
    Line: 2, Column: 11
    Source Code:
    Warning: Cannot modify header information – headers already sent by (output started at /border-radius.php:1) in /border-radius.php on line 3

    Do any particular server settings need to be set for this to work?

Comments are closed.