This post will take 2 minutes to read.
Creating a pie timer using CSS is a good lesson in creative use of pseudo-elements and CSS animations and the end result can be used to replace a loading spinner. Let’s start by creatng our pie timer element (using a single element to be as semantic as possible):
<div class="timer"></div>
Next, let’s get some basic CSS styles sorted, including setting the size of our pie timer and ensuring that it’s a circle:
.timer { border-radius: 50%; height: 6em; width: 6em; }
We can’t actually see anything yet, so let’s add some colour. We’re going to use a CSS gradient to fill the left half of our pie timer with our primary colour and the right half with our secondary colour.
.timer { background: linear-gradient(90deg, #6c6 50%, #ddd 50%); }
Now we need to create the pseudo-element that will act as a mask in the pie timer animation. Let’s also set the timer element’s positon to relative so that we can easily position the mask in relation to the pie timer:
.timer { position: relative; } .timer:after { border-radius: 100% 0 0 100% / 50% 0 0 50%; content: ''; height: 100%; left: 0; position: absolute; top: 0; width: 50%; }
As the mask will only be used to cover half of the pie timer, the width is set to 50%
and we’re using border-radius
to make the mask a semicircle. A background colour has not been set as the mask animation will take care of that.
Next we need to build our animations. We’ll start with the easier of the two; the one that will spin the pie timer around. The keyframes are very straightforward:
@keyframes timer { 100% { transform: rotate(360deg); } }
We don’t need to set any value at 0%
as that’s the position the element is already in. Now we’re going to use the animation
shorthand property to run our animation over 10 seconds with steps 1 second apart. We also want the animation looped infinitely:
.timer { animation: timer 10s steps(10, start) infinite; }
Now it gets a little more complex. We need to use the mask to fill the left side of the pie timer element with the secondary colour during the first five seconds of the animation and then to fill the right side with the primary colour during the last five seconds. As our mask is a psuedo-element, we also need to negate the spinning on the pie timer element to keep our mask on the correct side for each half of the animation. Here are our keyframes:
@keyframes mask { 0% { background: #ddd; transform: rotate(0deg); } 50% { background: #ddd; transform: rotate(-180deg); } 50.01% { background: #6c6; transform: rotate(0deg); } 100% { background: #6c6; transform: rotate(-180deg); } }
The animation
shorthand is basically the same as the first but we only need half the number of steps:
.timer:after { animation: mask 10s steps(5, start) infinite; }
You’ll now notice that the mask is not rotating in perfect alignment with the pie timer. This can be fixed by adjusting the exact point that it’s rotating around using transform-origin
:
.timer:after { transform-origin: 100% 50%; }
And now you have a fully functional pie timer using a single HTML element and 43 lines of CSS! How creative can you get with it?
Final Product:
<div class="timer"></div>
.timer { animation: timer 10s steps(10, start) infinite; background: linear-gradient(90deg, #6c6 50%, #ddd 50%); border-radius: 50%; height: 6em; position: relative; width: 6em; } .timer:after { animation: mask 10s steps(5, start) infinite; border-radius: 100% 0 0 100% / 50% 0 0 50%; content: ''; height: 100%; left: 0; position: absolute; top: 0; transform-origin: 100% 50%; width: 50%; } @keyframes timer { 100% { transform: rotate(360deg); } } @keyframes mask { 0% { background: #ddd; transform: rotate(0deg); } 50% { background: #ddd; transform: rotate(-180deg); } 50.01% { background: #6c6; transform: rotate(0deg); } 100% { background: #6c6; transform: rotate(-180deg); } }