CSS3 – Transitions

CSS transitions allow us to animate CSS properties from an original
value to a new value over time when a property value changes. These transition an element from one state to another, in response to some change—usually a user interaction, but it can also be due to the scripted change of class, ID, or other state.

Normally, when a CSS property value changes—when a “style change
event” occurs—the change is instantaneous. The new property value
replaces the old property in the milliseconds it takes to repaint, or reflow and repaint when necessary, the affected content. Most value
changes seem instantaneous, taking less than 16 milliseconds1 to render. Even if
the changes takes longer, it is still a single step from one value to the
next. For example, when changing a background color on mouse hover, the
background changes from one color to the next, with no gradual
transition.

CSS Transitions

CSS transitions provide a way to control how a property changes from one
value to the next over a period of time. Thus, we can make the property values change
gradually, creating pleasant and (hopefully) unobtrusive effects. For example:

button {
    color: magenta;
    transition: color 200ms ease-in 50ms;
}

button:hover {
    color: rebeccapurple;
    transition: color 200ms ease-out 50ms;
}

In this example, instead of instantaneously changing a
button’s color value on hover, with CSS transitions the button can be set to
gradually fade from magenta to rebeccapurple over 200 milliseconds,
even adding a 50-millisecond delay before transitioning. Changing the
color, no matter how long or short a time it takes, is a transition. But by adding the
CSS transition property, the color change can happen gradually over a period of time and be perceivable by the human eye.

You can use CSS transitions today, even if you still support IE9 or older
browsers. When a browser doesn’t support CSS transition properties, the
change is immediate instead of gradual, which is completely fine. If the property or property values specified aren’t
animatable, again, the change will be immediate instead of gradual.

Note

When we say “animatable,” we mean any properties that can be animated, whether through transitions or animations (the subject of the next chapter, “Animations.”) See Appendix A for a summary.

Sometimes you want instantaneous value changes. Though we used link
colors as an example in the preceding section, link colors usually change instantly on
hover, informing sighted users an interaction is occurring
and that the hovered content is a link. Similarly, options in an
autocomplete listbox shouldn’t fade in: you want the options to appear
instantly, rather than fade in more slowly than the user types. Instantaneous
value changes are often the best user experience.

At other times, you might want to make a property value change more gradually,
bringing attention to what is occurring. For example, you may want to make a card game more realistic by taking
200 milliseconds to animate the flipping of a card, as the user may not
realize what happened if there is no animation.

Tip

Look for the Play symbol to know when an online example is available. All of the examples in this chapter can be found at https://meyerweb.github.io/csstdg4figs/17-transitions/.

As another example, you may want some drop-down menus to expand
or become visible over 200 milliseconds (instead of instantly, which may be
jarring). With transitions, you can make a drop-down menu appear
slowly. In Figure 17-1 , we transition the submenu’s height by making a scale transform. This is a common use for CSS transitions, which we will also explore later in this chapter.

Figure 17-1. Transition initial, midtransition, and final state

Transition Properties

In CSS, transitions are written using four transition properties:
transition-property, transition-duration,
transition-timing-function, and transition-delay, along with the
transition property as a shorthand for the four longhand properties.

To create the drop-down navigation from Figure 17-1, we used all four CSS transition properties, in addition to non-transform properties defining the beginning and end states of the transition. The following code could define the transition for the example illustrated in Figure 17-1:

nav li ul {
    transition-property: transform;
    transition-duration: 200ms;
    transition-timing-function: ease-in;
    transition-delay: 50ms;
    transform: scale(1, 0);
    transform-origin: top center;
}
nav li:hover ul {
    transform: scale(1, 1);
}

Note that while we are using the :hover state for the style change event in our transition examples, you can transition properties in other scenarios too. For example, you might add or remove a class, or otherwise change the state—say, by changing an input from :invalid to :valid or from :checked to :not(:checked). Or you might append a table row at the end of a zebra-striped table or list item at the end of a list with styles based
on :nth-last-of-type selectors.

In the scenario pictured in Figure 17-1, the initial state of the nested
lists is transform: scale(1, 0) with a
transform-origin: top center. The final state is transform: scale(1, 1): the transform-origin remains the same.

Note

For more information on transform properties, see Chapter 16.

In this example, the transition properties define a transition on the transform
property: when the new transform value is set on hover, the nested
unordered list scales to its original, default size, changing
smoothly between the old value of transform: scale(1, 0) and the new
value of transform: scale(1, 1), all over a period of 200 milliseconds.
This transition starts after a 50-millisecond delay, and “eases in,”
proceeding slowly at first, then picking up speed as it progresses.

Transitions are declared along with the regular styles on an element.
Whenever a target property changes, if a transition is set on that
property, the browser will apply a transition to make the change
gradual.

Note that all the transition properties were set for the unhovered state of the ul elements. The hovered state was only used to change the transform, not the transition. There’s a very good reason for this: it means not only that the menus will slide open when hovered, but will slide closed when the hover state ends.

Imagine if the transition properties were applied via the hover state instead, like this:

nav li ul {
    transform: scale(1, 0);
    transform-origin: top center;
}
nav li:hover ul {
    transition-property: transform;
    transition-duration: 200ms;
    transition-timing-function: ease-in;
    transition-delay: 50ms;
    transform: scale(1, 1);
}

That would mean that when not hovered, the element would have default transition values—which is to say, instantaneous transitions. That means the menus in our previous example would slide open on hover, but instantly disappear when the hover state ends—because without being in hover, the transition properties would no longer apply!

It might be that you want exactly this effect: slide smoothly open, but instantly disappear. If so, then apply the transitions to the hover state. Otherwise, apply them to the element directly so that the transitions will apply as the hover state is both entered and exited. When the state change is exited, the transition timing is reversed. You can override this default reverse
transition by declaring different transitions in both the initial and
changed states.

By “initial state,” we mean a state that matches
the element on page load. This could be a state that the element always
has, such as properties set on an element selector versus a :hover
state for that element. It could mean a content-editable element that could get
:focus, as in the following:

/* selector that matches elements all the time */
p[contenteditable] {
    background-color: rgba(0, 0, 0, 0);
}

/* selector that matches elements some of the time */
p[contenteditable]:focus {
    /* overriding declaration */
    background-color: rgba(0, 0, 0, 0.1);
}

In this example, the fully transparent background is always the initial
state, only changing when the user gives the element focus.
This is what we mean when
we say initial or default value throughout this chapter. The
transition properties included in the selector that matches the element
all the time will impact that element whenever the state changes,
whether it is from the initial state to the changed state (being focused, in the preceding example).

An initial state could also be a temporary state that may change, such
as a :checked checkbox or a :valid form control, or even a class
that gets toggled on and off:

/* selector that matches elements some of the time */
input:valid {
    border-color: green;
}

/* selector that matches elements some of the time,
   when the prior selector does NOT match. */
input:invalid {
    border-color: red;
}

/* selector that matches elements some of the time,
   whether the input is valid or invalid */
input:focus {
    /* alternative declaration */
    border-color: yellow;
}

In this example, either the :valid or :invalid selector can match any given element, but
never both. The :focus selector, as shown in Figure 17-2, matches whenever an input has focus, regardless of whether the
input is matching the :valid or :invalid selector simultaneously.

In
this case, when we refer to the initial state, we are referring to the
original value, which could be either :valid or :invalid.
The changed state for a given element the opposite of the initial :valid
or :invalid state.

Figure 17-2. The input’s appearance in the valid, invalid, and focused states

Remember, you can apply different transition values to the initial and changed states, but you always want to apply the value used when you enter a given state. Take the following code as an example, where the transitions are set up to have menus slide open over 2 seconds but close in just 200 milliseconds:

nav li ul {
    transition-property: transform;
    transition-duration: 200ms;
    transition-timing-function: ease-in;
    transition-delay: 50ms;
    transform: scale(1, 0);
    transform-origin: top center;
}
nav li:hover ul {
    transition-property: transform;
    transition-duration: 2s;
    transition-timing-function: linear;
    transition-delay: 1s;
    transform: scale(1, 1);
}

This provides a horrible user experience, but it nicely illustrates the point. When hovered over, the opening of the
navigation takes a full 2 seconds. When closing, it quickly closes over
0.2 seconds. The transition properties in the changed, or hover, state
are in force when hovering over the list item. Thus, the transition-duration: 2s defined for the hover state takes effect.
When a menu is no longer hovered over, it
returns to the default scaled-down state, and the transition properties of
the initial state—the nav li ul condition—are used, causing the menu to take 200ms to close.

Look more closely at the example, specifically the default transition styles.
When the user stops hovering over the parent
navigational element or the child drop-down menu, the drop-down menu
delays 50 milliseconds before starting the 200ms transition to close. This is actually a decent user experience style, because it give users a chance (however brief) to get the mouse point back over a menu before it starts closing.

While the four transition properties can be declared separately, you
will probably always use the shorthand. We’ll take a look at the four
properties individually first so you have a good understanding of what
each one does.

Limiting Transition Effects by Property

The transition-property property specifies the names of the CSS
properties you want to transition. This allows you to limit the transition to only certain properties, while having other properties change instantaneously. And, yes, it’s weird to say “the transition-property property.”

The value of transition-property is a comma-separated list of
properties; the keyword none if you want no properties transitioned;
or the default all, which means “transition all the animatable
properties.” You can also include the keyword all within a
comma-separated list of properties.

If you include all as the only keyword—or default to all—all animatable
properties will transition in unison. Let’s say you want to change a box’s appearance on hover:

div {
    color: #ff0000;
    border: 1px solid #00ff00;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
}
div:hover {
    color: #000000;
    border: 5px dashed #000000;
    border-radius: 50%;
    transform: scale(2) rotate(-10deg);
    opacity: 0.5;
    box-shadow: -3px -3px rgba(255, 0, 0, 0.5);
    width: 100px;
    padding: 20px;
}

When the mouse pointer hovers over the div, every property that has a different
value in the initial state versus the hovered (changed) state will change to the
hover-state values. The transition-property property is used to define
which of those properties are animated over time (versus
instantly). All the properties change from the default value to the
hovered value on hover, but only the animatable properties included in
the transition-property transition over the transition’s duration. Non-animatable
properties like border-style change from one value to the next
instantly.

If all is the only value or the last value
in the comma-separated value for transition-property, then all the
animatable properties will transition in unison. Otherwise, provide a comma-separated list of properties to be affected by the transition properties.

Thus, if we want to transition all the properties, the following statements
are almost equivalent:

div {
    color: #ff0000;
    border: 1px solid #00ff00;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 1s;
}

div {
    color: #ff0000;
    border: 1px solid #00ff00;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
    transition-property: all;
    transition-duration: 1s;
}

Both transition-property property declarations will transition all the
properties listed—but the former will transition only the eight
properties that may change, based on property declarations that may be
included in other rule blocks. Those eight property values are included
in the same rule block, but they don’t have to be.

The transition-property: all in the latter rule ensures that all animatable property
values that would change based on any style change event—no matter
which CSS rule block includes the changed property value—transitions over one second.
The transition applies to all animatable properties of all elements
matched by the selector, not just the properties declared in the same
style block as the all.

In this case, the first version limits the transition to only the eight
properties listed, but enables us to provide more control over how each
property will transition. Declaring the properties individually lets
us provide different speeds, delays, and/or durations to each
property’s transition if we declared those transition properties
separately:

div {
    color: #ff0000;
    border: 1px solid #0f0;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
}

.foo {
    color: #00ff00;
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 1s;
}
<div >Hello</div>

If you want to define the transitions for each property separately,
write them all out, separating each of the properties with a comma. If
you want to animate almost all the properties at the same time, delay,
and pace, with a few exceptions, you can use a combination of all and
the individual properties you want to transition at different times,
speeds, or pace. Make sure to use all as the first value:

div {
    color: #f00;
    border: 1px solid #00ff00;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
    transition-property: all, border-radius, opacity;
    transition-duration: 1s, 2s, 3s;
}

The all part of the comma-separated value includes all the properties
listed in the example, as well as all the inherited CSS properties, and
all the properties defined in any other CSS rule block matching or inherited by the element.

In the preceding example, all the properties getting new values
will transition at the same duration, delay, and timing function, with
the exception of border-radius and opacity, which we’ve explicitly
included separately. Because we included them as part of a comma-separated
list after the all, we can transition them at the the same time,
delay, and timing function as all the other properties, or we can provide
different times, delays, and timing functions for these two properties. In this case, we transition all the properties over one
second, except for border-radius and opacity, which we transition
over two seconds and three seconds respectively. (transition-duration is covered in an upcoming section.)

Note

Make sure to use all as the first value in your comma-separated value list, as the properties
declared before the all will be included in the all, overriding any
other transition property values you intended to apply to those now
overridden properties.

Suppressing transitions via property limits

While transitioning over time doesn’t happen by default, if you do include a CSS
transition and want to override that transition in a particular scenario, you can set
transition-property: none to override the entire transition and ensure no properties are transitioned.

The none keyword can only be
used as a unique value of the property—you can’t include it as part
of a comma-separated list of properties. If you want to override the
transition of a limited set of properties, you will have to list all of
the properties you still want to transition. You can’t use the
transition-property property to exclude properties; rather, you can
only use that property to include them.

Note

Another method would be to set
the delay and duration of the property to 0s. That way it will appear
instantaneously, as if no CSS transition is being applied to it.

Transition events

In the DOM, a transitionend event if fired at the end of every transition, in either
direction, for every property that is transitioned over any amount of
time or after any delay. This happens whether the property is declared individually
or is part of the all declaration. For some seemingly single property
declarations, there will be several transitionend events, as every
animatable property within a shorthand property gets its own transitionend event. Consider:

div {
    color: #f00;
    border: 1px solid #00ff00;
    border-radius: 0;
    transform: scale(1) rotate(0deg);
    opacity: 1;
    box-shadow: 3px 3px rgba(0, 0, 0, 0.1);
    width: 50px;
    padding: 100px;
    transition-property: all, border-radius, opacity;
    transition-duration: 1s, 2s, 3s;
}

When the transitions conclude, there will be well
over eight transitionend events. For example, the border-radius
transition alone produces four transitionend events, one each for:

  • border-bottom-left-radius

  • border-bottom-right-radius

  • border-top-right-radius

  • border-top-left-radius

The padding property is also a shorthand for four longhand properties:

  • padding-top

  • padding-right

  • padding-bottom

  • padding-left

The border shorthand property produces eight transitionend events:
four values for the four properties represented by the border-width shorthand, and four for the properties represented by border-color:

  • border-left-width

  • border-right-width

  • border-top-width

  • border-bottom-width

  • border-top-color

  • border-left-color

  • border-right-color

  • border-bottom-color

There are no transitionend events for border-style properties, however, as
border-style is not an animatable property.

How do we know border-style isn’t animatable? We can assume it isn’t, since there
is no logical midpoint between the two values of solid and dashed.
We can confirm by looking up the list of animatable properties in Appendix A or the specifications for the individual properties.

There will be 21 transitionend events in our scenario in which 8
specific properties are listed, as those 8 include several shorthand
properties that have different values in the pre and post states.
In the case of all, there will be at least 21 transitionend events:
one for each of the longhand values making up the 8 properties we know are included in the pre and
post states, and possibly from others that are inherited or declared in
other style blocks impacting the element:

You can listen for transitionend events in a manner like this:

document.querySelector('div').addEventListener('transitionend',
    function (e) {
      console.log(e.propertyName);
});

The transitionend event includes three event specific attributes:

  1. propertyName, which is the name of the CSS property that just finished transitioning.

  2. pseudoElement, which is the pseudoelement upon which the transition occurred, preceded by two semicolons,
    or an empty string if the transition was on a regular DOM node.

  3. elapsedTime, which is the amount of time the transition took to run, in
    seconds; usually this is the time listed in the transition-duration
    property.

The transitionend event only occurs if the property successfully
transitions to the new value. The transitioned event doesn’t fire if
the transition was interrupted, such as by another change to the same property on the same element.

When the properties return to their initial value, another
transitionend event occurs. This event occurs as long as the
transition started, even if it didn’t finish its initial transition in the
original direction.

Setting Transition Duration

The transition-duration property takes as its value a comma-separated
list of lengths of time, in seconds (s) or milliseconds (ms). These values describe the time it will
take to transition from one state to another.

If reverting between two states, and the duration is only present in a declaration applying to one of those states, the transition duration will only impact the transition to that state. Consider:

input:invalid {
    transition-duration: 1s;
    background-color: red;
}

input:valid {
    transition-duration: 0.2s;
    background-color: green;
}

If different values for the transition-duration are declared, the
duration of the transition will be the transition-duration value declared in the rule block to which it is transitioning. In the preceding example, it will take 1 second for the input to change to a red background when it becomes invalid, and only 200 milliseconds to transition to a green background when it becomes valid.

The value of the transition-duration property is a
positive value in either seconds (s) or milliseconds (ms). The time unit of ms or s is required by the specification, even if the
duration is set to 0s. By default, properties change from one value to the next instantly, showing no visible animation, which is why the default value for the duration of a transition is 0s.

Unless there is a positive value for transition-delay set on a property, if transition-duration is omitted, it is as if no transition-property declaration had been
applied—with no transitionend event occuring. As long
as the total time set for a transition to occur is greater than zero seconds—which can happen with a duration of 0s or when the
transition-duration is omitted and defaults to 0s—the transition will still be
applied, and a transitionend event will occur when the transition
finishes.

Negative values for transition-duration are invalid, and, if included, will invalidate the entire
property value.

Using the same super-long transition-property declaration from before, we
can declare a single duration for all the properties or individual
durations for each property, or we can make alternate properties animate
for the same length of time. We can declare a single duration that applies to all properties during the transition by including a single transition-duration value:

 div {
    color: #ff0000;
    ...
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 200ms;
}

We can also declare the same number of comma-separated time
values for the transition-duration property value as the CSS
properties listed in the transition-property property value. If
we want each property to transition over a different length of time, we
have to include a different comma-separated value for each property name
declared:

div {
    color: #ff0000;
    ...
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 200ms, 180ms, 160ms, 140ms, 120ms, 100ms, 1s, 2s;
}

If the number of properties declared does not match the number of
durations declared, the browser has specific rules on how to handle the
mismatch. If there are more durations than properties, the extra
durations are ignored. If there are more properties than durations, the
durations are repeated. In this example, color, border-radius, opacity, and width have a duration of 100 ms; border, transform, box-shadow, and padding will be set to 200 ms:

div {
    ...
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 100ms, 200ms;
}

If we declare exactly two comma-separated durations, every odd property
will transition over the first time declared, and every even property
will transition over the second time value declared.

User experience is important. If a transition is too slow, the website will appear slow or
unresponsive, drawing unwanted focus to what should be a subtle effect.
If a transition is too fast, it may be too subtle to be noticed. While
you can declare any positive length of time you want for your
transitions, your goal is likely to provide an enhanced rather than
annoying user experience. Effects should last long enough to be seen,
but not so long as to be noticeable. Generally, the best effects range
between 100 and 200 milliseconds, creating a visible, yet not
distracting, transition.

We want a good user experience for our drop-down menu, so we set both properties to transition over 200 milliseconds:

nav li ul {
    transition-property: transform, opacity;
    transition-duration: 200ms;
    ...
}

Altering the Internal Timing of Transitions

Do you want your transition to start off slow and get faster, start off
fast and end slower, advance at an even keel, jump through various steps,
or even bounce? The transition-timing-function provides a way to
control the pace of the transition.

The transition-timing-function values include ease, linear,
ease-in, ease-out, ease-in-out, step-start, step-end,
steps(n, start)—where n is the number of steps—steps(n, end), and cubic-bezier(x1, y1, x2, y2). (These values are
also the valid values for the animation-timing-function and are
described in great detail in Chapter 18.)

The non-step keywords are easing timing functions that server as aliases for cubic Bézier mathematical functions that provide smooth curves. The
specification provides for five predefined easing functions, as shown in Table 17-1.

Table 17-1.
Supported keywords for cubic Bézier timing functions
Timing function Description Cubic Bezier value
cubic-bezier() Specifies a cubic-bezier curve cubic-bezier(x1, y1, x2, y2)
ease Starts slow, then speeds up, then slows down, then ends very slowly cubic-bezier(0.25, 0.1, 0.25, 1)
linear Proceeds at the same speed throughout transition cubic-bezier(0, 0, 1, 1)
ease-in Starts slow, then speeds up cubic-bezier(0.42, 0, 1, 1)
ease-out Starts fast, then slows down cubic-bezier(0, 0, 0.58, 1)
ease-in-out Similar to ease; faster in the middle, with a slow start but not as slow at the end cubic-bezier(0.42, 0, 0.58, 1)

