Individualizing CSS Properties with CSS Variables

In “A Trick: Individual CSS Transform Functions”, I discussed how we could use CSS Variables (Custom Properties) to bring us close to independent transform properties. Each day I explore CSS Variables, however, I’m finding they can help us with any property that accepts multiple values at the same time.

See the Pen Pause Independent Animations with CSS Variables by Dan Wilson (@danwilson) on CodePen.

This example shows a block of text that animates via clip-path to reveal itself over another block of text. I’ve added a second keyframe animation to do a hue-rotate filter to effectively cycle through colors for the background. The animation property therefore has two values: one for each animation. If I want to change anything later for only one animation - such as a duration or play-state, I would likely take one of two approaches (Let’s talk about toggling the animation-play-state from paused to running):

  1. Set up multiple classes to represent all the different combinations of play state we can have (in our case four: running,running; running,paused; paused,running; paused,paused;), and use JavaScript to determine the correct one and toggle the class. (A fork of this example on CodePen by Shaw shows how you can take this approach and still make this with a small amount of code via the checkbox hack)
  2. Or: use JavaScript to getComputedStyle on our animating element, tokenize the value based on the comma, set the desired value on the one we want to update, and then join back together for a final result string to be set on the element’s style.animationPlayState.

These approaches have drawbacks, since they both grow more complex as we add more animations. We need nine classes if we have three animations and more complicated JS to get the right class applied. For the second option, we need to add additional logic to our JS any time a new animation is added, and we need to be careful to not change order in our CSS.

Instead, our example shows how we can use CSS Variables to break them apart and think of them more individually. We set up our animation as we normally would but we also set a separate animation-play-state property value, with the default values of running:

main {
  --shape-play-state: running;
  --hue-play-state: running;

  animation: 
    reveal-shape 3000ms 1000ms infinite alternate ease-in-out both,
    hue-adjust 6000ms 0ms infinite alternate ease-in-out;
  animation-play-state: 
    var(--shape-play-state),
    var(--hue-play-state);
}

Now we can add some form controls that allow us to pause an individual animation via a small amount of JS:

var main = document.querySelector('main');
//Get Form controls with an id that syncs with the CSS Variable names
document.getElementById('shape').addEventListener('click', updatePlayState);
document.getElementById('hue').addEventListener('click', updatePlayState);

function updatePlayState(e) {
  var variable = '--' + e.currentTarget.id + '-play-state';
  main.style.setProperty(variable, e.currentTarget.checked ? 'paused' : 'running')
}

This allows us to break out of manipulating full property values when we only need to update a small segment. We could take this further and add controls to everything… for each animation’s durations, direction, iteration count, etc. It’s not just CSS Animations and Transforms that take multiple values/functions in their properties and benefit from this approach.

We can use CSS Variables to switch Hue, Saturation, and Lightness for color: hsl():

See the Pen HSL by Dan Wilson (@danwilson) on CodePen.

We can combine it with “The Original CSS Variable” currentColor and theme a site:

See the Pen Site Theming with input[type=range] by Dan Wilson (@danwilson) on CodePen.

We can even update the individual parts of a cubic-bezier() easing function on the fly:

See the Pen Jump to Where You Press by Dan Wilson (@danwilson) on CodePen.

Multiple backgrounds, box shadows, and more are open to this simplification. I’ve focused on using JS to manipulate values individually, but that certainly is not necessary. With backgrounds, for example, you could have the following instead of repeating all the parts for each variation (See the Demo):

.card {
  --front: radial-gradient(circle, teal 20%, teal 40%, transparent 40%); 
  --mid: linear-gradient(135deg, transparent 20%, cyan 20%, cyan 50%, transparent 40%); 
  --back: linear-gradient(45deg, cyan 40%, hotpink 40%, hotpink 60%); 

  background: var(--front), var(--mid), var(--back);
}
.card.highlight {
  --front: radial-gradient(circle, yellow 20%, yellow 60%, transparent 60%);
}
.card.subdued {
  --mid: linear-gradient(transparent, transparent);
}

If you understand and like the CSS cascade, CSS Variables will likely fit into your workflow well.

The latest Chrome, Safari, and Firefox do well with these examples (and as a side bonus: Firefox is only one version away from supporting clip-path without a flag). Edge 15, which was just released, is the first version to have CSS Variables, so I’m just starting to play with them there and… they largely work. Impressive for an initial release in my book, and the more common use cases for Variables seem to be working very well. The transition/animation ones do not always match the other browsers. Hopefully we will see improvements in all the browsers as we continue to explore what CSS Variables can do.