Random Numbers in CSS
I stumbled into an interesting problem the other day. I wanted to animate an element with a random animation-duration
. This was the non-randomized starting point:
See the Pen Random numbers CSS #1 by Robin Rendle (@robinrendle) on CodePen.
This is the CSS I wrote to make the animation:
@keyframes flicker {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#red {
animation: flicker 2s ease alternate infinite;
}
So far so good. But no randomization happening there, that’s a fixed 2 seconds.
I wanted that animation time of 2 seconds to be random. I wanted to essentially write:
.element {
animation: flicker $randomNumber alternate infinite;
}
Where $randomNumber
is randomized programatically.
CSS preprocessors like Sass do offer a random() function.
$randomNumber: random(5);
.thing {
animation-duration: $randomNumber + s;
}
That might be perfect for you, it wasn’t quite perfect for me. Random numbers generated during preprocessing have a big caveat:
random in Sass is like randomly choosing the name of a main character in a story. it’s only random when written. it doesn’t change.
— jake albaugh (@jake_albaugh) December 29, 2016
In other words, as soon as the CSS is processed, randomization is over. That number is locked at that value forever (i.e. until the preprocessor runs again).
It’s not like a random number in JavaScript (e.g. Math.random()
) where that random number is generated when the JavaScript runs.
So after sighing as obnoxiously loudly as I could I realized that this would actually be the perfect opportunity to use native CSS variables (custom properties)! By themselves, they can’t do random numbers easier, but as we’ll see, they can still help us.
If you’re not familiar with them, then not to worry. Effectively they’re native variables built into the CSS language itself, but they’re different from the sort of variables that you might be familiar with from a preprocessor like Sass or Less. Chris listed many of the benefits a while back:
- You can use them without the need of a preprocessor.
- They cascade. You can set a variable inside any selector to set or override its current value.
- When their values change (e.g. media query or other state), the browser repaints as needed.
- You can access and manipulate them in JavaScript.
That last bit is what’s important to us. We’re going to generate the random number in JavaScript, then move it over to CSS via custom properties.
Set one is to create the CSS custom property we need, with a default value (useful in case the JavaScript we write in a moment fails for any reason):
/* set the default transition time */
:root {
--animation-time: 2s;
}
Now we can use that variable in our CSS like this:
#red {
animation: flicker var(--animation-time) ease alternate infinite;
}
Undramatically, we’re exactly where we started. But although this demo now looks exactly the same as our previously animated SVG, this one is using CSS variables instead. You can test that everything is working by just changing the variable in the CSS and watch as the animation updates.
Now we’re all set up to access and manipulate that custom property via JavaScript.
var time = Math.random();
From here we can find the red circle in the SVG and change the --animation-time
CSS variable via the setProperty
method:
var red = document.querySelector('#red');
red.style.setProperty('--animation-time', time +'s');
And here it is! A randomly generated number in CSS which is being applied to an SVG animation:
See the Pen Random numbers CSS #3 by Robin Rendle (@robinrendle) on CodePen.
This is a step forward because the random number is generated when the JavaScript runs, so it’s different every time. That’s pretty close to what we wanted, but let’s make this a little bit more difficult still: let’s randomize that animation-duration
periodically as it’s running.
Fortunately, we have JavaScript to work with now, so we can update that custom property whenever we want to. Here’s an example where we update the animation-duration
every second:
var red = document.querySelector('#red');
function setProperty(duration) {
red.style.setProperty('--animation-time', duration +'s');
}
function changeAnimationTime() {
var animationDuration = Math.random();
setProperty(animationDuration);
}
setInterval(changeAnimationTime, 1000);
That’s exactly what I was after:
See the Pen Random numbers CSS #4 by Robin Rendle (@robinrendle) on CodePen.
It’s useful to remember that CSS variables (custom properties) support is still a little patchy, so beware. Although what we could do is progressively enhance this animation like so:
#red {
animation: flicker .5s ease alternate infinite;
animation: flicker var(--animation-time) ease alternate infinite;
}
If CSS variables aren’t supported then we’ll still get some kind of animation being shown, even if it isn’t precisely what we want.
It’s worth noting that CSS variables aren’t the only possible way to randomize the animation-duration. We could access the DOM element via JavaScript and apply the random value directly into the style
:
var red = document.querySelector('#red');
red.style.animationDuration = Math.floor(Math.random() * 5 + 1) + "s";
We could even wait for the animation to finish before setting a new duration, if we wanted:
var red = document.querySelector('#red');
function setRandomAnimationDuration() {
red.style.animationDuration = Math.floor(Math.random() * 10 + 1) + "s";
}
red.addEventListener("animationiteration", setRandomAnimationDuration);
Just to sprinkle one more possibility in here, you could also do this with EQCSS.
@element '#animation' {
.element {
animation-duration: eval('rand')s;
}
}
var rand = Math.random();
EQCSS.apply();
Do you wish randomization was available right in CSS itself? I’m not sure if there is any talk of that. Even if there was, we would likely have to wait quite a while to actually use it. Along those lines, Philip Walton recently wrote how difficult it would be to write a true polyfill for random numbers in CSS. Much easier to handle in JavaScript!
Random Numbers in CSS is a post from CSS-Tricks