This is my best effort at interpreting the English of the spec for border-radius into the much-more-useful mathematical notation. Below, you will find my demonstration on how the spec should work, in my oh-so-humble opinion.

The diagrams on this page use SVG, which not all browsers support. The lower diagram also used HTML5 sliders and manipulates the image with Javascript. Whether all the parts will work on your browser is far from certain.

width="500pt" height="450pt" A D C B O rv wtop rvinner rh rhinner wright E θ F θ

This diagram viewable only with an svg-enabled browser

Show your work

Code to do these calculations demonstrated below

A.x = box.width - rh

A.y = 0

B.x = box.width

B.y = rv

Origin.x = A.x

Origin.y = B.y

C.x = Origin.x + rhinner

C.y = Origin.y

D.x = Origin.x

D.y = Origin.y - rinner

θ = (π/2)(wtop/(wtop + wright)) [see note 2]

E.x = Origin.x + sqrt(1/((rv2/rh4)(tan2θ) + 1/rh2) [see notes 3, 4]

E.y = Origin.y - sqrt(rv2(1 - E.x2/rh2)

F.x = Origin.x + sqrt(1/((rvinner2/rhinner4)(tan2θ) + 1/rhinner2)

F.y = Origin.y - sqrt(rvinner2(1 - F.x2/rhinner2)


  1. From the specification: The padding edge (inner border) radius is the outer border radius minus the corresponding border thickness, e.g., rhinner = rh - border-right-width. While this matches the implementation of all the browsers that have attempted to do this, it contradicts figure 7 in the specification.
  2. From the specification: The center of color and style transitions between adjoining borders is at the point on the curve that is at an angle that is proportional to the ratio of the border widths. For example, if the two widths are equal, that point is at a 45° angle, and if one is twice the width of the other the point is at a 30° angle. The function I used meets those criteria.
  3. These equations have not been adjusted for edge cases where inner radii are < 0
  4. From the specification: The line demarcating this transition is drawn between the point at that angle on the outer curve and the point at that angle on the inner curve. This is a place where showing the math would have made things a whole lot simpler for everyone. I took that phrase to mean the point on the curve where the tangent is normal to the angle, marked as point E above. The corresponding point on the inner edge of the border is point F. The only problem is, that's ugly.

    Another possible interpretation is that they meant the points where a line of that slope that passes through the origin intersects the two curves (the green line on the diagram). The origin point would have to be adjusted for borders with thickness greater than the radius. The thing is, that can be pretty ugly, too.

    I suspect the guys who came up with the windy version of how it should work never diagrammed it with any care. If they did, I'd love to see that diagram. In fact, they should put that diagram into the spec.

width="500pt" height="450pt" A B C D G H I

This diagram viewable only with an svg-enabled browser
Depending on your browser, you may see sliders to play with below.

border-top-right-radius.v: 0
border-top-right-radius.h: 0
border-top-width: 0
border-right-width: 0
Show calculational stuff

Here's How I Would Do It

I would define the transition line as follows: A line with a slope proportional to the widths of the sides, normal to an ellipse between the midpoints of the inner and outer edges of the border. This was esaily the most aesthetic method for establishing the line that I could come up with. I'm still not entirely pleased aesthetiaclly with a couple of edge cases, but I don't think it's worth the effort to iron out every last detail.

It turned out to be easier to write code to calculate the points than to crunch the numbers for a specific case, and since I already had this nice little SVG image, I wrote Javascript to set the locations of the points on the graph.

Ho ho! Now that the image is driven by data, the next obvious step was to make sliders that changed the values of the inputs. Now it moves! My first venture into using Javascript to alter an image at runtime. Of course, being able to play with the sliders brought up all the boundary conditions, so I put them into the code as well. (There is one edge case my code still doesn't cover. Can you find it?)

This code snippet won't handle different border styles, but for solid borders it does better than any current browser. It would be very simple to implement most border styles (dashes would be tricky).

Why is it I enjoy fiddling with the sliders so much?


  1. I made heavy use of the transformations in SVG graphics tags. I move the origin and invert screen coordinates so all my calculations can be based on normal Euclidian conventions. I could have used the transformations to simplify the math further, when it came to calculating the intersections of lines and ellipses.
  2. You can see the code by getting the source of this page. If you use it, I'd appreciate a nod of credit. Not that this code is good for much except getting me deeper into SVG and tempting me to go in and fix Webkit even though someone else is already working on it. The coding style is made for easy reading rather than high performance, but works as a reference implementation.

Hire Jerry Seeger

Copyright ⓒ 2010 Jerry Seeger
All Rights reserved until I get that creative commons thing figured out.