There are times when we can use a timer on websites. 😉
Perhaps we want to time a quiz or put pressure on a survey. Maybe we are just trying to making a dramatic countdown after a user has done something great like successfully booking a concert ticket. We could be trying to build a micro time management tool (think Pomodoro). Or it could just be an alternative to a spinner UI.
When those situations come up, I have no qualms employing JavaScript which is probably a more powerful tool for this sort of thing generally. And yet! CSS substitutes are just as fun and efficient when the simplest option is the best one. Some email clients these days are highly CSS capable, but would never run JavaScript, so perhaps that situation could be an interesting progressive enhancement.
Let’s take a look at what it takes to cook up a CSS timer. We’ll use some modern CSS tech to do it. The ingredients?
- CSS Counters
- @property
- pseudo elements
- @keyframes
- A little Celtic sea salt to taste
To get started, fire up a (Code)Pen and keep it warm. Below is the demo we’ll be working towards (later, I’ll show some stylized examples):
There are three main requirements for our CSS Timer:
- A number that can decrement from 5 to 0
- A way to time five seconds, and decrement the number in each
- A way to display the decreasing number on page
The Number
For our first requirement, the update-able number, we’ll use @property
to create a custom property that will hold a value of type <integer>
.
Note: Integer numbers can be zero, or a positive or negative whole number. If you want numbers with decimal points, use <number>, which holds a real number.
@property --n {
 syntax: "<integer>";
 inherits: false;
 initial-value: 0;
}
Code language: CSS (css)
The Counting
For tracking seconds, while decreasing the number, we go to @keyframes animation.
@keyframes count {
 from { --n: 5; }
 to { --n: 0; }
}
Code language: CSS (css)
The animation function is put to action with the animation property.
.timer:hover::after {
 animation: 5s linear count;
}
Code language: CSS (css)
Here’s what’s happening:
When we register a custom property for a specific value type, <integer>
, <percentage>
, or <color>
, for instances, the browser knows that the property is created to work with that specific type of value.Â
With that knowledge the browser confidently updates the custom property’s value in the future, even throughout an animation.
That’s why our property --n
can go from 5 to 0 within an animation, and since the animation is set for five seconds, that’s essentially counting from five to zero over a period of five seconds. Hence, a timer is born.Â
But there’s still the matter of printing out the counted numbers onto the page. If you hadn’t noticed earlier, I’d assigned the animation to a pseudo-element, and that should give you a clue for our next move — content.
The Display
The property, content
, can display contents we have not yet added to the HTML ourselves. We generally use this property for a variety of things, because this accepts a variety of values — images, strings, counters, quotation marks, even attribute values. It doesn’t, however, directly takes a number. So, we’ll feed it our number --n
through counter
.Â
A counter can be set with either counter-reset
or counter-increment
. We’ll use counter-reset
. This property’s value is a counter name and an integer. Since counter-reset
doesn’t correctly process a CSS variable or custom property for an integer yet, but does accept calc()
, the calc()
function becomes our Trojan Horse, inside of which we’ll send in –n.Â
.timer:hover::after {
 animation: 5s linear count;
 animation-fill-mode: forwards;Â
 counter-reset: n calc(0 + var(--n));
 content: counter(n);
}
Code language: CSS (css)
That is:
- Our animate-able number,
--n
, is first fed tocalc()
calc()
is then fed tocounter()
- The
counter()
in turn is given tocontent
, finally rendering--n
on the page.Â
The rest is taken care of by the browser. It knows --n
is an integer. The browser keeps up with animation changing this integer from 5 to 0 in five seconds. Then, because the integer is used in a content
value, the browser displays the integer on the page as it updates.
At the end of the animation, the animation-fill-mode: forwards;
style rule prevents the timer from reverting back to the initial --n
value, zero, right away.
Once again, here’s the final demo:
For design variants you can count up or down, or play with its appearance, or you can combine this with other typical loader or progress designs, like a circular animation.
At the time of writing, Firefox is the only missing browser support for @property
, but they have announced an intent to ship, so shouldn’t be long now. For support reference, here’s the caniuse.com page for @property
.Â
CSS Custom Properties can also be set and updated in JavaScript. So, if at some point you would like to be able to update the property in JavaScript, just about with any other CSS property, you can do it using the setProperty()
function. And if you wish to create a new custom property in JavaScript, that can be done with registerProperty()
. The other direction, if you wanted to let JavaScript know a CSS animation has completed, you could listen for the animationend
end event.
If you’re really into this sort of thing, also check out Yuan Chuan’s recent Time-based CSS Animations article.
I actually made a little timer using @property & checkboxes recently!
Can’t wait for @property to come to Firefox
(click on the numbers)
https://codepen.io/JezDriver/pen/bGywPZx?editors=1100