How to Make a CSS Timer

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?

  1. CSS Counters
  2. @property
  3. pseudo elements
  4. @keyframes
  5. 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:

  1. A number that can decrement from 5 to 0
  2. A way to time five seconds, and decrement the number in each
  3. 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:

  1. Our animate-able number, --n, is first fed to calc() 
  2. calc() is then fed to counter()
  3. The counter() in turn is given to content, 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.

Wanna learn modern CSS layout?

One response to “How to Make a CSS Timer”

  1. Jez says:

    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

Leave a Reply

Your email address will not be published. Required fields are marked *

Did you know?

Frontend Masters Donates to open source projects. $313,806 contributed to date.