Drawing circular shapes in CSS is hard, since the only way to do so is by using border-radius: 50%, which only gets you so far - in particular when you’re trying to draw arcs. It’s tempting to resort to SVG, but that brings with it scaling trickery which we can avoid by sticking with pure HTML. In this tutorial, we’ll show you a trick to draw a circular progress arc using skew transformations.

What we’re going to make

Circular Progress

Clipping circle

We’ll be using several skewed rectangles to create our arc - more on that in a bit - and we’ll need a circle to clip them with. Let’s start off by creating a circle using the old border-radius:50% to round a div. We’ll add overflow:hidden to the circle to have it clip any elements we place within it.

See the pen on CodePen.

Segment

We can now use a rectangle to represent a quarter-circle segment. We place the rectangle’s top-left corner in the center of the circle, and make it very large so that we can be sure that the circle clips it:

See the pen on CodePen.

Interestingly, this rectangle is all we need to draw a segment anywhere between 0° and 90° in size. We can apply a skew transformation to it to squeeze it from 90° (unskewed) all the way to 0° (skewed to a line). Of course a single segment of at most 90° is not enough to represent a full circle, but we can simply create four copies of this rectangle and rotate them so that we can cover 360°. Here are several rectangles in different positions and with different degrees of skewing:

See the pen on CodePen.

With four copies of the rectangle, rotated by 0°, 90°, 180° and 270°, we can cover a complete circle. That way, we can set their skew degrees individually and draw any arc from 0° to 360°

See the pen on CodePen.

Inner circle

By overlaying a smaller inner circle on the main circle, we can turn our circle into an arc, and we’ll have a place to print the progress value.

See the pen on CodePen.

Finishing touches

The endings of the arc line can be rounded by creating small dots (div elements with our friend border-radius:50%, again) and placing them exactly where the line starts and ends.

A fuller implementation with animation can be found as part of TypeUI. Here is the CircularProgress implementation.