Animating Single Div Art
This article originally appeared on CSS Tricks on May 31, 2017.
When you dig deep with your tools, it is amazing what you can create out of the most basic of HTML. I have been constantly impressed with “Single Div Art” by Lynn Fisher and others where you take a single generic
<div> to create a beautiful cactus, Alamo, or panda.
It can be hard to jump in and deconstruct how these are made, due to the fact they are using layers of background gradients, box shadows, text shadows, and more in the midst of just a single div and its
::after pseudo elements. Before we go any further… check out Lynn Fisher’s article on why and how she started working with single div art.
One thing that single
div pieces rarely do is animate. If you can transform your
div or one of its pseudo elements, that’s fair (as Lynn Fisher does with her fantastic BB-8
[div](http://a.singlediv.com/#bb8)). But you cannot directly change the
transform of the individual “elements” you create inside your
div, since they are not actual DOM elements.
I am a big believer of trying something a little different and interesting to learn tools you otherwise might never learn. Working with the constraints of a single
div might not be great for production work, but it can be a great exercise (and challenge) to stretch your skills in a fun way. In that spirit, we’ll use this technique to explore how Custom Properties (CSS Variables) work and even provide us a path to animation inside our
div. To illustrate along the way we will be breaking down the following example with multiple animation approaches:
See the Pen Single Div Accordion (Animated with CSS Variables) by Dan Wilson (@danwilson) on CodePen.
This accordion (the instrument, not the UI construct) has three main parts, the keyboard side (our
div), the bellows (the part that squeezes, which is the
div::before), and the button side (
div::after). Since the accordion naturally divides into these pieces, we can
transform each piece inside CSS Keyframe animations to get our animation started. The bellows are going between different
scaleX values and the two sides are using countering
translateX values to move with the scaling bellows. Thus, the squeezebox is born.
<div> with CSS Custom Properties
Animating and thinking about the three big pieces is more straightforward than thinking about what appears inside. It can be helpful to group and name the individual bits inside the
div, and Custom Properties provide us a native way to do this. Instead of levels of seemingly infinite linear gradient stops, you can define the
--black-keys for a piano keyboard. Instead of a cross-section of multiple layered gradients you can have a
--tea-cup with its individual
--tea-bag and a related
--tea-bag-position defined inside.
The left side of the accordion boils down to:
Those variable values might be several lines long (even hundreds), but conceptually how the layers of the keyboard come into play are clearer thanks to the variables.
While the same can be done with Sass or Less, Custom Properties allow us to modify these values in the future. We can now conceptually think about animating just our
--button-key2 or the accordion’s decorative
--shine. There are a few ways to tackle this.
Animating Large Property Values with CSS Keyframes
The first way is to use CSS keyframe animations to change the property that contains the piece you want to move. If you want to change something inside your background (say, for example, we want to change the color of our “shine” lines from red to blue), you can set swap out values in the
background property. Building on the previous code sample:
This give us a lot, especially since
background is animatable (as are
box-shadow). In this example there would be a transition from red to blue.
If your property is long, this can be hard to maintain, though Custom Properties can give us a help by extracting out the parts that don’t change to minimize the repetition. We can take it further by abstracting out pieces that don’t need to animate into a new variable - resulting in levels of variables:
The three musical notes in the accordion are based on this approach by animating
Animating with Custom Properties inside CSS Keyframes
A related way to change states is to directly change the custom property inside the
A custom property has no predefined behavior and is not a useful property until it is used with
var(…), so the spec states changing one’s value will cause it to flip its value at 50%. This is the default behavior for all CSS properties that are not animatable and means it will not transition between the values.
You may have guessed since I already mentioned the spec that this is not available in all browsers. Currently, this is supported in Chrome and Opera only.
This will be a quick way to get jump states when it is supported across browsers. If you are viewing this in Chrome or Opera, the accordion uses this approach to animate the keys on the keyboard and the buttons on the right side. For a smaller example, here is a “Pixel Art” example using this approach where the eyes and eyebrows will move in Blink browsers. Other browsers will nicely fall back to a static image. This is in many ways will use the least amount of code, but will have the least amount of support.
setInterval to toggle an on/off state for a value (for our piano this could be a key pressed or not).
With the corresponding CSS:
white-key-1 to be either the value from the variable
white-key-color-active depending on its state.
This method is useful when toggling something on and off (such as with a direct change in size, position, or color). This is how the buttons on the right side of the accordion are animated (as a fallback when the Keyframe approach is not supported).
Each of the nine buttons has CSS uses the following default circle, where
--color1 is a light blue and
--button-dim is 1.4vmin:
If i want to change a specific button later to a “pressed” state I can set up a specific value in the CSS, for example the fourth button:
This property is similar, but it replaces the
--color1 with values that are specific to this button combined with a default value inside the
var(). This default value can be specified in our variables by using the form
var(--my-specific-variable, 13px). We can take it a little further and even use another variable value as our default, e.g.
var(--my-specific-variable, var(--my-default-variable)). This second form is what our previous code example uses to create a specific definition for our fourth button while keeping its default value the same. If you have buttons you want to remain unchanged, they can use the default
--button property in a different
In the accordion example,
--button4-dim are never explicitly defined in the CSS. So when loaded they use the default values of
--button-dim. The JS ultimately modifies the values and creates our on/off animation.
This will give us behavior similar to changing the Custom Properties directly in a keyframe where values jump from state to state with no transition. As we’ve already discussed,
background and the
*-shadow properties are animatable (and transitionable… not in a high performance
opacity kind of way… but in small uses that can be okay).
If we take our current JS on/off approach and combine it with a CSS
transition on the
background, we can get a transition instead of a jump state.
Combining with requestAnimationFrame
Depending on how your individual components are composed, the ability to transition the property may not be possible. If you want to move something, you might need to look to
One of my favorite Single Divs out there is a backpack by Tricia Katz:
I would love for that zipper to move back and forth. With a single custom property to represent the zipper’s
x position we can reach for
requestAnimationFrame to change that value and move the zipper right and left.
See the Pen Single Div Backpack with CSS Variables for Animation by Dan Wilson (@danwilson) on CodePen.
There are several approaches to animating inside a