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 Comments.

  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?

  3. Adam, that error message means that you have outputted something before the headers could be sent, Make sure there are no spaces before the opening <?.

    And – try this one instead: curved borders with valid css and un-obtrusive javascript.

%d bloggers like this: