Bonus Chapter 2 - Animating CSS Properties With Transitions
Bonus Chapter 2 - Animating CSS Properties With Transitions
Bonus Chapter 2
Animating CSS
Properties with
Transitions
Our brains aren’t really built for things to just happen. Companies like
Apple understand this, and they account for it in their products. If you have
an iPhone, watch it carefully as you do things like lock/unlock it, or switch
between apps. Notice how much life there is in every interaction and
transition.
—JOSH COMEAU
B
esides great content, the awesome pages you come across on the web have
a few things in common: good typography, solid layout, and a thoughtful
approach to responsiveness and accessibility. Chances are, those pages also
have one other feature in common: a sense of liveliness and a kind of twinkle that
come from the judicious use of animated effects. You may not even notice these
effects, but your brain registers them and probably squirts out some feel-good
dopamine in response because your brain reacts positively to the aliveness that
animation brings to the page.
What is a Transition?
All the CSS I talk about in this book has been applied by the web browser in,
as Shakespeare once said, “one fell swoop.” That is, whether it’s applying a
color, adding padding, or setting a font size, when the browser comes upon a
declaration (and assuming that declaration is a “winner” in the cascade; check
out Book 3, Chapter 4), the browser implements the declaration instantly. Even
transforms (the subject of Bonus Chapter 1) triggered by, say, the :hover pseudo-
class are applied right away in the sense that the transform immediately changes
the element to its new property value.
On the surface, nothing is inherently wrong with the browser’s unseemly haste
in applying the CSS it comes across. Speed is good, am I right? Well, not always,
because when “speed” means “instantly,” you often end up with something that
appears unnatural because few things in the real world happen instantly. An object
is in one state and then it changes to another state, and that change usually goes
through a continuum of intermediate states. There is, in short, a transition from
one state to another and it’s via that transition that we make sense of the change
and perceive the change as “natural.”
You can achieve that sense of naturalness in your web pages by asking the web
browser to apply some properties not right away, as usual, but via a continuous
series of intermediate states. This is called a transition and, as you learn in the rest
of this chapter, with transitions you can control not only which properties are
animated but also the animation trigger, the animation duration, and the anima-
tion timing.
In this chapter’s introduction, I use the phrase “judicious use of animated effects”
with italics here for emphasis. Why? Because, believe me, nobody wants to see
fireworks exploding while your page loads. Nobody wants to be inundated by
Hollywood-quality special effects when a menu opens. Nobody wants to hover the
mouse pointer over a button and sit through some interminable and ultimately
pointless transformation of the element.
with Transitions
Animating CSS Properties
stages, that property is almost certainly animatable with a transition (or any of
the CSS animations that I discuss in Bonus Chapter 3).
For example, the opacity property can take a numeric value from 0 to 1 (or 0% to
100%), where 0 means the element is fully transparent and 1 means the element
is fully opaque. When setting, say, opacity: 0 on an element, it makes sense that
the change from the default of opacity: 1 could take place over time in a series
of intermediate stages. That is, instead of becoming immediately transparent, the
element could fade out over the course of a second or two.
The Mozilla Developer Network maintains an exhaustive list of the CSS properties
that are animatable here: https://developer.mozilla.org/en-US/docs/Web/
CSS/CSS_animated_properties.
»» The initial value for the property you’re animating. If the initial value is just
the default value for the property, you don’t need to specify the value.
»» The final value for the property you’re animating. You declare this final
value within the trigger rule.
Given these items, the animation process goes something like this:
1. The browser renders the element with the initial (or default) property.
For example, if you used the :hover pseudo-class, the browser triggers the
animation when the user hovers the mouse pointer over the element.
3. The browser runs the transition based on the parameters you specified.
4. When the transition reaches the final property value, the browser ends
the animation.
The browser runs the animation for the duration you specify.
transition-property: property-name;
transition-duration: time;
transition: property-name time;
»» property-name: The name of the property you want to animate. You can
also use the keyword all to tell the browser to transition all the animatable
properties that change with the trigger.
For example, suppose you have the following button element (check out bk06ch02/
example01.html in this book’s example files):
Suppose, as well, that you want to animate a transform. Here’s the setup code for
that:
with Transitions
Animating CSS Properties
button {
transition-property: transform;
transition-duration: 1s;
}
So far, so good. The second stage is to set up the trigger and use that rule to spec-
ify the final value of the transition. For the example, you could do something like
this:
button:hover {
transform: scale(2);
}
This tells the browser that when the user hovers the mouse pointer over the but-
ton, the browser should smoothly transition the button to twice its default size.
Figure BC2-1 shows the original button, and Figure BC2-2 shows the button at the
end of the transition. Note, as well, that when the user moves the mouse pointer
off the button, the browser transitions the button back to its original state.
FIGURE BC2-1:
The original
button.
Both properties are animatable, so here’s some code that sets up a transition for
each property (check out bk06ch02/example02.html):
.toggle {
transition: background-color 0.75s;
}
.toggle::after {
transition: transform 0.75s;
}
#cb-toggle:checked + .toggle {
background-color: hsl(102deg 58% 39%);
}
#cb-toggle:checked + .toggle::after {
transform: translateX(24px);
}
The browser applies the duration time1 to the transition for the property name1,
the duration time2 to name2, and so on. Here’s an example (bk06ch02/example03.
hml):
with Transitions
Animating CSS Properties
button {
background: hsl(100, 61%, 75%);
color: hsl(0, 0%, 10%);
transition-property: background, color, transform;
transition-duration: 2s, 1s, 3s;
}
button:hover {
background: hsl(100, 61%, 25%);
color: hsl(0, 0%, 90%);
transform: scale(2);
}
If there are fewer durations than there are properties, the browser repeats the
durations as needed so that every property has a duration. This means that if you
want every property transition to use the same duration, you need to specify only
a single time value:
transition-property: property-name;
transition-duration: duration-time;
transition-delay: delay-time;
transition: property-name duration-time delay-time;
»» delay-time: The amount of time you want the browser to wait before
starting the transition, expressed in seconds (s) or milliseconds (ms)
Note, in particular, that when you use the transition shorthand and you specify
two time values, the first time value is interpreted as the transition duration and
the second time value is interpreted as the transition delay. Here’s an example
(bk06ch02/example04.hml):
button {
background: hsl(100, 61%, 75%);
color: hsl(0, 0%, 10%);
transition-property: background, color, transform;
transition-duration: 1s;
transition-delay: 0s, 1s, 2s;
}
button:hover {
background: hsl(100, 61%, 25%);
color: hsl(0, 0%, 90%);
transform: scale(2);
}
Intuitively, you’d think that interpolation would work something like this:
1. Subtract the final property value from the initial property value.
3. Run the transition by continually incrementing the property value by the result
of Step 2 until the final value is reached.
That, my friend, is CSS trying to make transitions appear more natural. After all,
when an action occurs in the real world, it’s rare for it to run at a constant rate.
A car takes a bit of time to get up to speed, and then it slows before it comes to
a stop; a snowball rolling down a hill starts slowly and then picks up speed as it
goes; a rubber ball dropped from a height bounces a few times when it hits the
with Transitions
Animating CSS Properties
floor.
CSS enables you to mimic these and similar natural behaviors by defining how the
speed of an animation varies throughout its duration. You define this animation
timing by specifying a timing function using the transition-timing-function
property:
transition-property: property-name;
transition-duration: duration-time;
transition-delay: delay-time;
transition-timing-function: timing-function;
transition: property-name duration-time delay-time
timing-function;
»» delay-time: The amount of time you want the browser to wait before
starting the transition, expressed in seconds (s) or milliseconds (ms)
Because most of these keywords ease (that is, slow down) the transition beginning
and/or end, they’re also known as easing functions.
ease-in Starts slowly and then speeds up to the end of the cubic-bezier
transition. (0.42, 0, 1, 1)
ease-out Starts quickly and then slows down to the end of the cubic-bezier
transition. (0, 0, 0.58, 1)
button {
transition-property: transform;
transition-duration: 2s;
transition-timing-function: ease-in-out;
}
button:hover {
transform: translateX(50vw);
}
»» The closer the slope of the curve is to horizontal, the slower the transi-
tion speed.
»» The closer the slope of the curve is to vertical, the faster the transition speed.
Okay, I get it: The cubic-bezier() function just isn’t easy to grasp, at least in
part because you have no way to examine the function’s values and intuit the
curve they create. So, forget that.
with Transitions
Animating CSS Properties
Instead, you can also use your web browser’s dev tools to play around with cubic-
bezier() function values. Right-click the element that has the defined transi-
tion and then click Inspect to display the element in the dev tools. Next to the
element’s cubic-bezier() function, click the Open Cubic Bezier Editor icon to
launch the editor, shown in Figure BC2-3. (Figure BC2-3 shows the Chrome tool;
all the major browsers offer similar tools.)
FIGURE BC2-3:
In the browser
dev tools, use
the Cubic Bezier
Editor to build
your cubic-Bezier
curves visually.
Feel free to drag the second curve button (the one attached to the end point of
the animation) above the plane, which will give you a “y” value of greater than 1.
This makes the animating property go “past” its final value briefly, which adds a
kind of “bounce” effect. (It is, admittedly, a pretty lame bounce effect. For the real
bounce deal, you need to set up animation frames, as I discuss in Bonus Chapter 3.)
HTML:
CSS:
body {
height: 100vh;
}
.ball {
border-radius: 50%;
height: 10rem;
width: 10rem;
transition-property: transform;
transition-duration: 1.5s;
transition-timing-function: cubic-bezier(0.5, 0.96, 0.75,
1.44);
}
.ball:focus {
transform: translateY(calc(100vh - 10rem));
}
This code turns a div element into a ball that, when clicked, shifts the ball from
the top of the viewport to just past the bottom of the viewport, and then finally
back up to the bottom of the viewport.
However, if you want a bit more control over your transitions, you need to get
JavaScript on the job, which enables you to set up a wider range of transition trig-
gers as well as to run some code when a transition ends.
with Transitions
Animating CSS Properties
Using code to trigger a transition
To use JavaScript to trigger a transition, you need to set up your CSS and JavaS-
cript as follows:
• Create a class that contains the final values of the property or properties
you want to transition. So, whereas before you put these declarations in a
pseudo-class rule, now you put them in a class rule. Make sure the element
you’re animating doesn’t use this class by default.
• In the event listener callback function, use the classList object’s add
method to add the class on the element. (For the details on the classList
object, check out Book 4, Chapter 6.) Because the element doesn’t have the
class by default, the add method inserts the class, which triggers the
transition.
HTML:
body {
height: 100vh;
}
.ball {
border-radius: 50%;
height: 10rem;
width: 10rem;
transition-property: transform;
transition-duration: 1.5s;
transition-timing-function: cubic-bezier(0.5, 0.96, 0.75,
1.44);
}
.move-ball {
transform: translateY(calc(100vh - 10rem));
}
JavaScript:
The HTML and CSS are the same as in the example from the previous section, but
now the transform declaration resides in the .move-ball rule. The JavaScript lis-
tens for clicks on the element with the .ball class. In the event handler, the script
uses event.target to reference the element (that is, in this case, event.target
is a reference to the element that was clicked), and then adds the move-ball class
to trigger the transition.
HTML:
<nav>
<button class="nav-menu-button">
Menu
</button>
CSS:
.nav-menu-dropdown-contents {
with Transitions
Animating CSS Properties
height: 0;
left: 0;
opacity: 0;
position: absolute;
top: 75px;
transition: all 0.75s ease-out;
}
.nav-menu-dropdown-contents.show {
height: fit-content;
opacity: 1;
}
.nav-menu-dropdown-contents > a {
display: block;
border-radius: 5px;
font-size: 1.25rem;
padding: 4px 8px;
text-decoration: none;
}
JavaScript:
Why did I use the toggle method instead of the add method to trigger the transi-
tion? Because in this case I want the transition to be reversible. That is, if the user
clicks the Menu button a second time, I want the menu to disappear. The easiest
way to do that is to toggle the class on and off.
That simplicity is great if you just want to add some subtle effects to your page
interface. However, you may find that you sometimes need a little something
extra to get the effect you want. I’m not talking about adding more animatable
properties to the transition. Nothing wrong with that (as long as you don’t go
overboard with it), but I’m after bigger game here: Running an entirely differ-
ent transition after the first one ends. This is called chaining transitions, and it
enables you to run one transition after another, which gives you extra scope for
all kinds of interesting effects.
Before getting to the code, let me stress, again, that you don’t want to let things
get out of hand here. You can usually get away with chaining two, perhaps three,
short transitions, but beyond that you’re asking way too much of your site visitors.
HTML:
body {
height: 100vh;
}
.ball {
border-radius: 50%;
height: 10rem;
width: 10rem;
transition-property: transform, background-color, color;
transition-duration: 1s, 750ms, 750ms;
with Transitions
Animating CSS Properties
transition-timing-function: ease;
width: 10rem;
}
.move-ball {
transform: translateY(calc(100vh - 10rem));
}
.color-ball {
background-color: hsl(0deg 75% 60%);
color: hsl(45deg 100% 90%);
}
JavaScript:
In the JavaScript, the .ball element has an event listener for the transitionend
event that, when fired, adds the .color-ball class to the element. Just for fun,
the transitionend callback function also checks whether the transition event’s
property name is color, which means this is the end of the second transition, so
the code then modifies the ball text.
If you want to run some code when a transition starts, set up a listener for the
transitionstart event.
So, does this mean you should never use animation on your site? No, that’s too
drastic because there are ways to give users control over the animations they
encounter:
»» JavaScript: If your site has a “Settings” feature through which users can
customize their experience, be sure to add a setting that lets users turn off
animations. (If you don’t implement settings, an alternative would be a check-
box or switch somewhere on each page.) In your animation code, you could
then get the setting from local storage and test it: If the user doesn’t want
animation, then just before you add the transition class, set the transition-
duration property to a very small value (bk06ch02/example10.html):
event.target.style.transitionDuration = '0.01ms';
with Transitions
Animating CSS Properties