Cubic Bézier curves, including the underlying curves defining the
five named easing functions defined in Table 17-1 and displayed in Figure 17-3, take four numeric parameters. For example,
linear is the same as cubic-bezier(0, 0, 1, 1). The first and third
cubic Bézier function parameter values need to be between 0 and +1.

Figure 17-3. Curve representations of named cubic Bézier functions

The four numbers in a cubic-bezier() function define the x and y coordinates of two handles within a box. These handles are the endpoints of lines that stretch from the bottom-left and top-right corners of the box. The curve is constructed using the two corners, and the two handles’ coordinates, via a Bézier function.

To get an idea of how this works, look at the curves and their corresponding values, as shown in Figure 17-4.

Figure 17-4. Four Bézier curves and their cubic-bezier() values (via http://cubic-bezier.com)

Consider the first example. The first two values, corresponding to x1 and y1, are 0.5 and 1. If you go halfway across the box (x1 = 0.5) and all the way to the top of the box (y1 = 1), you land at the spot where the first handle is placed. Similarly, the coordinates 0.5,0 for x2,y2 describes the point at the center bottom of the box, which is where the second handle is placed. The curve shown there results from those handle placements.

In the second example, the handle positions are switched, with the resulting change in the curve. Ditto for the third and fourth examples, which are inversions of each other. Notice how different the resulting curve is when switching the handle positions.

The predefined key terms are fairly limited. To better follow the principles of animation, you
may want to use a cubic Bézier function with four float values instead
of the predefined key words.
If you’re a whiz at calculus or have a lot of experience with programs like Freehand or Illustrator, you might be able to invent cubic Bézier
functions in your head; otherwise, there are online tools that
let you play with different values, such as
http://cubic-bezier.com/, which lets you compare the
common keywords against each other, or against your own cubic Bézier
function.

As shown in Figure 17-5, the website http://easings.net provides many additional cubic Bézier function
values you can use to provide for a more realistic, delightful
animation.

Figure 17-5. Useful author-defined cubic Bézier functions (from http://easings.net)

While the authors of the site named their animations, the
preceding names are not part of the CSS specifications, and must be written
as follows:

Unofficial name Cubic Bézier function value

easeInSine

cubic-bezier(0.47, 0, 0.745, 0.715)

easeOutSine

cubic-bezier(0.39, 0.575, 0.565, 1)

easeInOutSine

cubic-bezier(0.445, 0.05, 0.55, 0.95)

easeInQuad

cubic-bezier(0.55, 0.085, 0.68, 0.53)

easeOutQuad

cubic-bezier(0.25, 0.46, 0.45, 0.94)

easeInOutQuad

cubic-bezier(0.455, 0.03, 0.515, 0.955)

easeInCubic

cubic-bezier(0.55, 0.055, 0.675, 0.19)

easeOutCubic

cubic-bezier(0.215, 0.61, 0.355, 1)

easeInOutCubic

cubic-bezier(0.645, 0.045, 0.355, 1)

easeInQuart

cubic-bezier(0.895, 0.03, 0.685, 0.22)

easeOutQuart

cubic-bezier(0.165, 0.84, 0.44, 1)

easeInOutQuart

cubic-bezier(0.77, 0, 0.175, 1)

easeInQuint

cubic-bezier(0.755, 0.05, 0.855, 0.06)

easeOutQuint

cubic-bezier(0.23, 1, 0.32, 1)

easeInOutQuint

cubic-bezier(0.86, 0, 0.07, 1)

easeInExpo

cubic-bezier(0.95, 0.05, 0.795, 0.035)

easeOutExpo

cubic-bezier(0.19, 1, 0.22, 1)

easeInOutExpo

cubic-bezier(1, 0, 0, 1)

easeInCirc

cubic-bezier(0.6, 0.04, 0.98, 0.335)

easeOutCirc

cubic-bezier(0.075, 0.82, 0.165, 1)

easeInOutCirc

cubic-bezier(0.785, 0.135, 0.15, 0.86)

easeInBack

cubic-bezier(0.6, -0.28, 0.735, 0.045)

easeOutBack

cubic-bezier(0.175, 0.885, 0.32, 1.275)

easeInOutBack

cubic-bezier(0.68, -0.55, 0.265, 1.55)

Step timing

There are also step timing functions available, as well as two predefined
step values:

Timing function Definition

step-start

Stays on the final keyframe throughout transition. Equal to steps(1, start).

step-end

Stays on the initial keyframe throughout transition. Equal to steps(1, end).

steps(n, start)

Displays n stillshots, where the first stillshot is n/100 percent of the way through the transition.

steps(n, end)

Displays n stillshots, staying on the initial values for the first n/100 percent of the time.

As Figure 17-6 shows, the stepping functions show the progression of the transition from the
initial value to the final value in steps, rather than as a smooth curve.

Figure 17-6. Step timing functions

The step functions allow you to divide the transition over equidistant
steps. The functions define the number and direction of steps. There are
two direction options: start and end. With start, the first step
happens at the animation start. With end, the last step happens at the
animation end. For example, steps(5, end) would jump through the
equidistant steps at 0%, 20%, 40%, 60%, and 80%; and steps(5, start)
would jump through the equidistant steps at 20%, 40%, 60%, 80%, and 100%.

The step-start function is the same as steps(1, start). When used, transitioned property values stay on their final values from the
beginning until the end of the transition. The step-end function,
which is the same as steps(1, end), sets transitioned values to their initial values, staying there throughout the transition’s duration.

Note

Step timing, and especially the precise meaning of start and end, is discussed in depth in Chapter 18.

Continuing on with the same super-long transition-property declaration we’ve used before, we can declare a single timing function for all the properties, or
define individual timing functions for each property and so on. Here, we set all the transitioned properties to a single duration:

div {
    transition-property: color, border-width, border-color, border-radius,
        transform, opacity, box-shadow, width, padding;
    transition-duration: 200ms;
    transition-timing-function: ease-in;
}

We can also create a horrible user experience by making every property
transition at a different rhythm, like this:

Always remember that the transition-timing-function does
not change the time it takes to transition properties: that is set with
the transition-duration property. It just changes how the
transition progresses during that set time. Consider the following:

div {
    ...
    transition-property: color, border-width, border-color, border-radius,
        transform, opacity, box-shadow, width, padding;
    transition-duration: 200ms;
    transition-timing-function: ease, ease-in, ease-out, ease-in-out, linear,
        step-end, step-start, steps(5, start), steps(3, end);
}

If we include these nine different timing functions for the nine
different properties, as long as they have the same transition duration
and delay, all the properties start and finish transitioning at
the same time. The timing function controls how the transition
progresses over the duration of the transition, but does not alter the time it takes for the transition to
finish. (The preceding transition would be a terrible user experience, by the way. Please don’t do that.)

The best way to familiarize yourself with
the various timing functions is to play with them and see which one works best
for the effect you’re looking for. While testing, set a relatively long
transition-duration to better visualize the difference between the
various functions. At higher speeds, you may not be able to tell the
difference with the easing function; just don’t forget to set it back to
a faster speed before publishing the result to the web!

Delaying Transitions

The transition-delay property enables you to introduce a time delay
between when the change that initiates the transition is applied to an
element, and when the transition begins.

A transition-delay of 0s (the default) means the transition will begin immediately—it will start executing as soon as the state of the element is altered. This is familiar from the instant-change effect of a:hover, for example.

With a value other than 0s, the <time> value of
transition-delay defines the time offset from the moment the property
values would have changed, had no transition or transition-property
been applied, until the property values declared in the transition or
transition-property value begin animating to their final values.

Interestingly, negative values of time are valid. The effects you can create with
negative transition-delays are described in “Negative delay values”.

Continuing with the 8- (or 21-) property transition-property declaration we’ve been using, we can make all the properties start transitioning right away by omitting the transition-delay property, or by including it with a value of 0s. Another possibility is to start half the transitions right away, and the rest 200 milliseconds later, as in the the following:

div {
    transition-property: color, border, border-radius, transform, opacity,
        box-shadow, width, padding;
    transition-duration: 200ms;
    transition-timing-function: linear;
    transition-delay: 0s, 200ms;
}

By including transition-delay: 0s, 200ms on a series of properties,
each taking 200 milliseconds to transition, we make color, border-radius, opacity, and width begin their
transitions immediately. All the rest begin
their transitions as soon as the odd transitions have completed, because their transition-delay is equal to the transition-duration applied to all the properties.

As with transition-duration and transition-timing-function, when the
number of comma-separated transition-delay values outnumbers the
number of comma-separated transition-property values, the extra delay
values are ignored. When the number of comma-separated
transition-property values outnumbers the number of comma-separated
transition-delay values, the delay values are repeated.

We can even declare nine different transition-delay values so that each
property begins transitioning after the previous property has
transitioned, as follows:

div {
    ...
    transition-property: color, border-width, border-color, border-radius,
        transform, opacity, box-shadow, width, padding;
    transition-duration: 200ms;
    transition-timing-function: linear;
    transition-delay: 0s, 0.2s, 0.4s, 0.6s, 0.8s, 1s, 1.2s, 1.4s, 1.6s;
}

In this example, we declared each transition to
last 200 milliseconds with the transition-duration property. We then declare a
transition-delay that provides comma-separated delay values for each
property that increment by 200 milliseconds, or 0.2 seconds—the same time as the
duration of each property’s transition. Each property
starts transitioning at the point the previous property has finished.

We can use math to give every transitioning property different
durations and delays, ensuring they all complete transitioning at the
same time:

div {
    ...
    transition-property: color, border-width, border-color, border-radius,
        transform, opacity, box-shadow, width, padding;
    transition-duration: 1.8s, 1.6s, 1.4s, 1.2s, 1s, 0.8s, 0.6s, 0.4s, 0.2s;
    transition-timing-function: linear;
    transition-delay: 0s, 0.2s, 0.4s, 0.6s, 0.8s, 1s, 1.2s, 1.4s, 1.6s;
}

In this example, each property completes transitioning at the
1.8-second mark, but each with a different duration and delay. For each
property, the transition-duration value plus the transition-delay
value will add up to 1.8 seconds.

Generally, you want all the transitions to begin at the same time. You can make that happen by
including a single transition-delay value, which gets applied to all
the properties. In our drop-down menu in Figure 17-1, we include a delay of
50 milliseconds. This delay is not long enough for the user to notice and will not
cause the application to appear slow. Rather, a 50-millisecond delay can help
prevent the navigation from shooting open unintentionally as the user accidentally passes over, or hovers over, the menu items while moving the cursor from one part of the page or app to another.

Negative delay values

A negative value for
transition-delay that is smaller than the transition-duration will
cause the transition to
start immediately, partway through the transition. For example:

div {
  transform: translateX(0);
  transition-property: transform;
  transition-duration: 200ms;
  transition-delay: -150ms;
  transition-timing-function: linear;
}
div:hover {
  transform: translateX(200px);
}

Given the transition-delay of -150ms on a 200ms
transition, the transition will start three-quarters of the way through
the transition and will last 50 milliseconds. In that scenario, with a linear
timing function, it jumps to being translated 150px along the
x-axis immediately on hover and then animates the translation from
150 pixels to 200 pixels over 50 milliseconds.

If the absolute value of the negative transition-delay is greater than
or equal to the transition-duration, the change of property values is
immediate, as if no transition had been applied, and no transitionend event occurs.

When transitioning back from the hovered state to the original state, by
default, the same value for the transition-delay is applied. In the
preceding scenario, since the transition-delay is not overridden in the
hover state, it will jump 75% of the way back (or 25% of the way through
the original transition) and then transition back to the initial state.
On mouseout, it will jump to being translated 50 pixels along the x-axis and
then take 50 milliseconds to return to its initial position of being translated
0 pixels along the x-axis.

The transition Shorthand

The transition shorthand property combines the four properties covered thus far—transition-property, transition-duration,
transition-timing-function, and transition-delay—into a single
shorthand property.

The transition property accepts the value of none, or any number of
comma-separated list of single transitions. A single transition
contains a single property to transition, or the keyword all to
transition all the properties; the duration of the
transition; the timing function; and the delay.

If a single transition within the transition shorthand omits the
property to transition, the single transition
will default to all. If the transition-timing-function value is
omitted, it will default to ease. If only one time value is included,
that will be the duration, and there will be no delay, as if
transition-delay were set to 0s.

Within each single transition, the order of the duration versus the delay
is important: the first value that can be parsed as a time will be set
as the duration. If an additional time value is found before the comma or the
end of the statement, that will be set as the delay.

Here are three equivalent ways to write the same transition effects:

nav li ul {
    transition: transform 200ms ease-in 50ms,
                  opacity 200ms ease-in 50ms;
}

nav li ul {
    transition: all 200ms ease-in 50ms;
}

nav li ul {
    transition: 200ms ease-in 50ms;
}

In the first example, we see shorthand for each of
the two properties. Because we are transitioning all the properties that
change on hover, we could use the keyword all, as shown in the second
example. And, since all is the default value, we could write the
shorthand with just the duration, timing function, and delay. Had we used
ease instead of ease-in, we could have omitted the timing function,
since ease is the default.

We had to include the duration, or no transition would be visible. In
other words, the only portion of the transition property value that can truly be
considered required is transition-duration.

If we only wanted to delay the change from closed menu to open menu
without a gradual transition, we would still need to include a duration of 0s. Remember, the first value parsable as time will be set as the
duration, and the second one will be set as the delay:

nav li ul {
    transition: 0s 200ms; ...
Warning

This transition will wait 200 milliseconds, then show the drop-down fully open and
opaque with no gradual transition. This is horrible user experience.
Though if you switch the selector from nav li ul to *, it might make
for an April Fools’ joke.

If there is a comma-separated list of transitions (versus just a single
declaration) and the word none is included, the entire transition
declaration is invalid and will be ignored:

div {
    transition-property: color, border-width, border-color, border-radius,
        transform, opacity, box-shadow, width, padding;
    transition-duration: 200ms, 180ms, 160ms, 140ms, 120ms, 100ms, 1s, 2s, 3s;
    transition-timing-function: ease, ease-in, ease-out, ease-in-out, linear,
        step-end, step-start, steps(5, start), steps(3, end);
    transition-delay: 0s, 0.2s, 0.4s, 0.6s, 0.8s, 1s, 1.2s, 1.4s, 1.6s;
}

div {
    transition:
        color 200ms,
        border-width 180ms ease-in 200ms,
        border-color 160ms ease-out 400ms,
        border-radius 140ms ease-in-out 600ms,
        transform 120ms linear 800ms,
        opacity 100ms step-end 1s,
        box-shadow 1s step-start 1.2s,
        width 2s steps(5, start) 1.4s,
        padding 3s steps(3, end) 1.6s;
}

The two preceding CSS rule blocks are functionally equivalent: you can
declare comma-separated values for the four longhand transition
properties, or you can include a comma-separated list of multiple shorthand
transitions. You can’t, however, mix the two:
transition: transform, opacity 200ms ease-in 50ms will ease in the
opacity over 200 milliseconds after a 50-millisecond delay, but the transform change will be
instantaneous, with no transitionend event.

In Reverse: Transitioning Back to Baseline

In the preceding examples, we’ve declared a single transition. All our
transitions have been applied in the default state and initiated with a
hover. With these declarations, the properties return back to the
default state via the same transition on mouseout, with a reversing of
the timing function and a duplication of the delay.

With transition declarations only in the global state, both the hover
and mouseout states use the same transition declaration: the
selector matches both states. We can override this duplication of the
entire transition or just some of the transition properties by
including different values for transition properties in the global
(versus the hover-only) state.

When declaring transitions in multiple states, the transition included
is to that state:

a {
    background: yellow;
    transition: 200ms background-color linear 0s;
  }
a:hover {
    background-color: orange;
    /* delay when going TO the :hover state */
    transition-delay: 50ms;
  }

In this scenario, when the user hovers over a link, the background
color waits 50 milliseconds before transitioning to orange. When the user
mouses off the link, the background starts transitioning back to yellow
immediately. In both directions, the transition takes 200 milliseconds to complete, and the gradual change proceeds in a linear manner. The 50 milliseconds is included in the :hover (orange) state. The delay happens, therefore, as the background changes to orange.

In our drop-down menu example, on :hover, the menu appears and grows
over 200 milliseconds, easing in after a delay of 50 milliseconds. The transition is set with the transition property in the default (non-hovered) state. When the user mouses out, the properties revert over 200 milliseconds, easing out after a delay of 50 milliseconds. This reverse effect is responding to the transition value from the non-hovered state. This is the default behavior, but it’s something we can control. The best user experience is
this default behavior, so you likely don’t want to alter it—but it’s important to know that you can.

If we want the closing of the menu to be jumpy and slow (we don’t want
to do that; it’s bad user experience. But for the sake of this example,
let’s pretend we do), we can declare two different transitions:

nav ul ul {
  transform: scale(1, 0);
  opacity: 0;
  ...
  transition: all 4s steps(8, start) 1s;
}
nav li:hover ul {
  transform: scale(1, 1);
  opacity: 1;
  transition: all 200ms linear 50ms;
}

Transitions are to the to state: when there’s a style change, the
transition properties used to make the transition are the new values of
the transition properties, not the old ones. We put the smooth, linear
animation in the :hover state. The transition that applies is the one
we are going toward. In the preceding example, when the user hovers over
the drop-down menu’s parent li, the opening of the drop-down menu will
be gradual but quick, lasting 200 milliseconds after a delay of 50 milliseconds. When the user
mouses off the drop-down menu or its parent li, the transition will
wait one second and take four seconds to complete, showing eight steps along the
way.

When we only have one transition, we put it in the global from state,
as you want the transition to occur toward any state, be that a hovering or
a class change. Because we want the transition to occur with any change,
we generally put the only transition declaration in the initial, default (least
specific) block. If you do want to exert more control and provide for
different effects depending on the direction of the transition, make
sure to include a transition declaration in all of the possible class
and UI states.

Warning

Beware of having transitions on both ancestors and descendants.
Transitioning properties soon after making a change that transition ancestral or descendant nodes can have unexpected outcomes. If
the transition on the descendant completes before the transition on the
ancestor, the descendant will then resume inheriting the (still
transitioning) value from its parent. This effect may not be what you
expected.

Reversing interrupted transitions

When a transition is interrupted before it is able to finish (such as
mousing off of our drop-down menu example before it finishes opening),
property values are reset to the values they had before the transition
began, and the properties transition back to those values. Because
repeating the duration and timing functions on a reverting partial
transition can lead to an odd or even bad user experience, the CSS
transitions specification provides for making the reverting transition
shorter.

In our menu example, we have a transition-delay of
50ms set on the default state and no transition properties declared on the hover state; thus, browsers will wait 50 milliseconds before beginning the
reverse or closing transition.

When the forward animation finishes transitioning to the final values and the transitionend event
is fired, all browsers will duplicate the transition-delay in the
reverse states.

As Table 17-2 shows, if the transition didn’t finish—say, if the user moved off the navigation before the transition finished—all browsers except Microsoft Edge will repeat the delay in the reverse direction. Some browsers replicate the transition-duration as well, but
Edge and Firefox have implemented the specification’s reverse
shortening factor.

Table 17-2. Unfinished transition reverse behavior by browser
Browser Reverse delay Transition time Elapsed time

Chrome

Yes

200 ms

0.200 s

Chrome

Yes

200 ms

0.250 s

Safari

Yes

200 ms

0.200 s

Firefox

Yes

38 ms

0.038 s

Opera

Yes

200 ms

0.250 s

Edge

No

38 ms

0.038 s

Let’s say the user moves off that menu 75 milliseconds after it started
transitioning. This means the drop-down menu will animate closed without ever being fully opened and fully opaque. The browser should have
a 50-millisecond delay before closing the menu, just like it waited 50 milliseconds before
starting to open it.

This is actually a good user experience, as it
provides a few milliseconds of delay before closing, preventing jerky
behavior if the user accidentally navigates off the menu. As shown in
Table 17-2, all browsers do this, except Microsoft
Edge.

Even though we only gave the browser 75 milliseconds to partially open the
drop-down menu before closing
the menu, some browsers will take 200 milliseconds—the full value of the
transition-duration property—to revert. Other browsers, including
Firefox and Edge, have implemented the CSS specification’s reversing
shortening factor and the reversing-adjusted start value. When
implemented, the time to complete the partial transition in the reverse
direction will be similar to the original value, though not necessarily
exact.

In the case of a step timing function, Firefox and Edge will take the
time, rounded down to the number of steps the function has completed. For example,
if the transition was 10 seconds with 10 steps, and the properties
reverted after 3.25 seconds, ending a quarter of the way between the
third and fourth steps (completing 3 steps, or 30% of the transition), it
will take 3 seconds to revert to the previous values. In the following
example, the width of our div will grow to 130 pixels wide before it begins
reverting back to 100 pixels wide on mouseout:

div {
    width: 100px;
    transition: width 10s steps(10, start);
}
div:hover {
    width: 200px;
}

While the reverse duration will be rounded down to the time it took to
reach the most recently-executed step, the reverse direction will be split into the originally
declared number of steps, not the number of steps that completed. In our
3.25-second case, it will take 3 seconds to revert through 10 steps. These
reverse transition steps will be shorter in duration at 300 milliseconds each, each step shrinking the width by 3 pixels, instead of 10 pixels.

If we were animating a sprite by
transitioning the background-position , this would look really bad. The
specification and implementations may change to make the reverse
direction take the same number of steps as the partial transition.
Other browsers currently take 10 seconds, reverting the progression of
the 3 steps over 10 seconds across 10 steps—taking a full second to
grow the width in 3-pixel steps.

Browsers that haven’t implemented shortened reversed timing will take the full 10 seconds, instead of only
3, splitting the transition into 10 steps, to reverse the 30% change.
Whether the initial transition completed or not, these browsers will
take the full value of the initial transition duration, less the
absolute value of any negative transition-delay, to reverse the
transition, no matter the timing function. In the steps case just shown, the
reverse direction will take 10 seconds. In our navigation example, it
will reverse over 200 milliseconds, whether the navigation has fully scaled up or not.

For browsers that have implemented the reversing timing adjustments, if the
timing function is linear, the duration will be the same in both
directions. If the timing function is a step function, the reverse
duration will be equal to the time it took to complete the last
completed step. All other cubic-bezier functions will have a duration that is proportional to progress the initial transition made before being interrupted. Negative transition-delay values are also proportionally shortened. Positive delays remain unchanged in both directions.

No browser will have a transitionend for the hover state, as the
transition did not end; but all browsers will have a transitionend
event in the reverse state when the menu finishes collapsing. The
elapsedTime for that reverse transition depends on whether the browser
took the full 200 milliseconds to close the menu, or if the browser takes as long to close the menu as it did to partially open the menu.

To override these values, include transition properties in both the initial and final states (e.g., both the unhovered and hovered styles). While this does not impact the reverse shortening, it does provide more control.

Animatable Properties and Values

Before implementing transitions and animations, it’s important to
understand that not all properties are animatable. You can
transition (or animate) any animatable CSS properties; but which
properties are animatable?

Note

While we’ve included a list of these properties in Appendix A, CSS is evolving, and the animatable properties list will likely get new additions.

One key to developing a sense for which properties can be animated is to identify which have values that can be interpolated.
Interpolation is the construction of data points between the values of
known data points. The key guideline to determining if a property value
is animatable is whether the computed value can be interpolated. If a property’s computed values are keywords, they can’t be interpolated; if its keywords compute to a number of some sort, they can be.
The quick rule of thought is that if you can determine a midpoint
between two property values, those property values are probably
animatable.

For example, the display values like
block and inline-block aren’t numeric and therefore don’t have a
midpoint; they aren’t animatable. The transform property values of
rotate(10deg) and rotate(20deg) have a midpoint of rotate(15deg); they are animatable.

The border property is shorthand for border-style, border-width,
and border-color (which, in turn, are themselves shorthand properties
for the four side values). While there is no midpoint between any of the
border-style values, the border-width property length units are
numeric, so they can be animated. The keyword values of medium, thick, and
thin have numeric equivalents and are interpolatable: the computed
value of the border-width property computes those keywords to lengths.

In the border-color value, colors are numeric—the named
colors all represent hexadecimal color values—so colors are
animatable as well. If you transition from border: red solid 3px to
border: blue dashed 10px, the border width and border colors will
transition at the defined speed, but border-style will jump from
solid to dashed as soon as the transition begins (after any delay).

As noted (see Appendix A), numeric values tend to be animatable. Keyword values that
aren’t translatable to numeric values generally aren’t. CSS functions
that take numeric values as parameters generally are animatable. One
exception to this rule is visibility: while there is no
midpoint between the values of visible and hidden, visibility
values are interpolatable between visible and not-visible. When it comes
to the visibility property, either the initial value or the destination
value must be visible or no interpolation can happen. The value will
change at the end of the transition from visible to hidden. For a
transition from hidden to visible, it changes at the start of the
transition.

auto should generally be considered a non-animatable value and should
be avoided for animations and transitions. According to the
specification, it is not an animatable value, but some browsers
interpolate the current numeric value of auto (such as height: auto)
to be 0px. auto is non-animatable for properties like
height, width, top, bottom, left, right, and margin.

Often an alternative property or value may work. For example,
instead of changing height: 0 to height: auto, use max-height: 0
to max-height: 100vh, which will generally create the expected
effect. The auto value is animatable for min-height and min-width,
since min-height: auto actually computes to 0.

How Property Values Are Interpolated

Interpolation can happen when values falling between two or more known values can be determined. Interpolatable values can be transitioned and animated.

Numbers are interpolated as floating-point numbers. Integers are
interpolated as whole numbers, incrementing or decrementing as whole
numbers.

In CSS, length and percentage units are translated into real numbers.
When transitioning or animating calc(), or from one type of length to
or from a percentage, the values will be converted into a calc()
function and interpolated as real numbers.

Colors, whether they are HSLA, RGB, or named colors like aliceblue, are translated to their RGBA equivalent values for transitioning, and interpolated across the RGBA color space.

When animating font weights, if you use keywords like bold, they’ll
be converted to numeric values and animated in steps of multiples of
100. This may change in the future, as font weights may be permitted to take any integer value, in which case weights will be interpolated as integers instead of multiples of 100.

When including animatable property values that have more than one
component, each component is interpolated appropriately for that
component. For example, text-shadow has up to four components: the
color, x, y, and blur. The color is interpolated as color: the x, y,
and blur components are interpolated as lengths. Box shadows have two
additional optional properties: inset (or lack thereof) and spread.
spread, being a length, is interpolated as such. The inset keyword
cannot be converted to a numeric equivalent: you can transition from one
inset shadow to another inset shadow, or from one drop shadow to another
drop shadow multicomponent value, but there is no way to gradually
transition between inset and drop shadows.

Similar to values with more than one component, gradients can be
transitioned only if you are transitioning gradients of the same type
(linear or radial) with equal numbers of color stops. The colors of each
color stop are then interpolated as colors, and the position of each
color stop is interpolated as length and percentage units.

Interpolating repeating values

When you have simple lists of other types of properties, each item in the list is interpolated appropriately for that type—as long as the
lists have the same number of items or repeatable items, and each pair
of values can be interpolated:

.img {
    background-image:
        url(1.gif), url(2.gif), url(3.gif), url(4.gif),
        url(5.gif), url(6.gif), url(7.gif), url(8.gif),
        url(9.gif), url(10.gif), url(11.gif), url(12.gif);
    background-size: 10px 10px, 20px 20px, 30px 30px, 40px 40px;
    transition: background-size 1s ease-in 0s;
}
.img:hover {
    background-size: 25px 25px, 50px 50px, 75px 75px, 100px 100px;
}

For example, in transitioning four background-sizes, with all the sizes in
both lists listed in pixels, the third background-size from the
pretransitioned state can gradually transition to the third
background-size of the transitioned list. In the preceding example,
background images 1, 6, and 10 will transition from 10px to 25px in height and width when hovered. Similarly, images 3, 7, and 11
will transition from 30px to 75px, and so forth.

Thus, the background-size values
are repeated three times, as if the CSS had been written as:

.img {
    ...
    background-size: 10px 10px, 20px 20px, 30px 30px, 40px 40px,
                     10px 10px, 20px 20px, 30px 30px, 40px 40px,
                     10px 10px, 20px 20px, 30px 30px, 40px 40px;
    ...
}
.img:hover {
    background-size: 25px 25px, 50px 50px, 75px 75px, 100px 100px,
                     25px 25px, 50px 50px, 75px 75px, 100px 100px,
                     25px 25px, 50px 50px, 75px 75px, 100px 100px;
}

If a property doesn’t have enough comma-separated values to match the
number of background images, the list of values is repeated until there are
enough, even when the list in the :hover state doesn’t match the
initial state:

.img:hover {
    background-size: 33px 33px, 66px 66px, 99px 99px;
}

If we transitioned from four background-size declarations in the initial
state to three background-size declarations in the :hover state, all in
pixels, still with 12 background images, the hover and initial state
values are repeated (three and four times respectively) until we have the 12
necessary values, as if the following had been declared:

.img {
    ...
    background-size: 10px 10px, 20px 20px, 30px 30px,
                     40px 40px, 10px 10px, 20px 20px,
                     30px 30px, 40px 40px, 10px 10px,
                     20px 20px, 30px 30px, 40px 40px;
    ...
}
.img:hover {
    background-size: 33px 33px, 66px 66px, 99px 99px,
                     33px 33px, 66px 66px, 99px 99px,
                     33px 33px, 66px 66px, 99px 99px,
                     33px 33px, 66px 66px, 99px 99px;
}

If a pair of values cannot be interpolated—for example, if the background-size changes from contain in the default state to cover when hovered—then, according to the
specification, the lists are not interpolatable. However, some browsers
ignore that particular pair of values for the purposes of the transition, but
still animate the interpolatable values.

There are some property values that can animate if the browser can infer
implicit values. For example, for shadows, the browser will
infer an implicit shadow box-shadow: transparent 0 0 0 or
box-shadow: inset transparent 0 0 0, replacing any values not
explicitly included in the pre- or post-transition state. These examples are in the chapter files for this book.

Only the interpolatable values trigger transitionend events.

As noted previously, visibility animates differently than other properties:
if animating or transitioning to or from visible, it is interpolated
as a discrete step. It is always visible during the transition or
animation as long as the timing function output is between 0 and 1. It
will switch at the beginning if the transition is from hidden to
visible. It will switch at the end if the transition is from
visible to hidden. Note that this can be controlled with the step timing
functions.

If you accidentally include a property that can’t be transitioned, fear
not. The entire declaration will not fail. The browser will simply not transition the property that is not
animatable. Note that the non-animatable
property or nonexistent CSS property is not exactly ignored. The browser
passes over unrecognized or non-animatable properties, keeping their
place in the property list order to ensure that the other
comma-separated transition properties described next are not applied on
the wrong properties.2

Note

Transitions can only occur on properties that are not currently
being impacted by a CSS animation. If the element is being animated,
properties may still transition, as long as they are not properties that
are currently controlled by the animation. CSS animations are covered in
Chapter 18.

Fallbacks: Transitions Are Enhancements

Transitions have excellent browser support. All browsers, including
Safari, Chrome, Opera, Firefox, Edge, and Internet Explorer (starting
with IE10) support CSS transitions.

Transitions are user-interface (UI) enhancements. Lack of full support should not prevent
you from including them. If a browser doesn’t support CSS transitions,
the changes you are attempting to transition will still be applied: they
will just “transition” from the initial state to the end state
instantaneously when the style recomputation occurs.

Your users may miss out on an interesting (or possibly annoying)
effect, but will not miss out on any content.

As transitions are generally progressive enhancements, there is no need
to polyfill for archaic IE browsers. While you could use a JavaScript
polyfill for IE9 and earlier, and prefix your transitions for Android
4.3 and earlier, there is likely little need to do so.

Printing Transitions

When web pages or web applications are printed, the stylesheet for print
media is used. If your style element’s media attribute matches only
screen, the CSS will not impact the printed page at all.

Often, no media attribute is included; it is as if media="all" were
set, which is the default. Depending on the browser, when a transitioned
element is printed, either the interpolating values are ignored, or the
property values in their current state are printed.

You can’t see the element transitioning on a piece of paper, but in some
browsers, like Chrome, if an element transitioned from one state to
another, the current state at the time the print function is called will
be the value on the printed page, if that property is printable. For
example, if a background color changed, neither the pre-transition or the
post-transition background color will be printed, as background colors
are generally not printed. However, if the text color mutated from one
value to another, the current value of color will be what gets printed
on a color printer or PDF.

In other browsers, like Firefox, whether the pre-transition or
post-transition value is printed depends on how the transition was
initiated. If it initiated with a hover, the non-hovered value will be
printed, as you are no longer hovering over the element while you interact
with the print dialog. If it transitioned with a class addition, the
post-transition value will be printed, even if the transition hasn’t
completed. The printing acts as if the transition properties are ignored.

Given that there are separate printstyle sheets or @media rules for
print, browsers compute style separately. In the print style, styles
don’t change, so there just aren’t any transitions. The printing acts as
if the property values changed instantly, instead of transitioning over
time.

1 Changing a background image may take longer than 16 milliseconds to decode and repaint to the page. This isn’t a transition; it is just poor performance.

2 This might change. The CSS Working Group is considering making all property values animatable, switching from one value to the next at the midpoint of the timing function if there is no midpoint between the pre and post values.

Comments are closed.