How to Create a Circular Progress Button

A tutorial on how to implement the circular progress button concept by Colin Garven. We are using the SVG line drawing animation technique as described by Jake Archibald to animate the circular progress and provide a success and error state to indicate the final status.

Today we are going to show you how to implement a nifty progress button concept. The concept is the fabulous Submit Button by Colin Garven. Take a look at it first to get an idea of what steps need to be done, and enjoy the animation. The idea behind the button is the following: once clicked, the submit button is transformed into a circle that will show a progress animation using its border. When the progress animation is finished, the button will expand again and show a checkmark to confirm that the submission is complete, as Colin mentions in a comment. We are going to implement this concept and add another state for the case when the submission fails.

There are quite some possibilities for creating this button and the effect. When thinking about CSS-only techniques, the most challenging part would be the progress circle. There is a smart technique using the clip property for achieving the effect and Anders Ingemann wrote an excellent and very complete tutorial on it (he uses LESS). But we are going to implement this beauty using an SVG based technique with CSS transitions and some JavaScript. For the progress circle, the checkmark and cross in particular, we’ll make use of the animated line drawing technique explained by Jake Archibald.

Note that animating SVGs can be problematic for some browsers so it might not work everywhere as expected. These kind of techniques are still in their infancy so consider this tutorial as an experimental exercise that might come in handy for future implementations. ;)

So, let’s get started!

The Master plan

If you have carefully observed Colin’s Dribbble shot, you might have noticed that we’ll need to take care of several states of the button. The interesting part is the transition from one state to another.
First, we want to show a simple button with a transparent background and a colored border. When we hover over the button, we want it to get filled with the border color and the text should become white.

When we click on the button (in order to, for example, submit a form), we want to fade out the text, decrease the button’s width to become a circle, make the border thicker and start a progress animation on the border. We are going to use an SVG circle for this animation, so we need to ensure that the decreased round button is of the same size and sits in the same position as the SVG circle, that we’ll show in that moment.
We’ll then draw the circle stroke, simulating the progress of the submission.

Once the submission is complete, i.e. the stroke is all drawn, we have to make our button expand again and draw the checkmark in case of a successful submission. We’ll also color the button accordingly.

For the case of a failed submission, we’ll also want an error state style.

So, let’s create our markup with all the elements that we need.

The Markup

For our markup we need a main container, a button with a span that contains the text, and the three SVGs:

We’ve used Method Draw, an easy-to-use online SVG editor to draw the checkmark and cross previously. The size for all SVGs will be 70×70 because our button has a height of 70 pixels. Since we want the circle to have a stroke thickness of 5 pixel in order to look like in Colin’s concept, we need to set the correct radius when we draw it in the graphics editor so that the whole circle together with its stroke fills the button height of 70 pixels. Note that strokes in SVG are drawn half inset and half outset. For example, a stroke of 2 will increase a circle of radius 10 to a “real” width and height of 20 plus 2 instead of 20 plus 4 (two times the border width), so the formula is 2r + border. So, in our case we know that 2r + 5 = 70, hence our circle needs a radius of 32.5 and we end up with this shape <circle cx="35" cy="35" r="32.5"/>.

Unfortunately, we can’t just use this basic shape because the starting point of the “path” will differ in browsers and so we can’t control where the “progress animation” starts. So, we’ll convert this circle shape to a path and use that instead (see above). You can easily do that in Method Draw under Object > Convert to Path.

For the cross we will use four paths so that we can draw them from a middle point, making it look similar to the checkmark animation.

So, now we have all the elements that we need. Let’s think about the action flow and start styling!

The CSS

First, we need to style our button container. It’s like the outer skin of our button, so let’s make it a bit more like a button and set it’s display to inline-block so that we can use it in the flow:

The success/error indicators will have a thinner stroke and they’ll be white. We’ll also set the linecap of the stroke to round, so that they look nicer. These two will have a quick opacity transition:

So, let’s just recap for a moment and remember our master plan. We need to be able to “style” three additional states (besides the default state) of the button and it’s special elements: the loading state, the success and the error state. So, we will use the classes “loading”, “success” and “error” to indicate them.

The button will become a circle and look exactly like the progress circle when we start with the loading process:

When we reach the last state and the submission was either successful or there was an error, we need to redefine the transitions for our button since we don’t need the border color or width to animate:

If you’d like to play with some other easing functions, check out Ceaser, the CSS Easing Animation Tool by Matthew Lein.

And that’s the style, let’s go on and do the magic!

The JavaScript

We will start by initializing/caching some elements: button is the HTML button element, progressEl is the SVG element that will have the path that represents the ring shaped progress bar, and the successEl, errorEl are the SVG elements that have the paths for the checkmark and the cross, respectively.

We’ll add a function SVGEl that will be used to represent an SVG element and its paths. For each one we will cache the paths and the respective lengths. We initially “undraw” all the paths by manipulating both, the strokeDasharray and the strokeDashoffset values. Later on we will “draw” them back when we want to show the progress path and the checkmark or cross paths. This technique is very well explained in Jake Archibald’s article Animated line drawing in SVG. We basically set the stroke-dasharray to the length of the path and “pull it back” so that we don’t see it anymore by setting the stroke-dashoffset to its length, too. When we want to “draw” the stroke, we will push the offset back to 0, simulating the drawing of the path.

Next, we need to bind the onclick event to the button. The button will initially animate to a rounded shape (by adding the class loading). After this animation ends, either an existing callback function is called (if any was specified in the options) or we simply set the progress to 100% (the speed of this “dummy” animation will be the same as defined for the transition of the stroke-dashoffset in the CSS). The button will also become disabled at this point. (This should actually be the first thing to happen when clicking it, however, if we do so Firefox does not seem to fire the transitionend event.)

Once the progress reaches 100% we need to reset the stroke of the progress circle path. Also, we will either show the success checkmark or the error cross paths. After some time (options.statusTime) we “undraw” any status indicator path and enable the button again. Note that, as shown before, we control the transitions via CSS.

Mary Lou (Manoela Ilic) is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

First of all thanks for the tutorial, it was very helpful. I have two questions if you don’t mind.

The first is whether this control can be used with an asp form. I am not very pro efficient in the subject and the only workaround I could come up with is to perform javascript validation instead and imitate a click afterwards. Is there some other neater way? Also, I would like to ask where should the validation be included. I downloaded the source and couldn’t identify where the code should be placed.