An Innovative Idea for Holding State in CSS

janeori

Jane Ori

Posted on January 15, 2022

An Innovative Idea for Holding State in CSS

When it comes to CSS and HTML, application state falls into these categories:
cascade state: classes/checkbox state/other dom
screenshot of html and css producing a static state
pseudo state: based on current user interaction
gif showing HTML and CSS with a user interacting to trigger a temporary hover state
animation state: pre-determined series, triggered by cascade and/or pseudo state
gif showing a CSS animation triggered by clicking a checkbox
transition state: easing between the cascade and/or pseudo states when they change
gif showing CSS transition triggered by hover

These are the building blocks for all UX that can be combined in an infinite number of ways[1]. These are all intertwined of course, and we can split hairs on how I've referred to them, but ultimately though; Either our state is static - derived from the current DOM, or it's temporary - derived from the user's current interaction.

[1] except that animation state cannot be combined with animation state due to animation-tainting

"But wait, there's more!"

- @rockstarwind, probably

The new static state, stored in CSS (not DOM!)

Earlier this week, @rockstarwind posted an idea on their twitter that demonstrates a new technique for remembering temporary interactions without DOM needing to be the source of that memory (like it is with the :checked state of a checkbox).
They go on to show a reduced example in a codepen with excellent comments to learn from:

Neat!

Let's break down the idea and play with it a bit.

Register the one-bit memory cell

Following their work, this is what we have to do first:

@keyframes memory {
  0% { --cell: initial }
  1%, 100% { --cell: ; }
}
Enter fullscreen mode Exit fullscreen mode

Using this memory animation sets the custom property --cell as a Space Toggle which has two states, off 'initial' and on ' '.


The TL;DR of Space Toggles

You can concatenate CSS custom properties like so:

.demo {
--val1: 2px solid;
--val2: red;
--result: var(--val1) var(--val2);
}
Enter fullscreen mode Exit fullscreen mode

--result is effectively 2px solid red

If you concatenate a space:

.demo {
--toggle: ;
--value: green;
--result: var(--toggle) var(--value);
}
Enter fullscreen mode Exit fullscreen mode

--result is effectively just green

If you concatenate initial:

.demo {
--toggle: initial;
--value: green;
--result: var(--toggle) var(--value);
color: var(--result, cyan);
}
Enter fullscreen mode Exit fullscreen mode

--result is invalidated and var(--result, fallback) will use the fallback instead. (color is set to cyan in this example)

So in effect, a space toggle is is a one bit variable we can use to derive two completely different states from reading it with var() wherever and as often as we want.



Note: Animating custom properties is part of the Houdini spec, so this may not work in firefox for a while.

Initialize the memory

All we have to do here is apply the keyframes to an element:

.demo {
  animation-name: memory; /* keyframes reference */
  animation-duration: .1s;
  animation-delay: 0s;
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
  animation-timing-function: linear;
  animation-play-state: paused; /* must be paused at first */
}
Enter fullscreen mode Exit fullscreen mode

Using the shorthand animation property, that looks more like this:

.demo {
  animation: memory 1ms linear 1 forwards paused;
}
Enter fullscreen mode Exit fullscreen mode

Now .demo elements have a --cell property explicitly set to initial.

Set up properties to use the space toggle

.demo {
  --bg-if-cell-is-on: var(--cell) rebeccapurple;
  background: var(--bg-if-cell-is-on, hotpink);

  --color-on: var(--cell) white;
  color: var(--color-on, black);
}
Enter fullscreen mode Exit fullscreen mode

In English, if the cell is off, the background is hotpink and color is black. If the cell is on, the background is rebeccapurple and color is white.

Flipping the bit

Next, they use the :active pseudo state from a button to un-pause the animation. Theoretically we could use any pseudo state, like :hover.

Let's test

click 'rerun' in the bottom right corner if needed!

Great! If the user's pointer enters the document, CSS flips state and the background becomes rebeccapurple.

Un-flipping the bit

To return to the initial state, the easiest way is what they've shown, set animation: none; on our element when a selector matches.
This changes --cell back to initial implicitly because when a custom property is not set (nor registered with a specific syntax), it is initial.

The rest of the owl

We can use this technique to do something fun like 100% CSS etch-a-sketch:

Delightful!

"Remembering is so much more a psychotic activity than forgetting"

I've only been friends with RockStarwind for a couple years but they are continually sharing innovative ideas and awesome demos in the CSS space. Definitely give them a follow on Twitter so you don't have to remember to check manually. ;)

If you enjoyed my first article, please consider following me here and on twitter as well!

šŸ’œ

Prior art!

After tweeting, I learned about someone already using this idea in a really really interesting way! Check it out!

and her demo:

HOW COOL IS THAT?!

šŸ’– šŸ’Ŗ šŸ™… šŸš©
janeori
Jane Ori

Posted on January 15, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related