18. Transitions, Transforms, and Animation

18

Transitions, Transforms, and Animation

In This Chapter

Creating smooth transitions

Moving, rotating, and scaling elements

Combining transitions and transforms

A few words about 3-D transforms

Keyframe animations

We’ve seen CSS used for visual effects like rounded corners, color gradients, and drop shadows that previously had to be created with graphics. In this chapter, we’ll look at some CSS3 properties for producing animated interactive effects that were previously possible only with JavaScript or Flash.

We’ll start with CSS Transitions, a nifty way to make style changes fade smoothly from one to another. Then we’ll discuss CSS Transforms for repositioning, scaling, rotating, and skewing elements and look at how you can animate them with transitions. I’m going to close out the chapter with brief introductions to 3-D Transforms and CSS Animation, which are important to know about but are too vast a topic to cover here, so I’ll give you just a taste.

The problem with this chapter is that animation and time-based effects don’t work on paper, so I can’t show them off right here. I did the next best thing, though, and made the source code for the figures available in the materials for this chapter () in a folder called figures. Just open the file in your browser.

Ease-y Does It (CSS Transitions)

Picture, if you will, a link in a navigation menu that changes from blue to red when the mouse hovers over it. The background is blue…mouse passes over it…BAM! Red! It goes from state to state instantly, with no states in between. Now imagine putting your mouse over the link and having the background gradually change from blue to red, passing through several shades of purple on the way. It’s smoooooth. And when you remove the mouse, it fades back down to blue again.

That’s what CSS Transitions do. They smooth out otherwise abrupt changes to property values between two states over time by filling in the frames in between. Animators call that tweening. When used with reserve, CSS Transitions can add sophistication and polish to your interfaces and even improve usability.

CSS Transitions were originally developed by the WebKit team for the Safari browser, and they are a Working Draft at the W3C (see Note). Browser support for Transitions is excellent (see the “CSS Transition Support” sidebar), so there is no reason not to use them in your designs, particularly if you treat them as an enhancement. For example, on the rare non-supporting browser (I’m looking at you, old IE), our link snapping directly from blue to red is not a big deal.

NOTE

You can read CSS Transitions Module for yourself at

Transition Basics

Transitions are a lot of fun, so let’s give them a whirl. When applying a transition, you have a few decisions to make, each of which is set with a CSS property:

  • Which CSS property to change (transition-property) (Required)
  • How long it should take (transition-duration) (Required)
  • The manner in which the transition accelerates (transition-timing-function)
  • Whether there should be a pause before it starts (transition-delay)

Transitions require a beginning state and an end state. The element as it appears when it first loads is the beginning state. The end state needs to be triggered by a state change such as :hover, :focus, or :active, which is what we’ll be using for the examples in this chapter. You could use JavaScript to change the element (such as adding a class attribute) and use that as a transition trigger as well.

Let’s put that all together with a simple example. Here is that blue-to-red link you imagined earlier (Figure 18-1). There’s nothing special about the markup. I added a class so I could be specific about which links receive transitions.

Figure 18-1. The background color of this link gradually fades from blue to red over .3 seconds when awesome sauce a transition is applied.

The transition properties are applied to the object that will be transitioned—in this case, the a element in its normal state. You’ll see them in the set of other declarations for .smooth, like padding and background-color. I’ve changed the background color of the link to red by declaring the background-color for the :hover state (and :focus too, in case someone is tabbing through links with a keyboard).

The markup

<a href="…" class="smooth">awesomesauce</a>

The styles

.smooth {  display: block;   text-decoration:none;   text-align: center;  padding: 1em 2em;  width: 10em;  border-radius: 1.5em;  color: #fff;  background-color: mediumblue;  transition-property: background-color;  transition-duration: 0.3s;
}.smooth:hover, .smooth:focus {  background-color: red;}

Specifying the property

transition-property

Values: property-name | all | none

Default: all

Applies to: all elements, :before and :after pseudo-elements

Inherits: no

transition-property identifies the CSS property that is changing and that you want to transition smoothly. In our example, it’s the background-color. You can also change the foreground color, borders, dimensions, font- and text-related attributes, and many more. Table 18-1 lists the animatable CSS properties as of this writing. The general rule is that if its value is a color, length, or number, that property can be a transition property.

Table 18-1. Animatable CSS properties

Backgrounds

background-color
background-position

Borders and outlines

border-bottom-color
border-bottom-width
border-left-color
border-left-width
border-right-color
border-right-width
border-top-color
border-top-width
border-spacing
outline-color
outline-width

Color and opacity

color
opacity
visibility

Font and text

font-size
font-weight
letter-spacing
line-height
text-indent
text-shadow
word-spacing
vertical-align

Element box measurements

height
width
max-height
max-width
min-height
min-width
margin-bottom
margin-left
margin-top
padding-bottom
padding-left
padding-right
padding-top

Position

top
right
bottom
left
z-index
clip-path

Transforms (not in the spec as of this writing, but supported)

transform
transform-origin

How long should it take?

transition-duration

Values: time

Default: 0s

Applies to: all elements, :before and :after pseudo-elements

Inherits: no

transition-duration sets the amount of time it takes for the animation to complete in seconds (s) or milliseconds (ms). I’ve chosen .3 seconds, which is just enough to notice something happened but not so long that the transition feels sluggish or slows the user down. There is no correct duration, of course, but I’ve found that .2s seems to be a popular transition time for UI elements. Experiment to find the duration that makes sense for your application.

Timing Functions

transition-timing-function

Values: ease | linear | ease-in | ease-out | ease-in-out | step-start | step-end | steps | cubic-bezier(#,#,#,#)

Default: ease

Applies to: all elements, :before and :after pseudo-elements

Inherits: no

The property and the duration are required and form the foundation of a transition, but you can refine it further. There are a number of ways a transition can roll out over time. For example, it could start out fast and then slow down, start out slow and speed up, or stay the same speed all the way through, just to name a few possibilities. I think of it as the transition “style,” but in the spec, it is known as the timing function or easing function.

The timing function you choose can have a big impact on the feel and believability of the animation, so if you plan on using transitions and CSS animations, it is a good idea to get familiar with the options.

If I set the transition-timing-function to ease-in-out, the transition will start out slow, then speed up, then slow down again as it comes to the end state.

.smooth {  …  transition-property: background-color;   transition-duration: 0.3s;  transition-timing-function: ease-in-out;}

The transition-timing-function property takes one of the following keyword values:

ease

Starts slowly, accelerates quickly, and then slows down at the end. This is the default value and works just fine for most short transitions.

linear

Stays consistent from the transition’s beginning to end. Because it is so consistent, some say it has a mechanical feeling.

ease-in

Starts slowly, then speeds up.

ease-out

Starts out fast, then slows down.

ease-in-out

Starts slowly, speeds up, and then slows down again at the very end. It is similar to ease, but with less pronounced acceleration in the middle.

cubic-bezier(x1,y1,x2,y2)

The acceleration of a transition can be plotted with a curve called a Bezier curve. The steep parts of the curve indicate a fast rate of change, and the flat parts indicate a slow rate of change. Figure 18-2 shows the Bezier curves that represent the function keywords as well as a custom curve I created. You can see that the ease curve is a tiny bit flat in the beginning, gets very steep (fast), then ends flat (slow). The linear keyword, on the other hand, moves at a consistent rate for the whole transition.

You can get the feel of your animation just right by creating a custom curve. The site is a great tool for playing around with transition timing and generating the resulting code. The four numbers in the value represent the x and y positions of the start and end Bezier curve handles (the pink and blue dots in Figure 18-2).

Figure 18-2. Examples of Bezier curves from Cubic-Bezier.com. On the left is my custom curve that starts fast, slows down, and ends fast.

steps(#, start|end)

Divides the transitions into a number of steps as defined by a stepping function. The first value is the number of steps, and the start and end keywords define whether the change in state happens at the beginning (start) or end of each step. Step animation is especially useful for keyframe animation with sprite images. For a better explanation and examples, I recommend the article “Using Multi-Step Animations and Transitions,” by Geoff Graham on CSS-Tricks ().

step-start

Changes states in one step, at the beginning of the duration time (the same as steps(1,start)). The result is a sudden state change, the same as if no transition had been applied at all.

step-end

Changes states in one step, at the end of the duration time (the same as steps(1,end)).

It’s difficult to show the various options on a still page, but I have put together a little demo, which is illustrated in Figure 18-3 and available in the figures folder with the materials for this chapter. The width of each labeled element (white with a blue border) transitions over the course of 4 seconds when you hover over the green box. They all arrive at their full width at exactly the same time, but they get there in different manners. The image shown in Figure 18-3 was taken at the 2-second mark, halfway through the duration of the transition.

Note

The W3C has broken out the timing functions into their own spec so they are easier to share among modules. It is available at .

Figure 18-3. In this transition-timing-function demo, the elements reach full width at the same time but vary in the manner in which they get there. If you’d like to see it in action, the ch18_figures.html file is available with the materials for this chapter.

Setting a Delay

transition-delay

Values: time

Default: 0s

Applies to: all elements, :before and :after pseudo-elements

Inherits: no

The transition-delay property, as you might guess, delays the start of the animation by a specified amount of time. In the following example, the background color transition starts .2 seconds after the pointer moves over the link.

.smooth {  …  transition-property: background-color;  transition-duration: 0.3s;  transition-timing-function: ease-in-out;  transition-delay: 0.2s;}

The Shorthand transition Property

Thankfully, the authors of the CSS3 spec had the good sense to give us the shorthand transition property to combine all of these properties into one declaration. You’ve seen this sort of thing with the shorthand border property. Here is the syntax:

transition: property duration timing-function delay;

The values for each of the transition-* properties are listed out, separated by character spaces. The order isn’t important as long as the duration (which is required) appears before delay (which is optional). If you provide only one time value, it will be assumed to be the duration.

Using the blue-to-red link example, we could combine the four transition properties we’ve applied so far into this one line:

.smooth {  …  transition: background-color 0.3s ease-in-out 0.2s;}

Definitely an improvement.

Applying Multiple Transitions

So far, we’ve changed only one property at a time, but it is possible to transition several properties at once. Let’s go back to the “awesomesauce” link example. This time, in addition to changing from blue to red, I’d like the letter-spacing to increase a bit. I also want the text color to change to black, but more slowly than the other animations. Figure 18-4 attempts to show these transitions on this static page.

Figure 18-4. The color, background-color, and letter-spacing change at different paces.

One way to do this is to list all of the values for each property separated by commas, as shown in this example:

.smooth {  …  transition-property: background-color, color, letter-spacing;  transition-duration: 0.3s, 2s, 0.3s;  transition-timing-function: ease-out, ease-in, ease-out;
 }.smooth:hover, .smooth:focus {  background-color: red;  letter-spacing: 3px;  color: black;}

The values are matched up according to their positions in the list. For example, the transition on the color property (second in the list) has a duration of 2s and uses the ease-in timing function. If one list has fewer values than the others, the browser repeats the values in the list, starting over at the beginning. In the previous example, if I had omitted the third value (.3s) for transition-duration, the browser would loop back to the beginning of the list and use the first value (.3s) for letter-spacing. In this case, the effect would be the same.

You can line up values for the shorthand transition property as well. The same set of styles we just saw could also be written as follows:

.smooth {  …  transition: background-color 0.3s ease-out,             color 2s ease-in,             letter-spacing 0.3s ease-out;
}

A Transition for All Occasions

But what if you just want to add a little bit of smoothness to all your state changes, regardless of which property might change? For cases when you want the same duration, timing function, and delay to apply to all transitions that might occur on an element, use the all value for transition-property. In the following example, I’ve specified that any property that might change for the .smooth element should last .2 seconds and animate via the ease-in-out function.

.smooth {
  transition: all 0.2s ease-in-out; 	
}

For user interface changes, a short, subtle transition is often all you need for all your transitions, so the all value will come in handy. Well, that wraps up our lesson on CSS3 Transitions. Now you give it a try in Exercise 18-1.

Exercise 18-1. Trying out transitions

In this exercise, we’re going to create the rollover and active states for a menu link (Figure 18-5) with animated transitions. I’ve put together a starter document (exercise_18-1.html) for you in the materials folder for this chapter at . Be sure you are using an up-to-date desktop browser to view your work (see Note).

Note

If you’re using a touch device for this exercise, you’ll miss out on this effect because there is no hover state on touch screens. You may see the hover state with a single tap. Transitions triggered by a click/tap or when the page loads will work on all devices, but they are not covered here.

Figure 18-5. In this exercise, we’ll create transitions between these link states.

First, take a look at the styles that are already applied. The list has been converted to a horizontal menu with Flexbox. The a element has been set to display as a block element, underlines are turned off, dimensions and padding are applied, and the color, background color, and border are established. I used the box-shadow property to make it look as though the links are floating off the page.

  1. Now we’ll define the styles for the hover and focus states. When the user puts the pointer over or tabs to the link, make the background color change to green (#c6de89) and the border color change to a darker shade of green (#a3c058).
    a:hover, a:focus {  background-color: #c6de89;  border-color: #a3c058;} 
  2. While the user clicks the link (:active), make it move down by 3 pixels as though it is being pressed. Do this by setting the a element’s position to relative and its top position to 0px , and then change the value of the top property for the active state. This moves the link 3 pixels away from the top edge (in other words, down).

    NOTE: Setting the top to 0px in the initial state is for working around a bug that arises when transitioning the top, bottom, left, and right properties.

    a {  …  position: relative;  top: 0px;}a:active {  top: 3px;}
  3. Logically, if the button were pressed down, there would be less room for the shadow, so we’ll reduce the box-shadow distance as well.
    a:active   top: 3px;  box-shadow: 0 1px 2px rgba(0,0,0,.5);}
  4. Save the file and give it a try in the browser. The links should turn green and move down when you click or tap them. I’d say it’s pretty good just like that. Now we can enhance the experience by adding some smooth transitions.
  5. Make the background and border color transition ease in over 0.2 seconds, and see how that changes the experience of using the menu. I’m using the shorthand transition property to keep the code simple. I’m also using the default ease timing function at first so we can omit that value.

    I’m not using any vendor prefixes here because modern browsers don’t need them. If you wanted to support mobile browsers released in 2013 and earlier, you could include the -webkit- prefixed version as well, but since this isn’t production code, we’re fine without it.

    a {  transition: background-color 0.2s,             border-color 0.2s;} 
  6. Save your document, open it in the browser, and try moving your mouse over the links. Do you agree it feels nicer? Now I’d like you to try some other duration values. See if you can still see the difference with a 0.1s duration. Now try a full second (1s). I think you’ll find that 1 second is surprisingly slow. Try setting it to several seconds and trying out various timing-function values (just add them after the duration times). Can you tell the difference? Do you have a preference? When you are done experimenting, set the duration back to 0.2 seconds.
  7. Now let’s see what happens when we add a transition to the downward motion of the link when it is clicked or tapped. Transition both the top and box-shadow properties because they should move in tandem. Let’s start with a 0.2s duration like the others.
    a {  transition:    background-color 0.2s,    border-color 0.2s,    top 0.2s,    box-shadow 0.2s;}

    Save the file, open it in the browser, and try clicking the links. That transition really changes the experience of using the menu, doesn’t it? The buttons feel more difficult to “press.” Try increasing the duration. Do they feel even more difficult? I find it interesting to see the effect that timing has on the experience of a user interface. It is important to get it right and not make things feel sluggish. I’d say that a very short transition such as 0.1 second—or even no transition at all—would keep these buttons feeling snappy.

  8. If you thought increasing the duration made the menu uncomfortable to use, try adding a short 0.5-second delay to the top and box-shadow properties.
    a {  transition:    background-color 0.2s,    border-color 0.2s,    top 0.1s 0.5s,    box-shadow 0.1s 0.5s;}

    I think you’ll find that little bit of extra time makes the whole thing feel broken. Timing is everything!

CSS Transforms

transform

Values: rotate() | rotateX() | rotateY() | rotateZ() | rotate3d() | translate() | translateX() | translateY() | scale() | scaleX() | scaleY() | skew() | skewX() | skewY() | none

Default: none

Applies to: transformable elements (see sidebar)

Inherits: no

The CSS3 Transforms Module () gives authors a way to rotate, relocate, resize, and skew HTML elements in both two- and three-dimensional space. It is worth noting up front that transforms change how an element displays, but it is not motion- or time-based. However, you can animate from one transform state to another using transitions or keyframe animations, so they are useful to learn about in the context of animation.

This chapter focuses on the more straightforward two-dimensional transforms because they have more practical uses. Transforms are supported on virtually all current browser versions without vendor prefixes (see the sidebar “CSS Transforms Support” for exceptions).

You can apply a transform to the normal state of an element, and it appears in its transformed state when the page loads. Just be sure that the page is still usable on browsers that don’t support transforms. It is common to introduce a transform only when users interact with the element via :hover or a JavaScript event. Either way, transforms are a good candidate for progressive enhancement—if an IE8 user sees an element straight instead of at a jaunty angle, it’s probably no biggie.

Figure 18-6 shows a representation of four two-dimensional transform functions: rotate(), translate(), scale(), and skew() (see Note). The dashed outline shows the element’s original position.

Figure 18-6. Four types of transforms: rotate(), translate(), scale(), and skew().
Note

There are actually five 2-D transform functions in the CSS spec. The fifth, matrix(), allows you to craft your own combined transformation using six values and some badass trigonometry. There are tools that can take a number of transforms and combine them into a matrix function, but the result isn’t very user-friendly. Fascinating in theory, but more than I want to take on personally.

When an element transforms, its element box keeps its original position and influences the layout around it, in the same way that space is left behind by a relatively positioned element. It is as though the transformation magically picks up the pixels of the rendered element, messes around with them, and lays them back down on top of the page. So, if you move an element with transform, you’re moving only a picture of it. That picture has no effect on the surrounding layout. Let’s go through the transform functions one by one, starting with rotate().

Transforming the Angle (rotate)

If you’d like an element to appear on a bit of an angle, use the rotate() transform function. The value of the rotate() function is an angle specified in positive (clockwise) or negative (counterclockwise) degrees. The image in Figure 18-7 has been rotated –10 degrees (350 degrees) with the following style rule. The tinted image shows the element’s original position for reference.

img {  width: 400px;  height: 300px;  transform: rotate(-10deg);}

Notice that the image rotates around its center point, which is the default point around which all transformations happen. But you can change that easily with the transform-origin property.

Figure 18-7. Rotating an img element by using transform: rotate().

transform-origin

Values: percentage | length | left | center | right | top | bottom

Default: 50% 50%

Applies to: transformable elements

Inherits: no

The value for transform-origin is either two keywords, length measurements, or percentage values. The first value is the horizontal offset, and the second is the vertical offset. If only one value is provided, it will be used for both. The syntax is the same as you learned for background-position back in Chapter 13, Colors and Backgrounds. If we wanted to rotate our image around a point at the center of its top edge, we could write it in any of the following ways:

transform-origin: center top; transform-origin: 50%, 0%; transform-origin: 200px, 0;

The images in Figure 18-8 have all been rotated 25 degrees, but from different origin points. It is easy to demonstrate the origin point with the rotate() function, but keep in mind that you can set an origin point for any of the transform functions.

Figure 18-8. Changing the point around which the image rotates by using transform-origin.

Transforming the Position (translate)

Another thing you can do with the transform property is give the element’s rendering a new location on the page by using one of three translate() functions, as shown in the examples in Figure 18-9. The translateX() function allows you to move an element on a horizontal axis; translateY() is for moving along the vertical axis; and translate() combines both x and y values.

transform: translateX(50px); transform: translateY(25px); transform: translate(50px, 25px); /* (translateX, translateY) */

Figure 18-9. Moving an element around with the translate() function.

Provide length values in any of the CSS units or as a percentage value. Percentages are calculated on the width of the bounding box—that is, from border edge to border edge (which, incidentally, is how percentages are calculated in SVG, from which transforms were adapted). You can provide positive or negative values, as shown in Figure 18-9.

If you provide only one value for the shorthand translate() function, it will be presumed to be the translateX value, and translateY will be set to zero. So translate(20px) would be equivalent to applying both translateX(20px) and translateY(0).

How do you like the transform property so far? We have two more functions to go.

Transforming the Size (scale)

Make an element appear larger or smaller by using one of three scale functions: scaleX() (horizontal), scaleY() (vertical), and the shorthand scale(). The value is a unitless number that specifies a size ratio. This example makes an image 150% its original width:

a img {  transform: scaleX(1.5);}

The scale() shorthand lists a value for scaleX and a value for scaleY. This example makes an element twice as wide but half as tall as the original:

a img {  transform: scale(2, .5);}

Unlike translate(), however, if you provide only one value for scale(), it will be used as the scaling factor in both directions. So specifying scale(2) is the same as applying scaleX(2) and scaleY(2), which is intuitively the way you’d want it to be.

Figure 18-10 shows the results of all our scaling endeavors.

Figure 18-10. Changing the size of an element with the scale() function.

Making It Slanty (skew)

The quirky collection of skew properties—skewX(), skewY(), and the shorthand skew()—changes the angle of either the horizontal or vertical axis (or both axes) by a specified number of degrees. As for translate(), if you provide only one value, it is used for skewX(), and skewY() will be set to zero.

The best way to get an idea of how skewing works is to take a look at some examples (Figure 18-11):

a img {  transform: skewX(15deg);} a img {  transform: skewY(30deg);} a img {  transform: skew(15deg, 30deg);}

Figure 18-11. Slanting an element by using the skew() function.

Applying Multiple Transforms

It is possible to apply more than one transform to a single element by listing out the functions and their values, separated by spaces, like this:

transform: function(value) function(value);

In the example in Figure 18-12, I’ve made the forest image get larger, tilt a little, and move down and to the right when the mouse is over it or when it is in focus:

img:hover, img:focus {  transform: scale(1.5) rotate(-5deg) translate(50px,30px);}

Figure 18-12. Applying scale(), rotate(), and translate() to a single element.

It is important to note that transforms are applied in the order in which they are listed. For example, if you apply a translate() and then rotate(), you get a different result than with a rotate() and then a translate(). Order matters.

Another thing to watch out for is that if you want to apply an additional transform on a different state (such as :hover, :focus, or :active), you need to repeat all of the transforms already applied to the element. For example, this a element is rotated 45 degrees in its normal state. If I apply a scale() transform on the hover state, I would lose the rotation unless I explicitly declare it again:

a {  transform: rotate(45deg);}a:hover {  transform: scale(1.25);  /* rotate on a element would be lost */
}

To achieve both the rotation and the scale, provide both transform values:

a:hover {  transform: rotate(45deg) scale(1.25);  /* rotates and scales */
}

Smooooooth Transforms

The multiple transforms applied to the redwood forest image look interesting, but it might feel better if we got there with a smooth animation instead of just BAM! Now that you know about transitions and transforms, let’s put them together and make some magic happen. And by “magic,” of course I mean some basic animation effects between two states. We’ll do that together, step-by-step, in Exercise 18-2.

Exercise 18-2. Transitioning transforms

In this exercise, we’ll make the travel photos in the gallery shown in Figure 18-13 grow and spin out to an angle when the user mouses over them—and we’ll make it smoooooth with a transition. A starter document (exercise_18-2.html) and all of the images are available in the materials folder for this chapter.

Figure 18-13. Photos get larger and tilt on :hover and :focus . A transition is used to help smooth out the change between states. You can see how it works when you are finished with this exercise (or check it out in the ch18_figures.html page).
  1. Open exercise_18-2.html in a text editor, and you will see that there are already styles that arrange the list items horizontally and apply a slight drop shadow. The first thing we’ll do is add the transform property for each image.
  2. We want the transforms to take effect only when the mouse is over the image or when the image has focus, so the transform property should be applied to the :hover and :focus states. Because I want each image to tilt a little differently, we’ll need to write a rule for each one, using its unique ID as the selector. You can save and check your work when you’re done.
    a:hover #img1, a:focus #img1 {  transform: rotate(-3deg);} a:hover #img2, a:focus #img2 {  transform: rotate(5deg); } a:hover #img3, a:focus #img3 {  transform: rotate(-7deg);}a:hover #img4, a:focus #img4 {  transform: rotate(2deg);}  
    NOTE

    As of this writing, prefixes are still recommended for the transform property, so for production-quality code, the complete rule would look like this:

    a:hover #img1, a:focus #img1 {  -webkit-transform: rotate(-3deg);  -ms-transform: rotate(-3deg); /* for IE9 */
  transform: rotate(-3deg);} 

    Because we are checking our work on a modern browser, we can omit the prefixes for this exercise.

  3. Now let’s make the images a little larger as well, to give visitors a better view. Add scale(1.5) to each of the transform values. Here is the first one; you do the rest:
    a:hover #img1 {  transform: rotate(-3deg) scale(1.5);} 

    Note that my image files are created at the larger size and then scaled down for the thumbnail view. If we started with small images and scaled them larger, they would look crummy.

  4. As long as we are giving the appearance of lifting the photos off the screen, let’s make the drop shadow appear to be a little farther away by increasing the offset and blur, and lightening the shade of gray. All images should have the same effect, so add one rule using a:hover img as the selector.
    a:hover img {  box-shadow: 6px 6px 6px rgba(0,0,0,.3);}

    Save your file and check it out in a browser. The images should tilt and look larger when you mouse over them. But the action is kind of jarring. Let’s fix that with a transition.

  5. Add the transition shorthand property to the normal img state (i.e., not on :hover or :focus). The property we want to transition in this case is transform. Set the duration to 0.3 seconds and use the linear timing function.
    img {  …  transition: transform 0.3s linear;}
Note

The prefixed transform property should be included in the context of a transition as well, as shown in this fully prefixed declaration:

-webkit-transition: -webkit-transform .3s linear;

The -ms- prefix is not needed because transitions are not supported by IE9. Those users will see an immediate change to the transformed image without the smooth transition, which is fine.

And that’s all there is to it! You can try playing around with different durations and timing functions, or try altering the transforms or their origin points to see what other effects you can come up with.

3-D Transforms

In addition to the two-dimensional transform functions we’ve just seen, the CSS Transforms spec also describes a system for creating a sense of three-dimensional space and perspective. Combined with transitions, you can use 3-D transforms to create rich interactive interfaces, such as image carousels, flippable cards, or spinning cubes! Figure 18-14 shows a few examples of interfaces created with 3-D transforms.

It’s worth noting that this method does not create 3-D objects with a sense of volume; it merely tilts the otherwise flat element box around on three axes (animation expert Val Head calls them “postcards in space”). The rotating cube example in the figure merely stitches together six element boxes at different angles. That said, 3-D transforms still add some interesting depth to an otherwise flat web page.

Figure 18-14. Some examples of 3-D transforms. The book covers, movie posters, and 3-D cube also have cool animation effects, so it’s worth going to the links and checking them out. Webflow is a visual web design tool that includes the ability to create 3-D transformed elements.

3-D transforms are not a need-to-know skill for folks just starting out in web design, so I’m not going to go into full detail here, but I will give you a taste of what it takes to add a third dimension to a design. If you’d like to learn more, the following tutorials are good places to start (although the browser support information they contain may be out-of-date):

To give you a very basic example, I’m going to use the images from Exercise 18-2 and arrange them as though they are in a 3-D carousel-style gallery (Figure 18-15).

Figure 18-15. Our aquarium images arranged in space…space…space…

The markup is the same unordered list used in the previous exercise:

<ul>  <li><a href=""><img src="anchovies.jpg" id="img1" alt=""></a></li>  <li><a href=""><img src="jellyfish1.jpg" id="img2" alt=""></a></li>  <li><a href=""><img src="bluejellyfish.jpg" id="img3" alt=""></a>  </li>  <li><a href=""><img src="seadragon.jpg" id="img4" alt=""></a></li></ul>   

The first step is to add some amount of “perspective” to the containing element by using the perspective property. This tells the browser that the child elements should behave as though they are in 3-D space. The value of the perspective property is some integer larger than zero that specifies a distance from the element’s origin on the z-axis. The lower the value, the more extreme the perspective. I have found that values between 300 and 1,500 are reasonable, but this is something you need to fuss around with until you get the desired effect.

ul {  width: 1000px;  height: 100px;  list-style-type: none;  padding: 0;  margin: 0;  perspective: 600;}  
Note

When using the -webkit- prefix for transform, include the prefixed version of perspective as well (-webkit-perspective).

The perspective-origin property (not shown) describes the position of your eyes relative to the transformed items. The values are a horizontal position (left, center, right, or a length or percentage) and a vertical position (top, bottom, center, or a length or percentage value). The default (Figure 18-15) is centered vertically and horizontally (perspective-origin: 50% 50%). The final transform-related property is backface-visibility, which controls whether the reverse side of the element is visible when it spins around.

With the 3-D space established, apply one of the 3-D transform functions to each child element—in this case, the li within the ul. The 3-D functions include translate3d, translateZ, scale3d, scaleZ, rotate3d, rotateX, rotateY, rotateZ, and matrix3d. You should recognize some terms in there. The *Z functions define the object’s orientation relative to the z-axis (picture it running from your nose to this page, whereas the x- and y-axes lie flat on the page).

In our example in Figure 18-15, each li is rotated 45 degrees around its y-axis (vertical axis) by using the rotateY function, which works as though the element boxes are rotating around a pole.

Compare the result to Figure 18-16, in which each li is rotated on its x-axis (horizontal axis) by using rotateX. It’s as though the element boxes are rotating around a horizontal bar.

li {  float: left;   margin-right: 10px;  transform: rotateX(45deg);} 

Figure 18-16. The same list of images rotated on their horizontal axes with rotateX().

Obviously, I’m barely scratching the surface of what can be done with 3-D transforms, but this should give you a mental model for how it works. Next up, I’ll introduce you to a more sophisticated way to set your web pages in motion.

Keyframe Animation

The CSS Animations Module allows authors to create real, honest-to-goodness keyframe animation. Figure 18-17 shows just a few examples that you can see in action online. Unlike transitions that go from a beginning state to an end state, keyframe animation allows you to explicitly specify other states at points along the way, allowing for more granular control of the action. Those “points along the way” are established by keyframes that define the beginning or end of a segment of animation.

Note

Keyframe animation is known as explicit animation because you program its behavior. By contrast, transitions are an example of implicit animation because they are triggered only when a property changes.

Figure 18-17. Examples of animations using only CSS.

Creating keyframe animations is complex, and more than I can cover here. But I would like for you to have some idea of how it works, so I’ll sketch out the minimal details. The following resources are good starting points for learning more:

  • CSS Animations Level 1 (a Working Draft at the time of this writing) at
  • Transitions and Animations in CSS by Estelle Weyl (O’Reilly).
  • “Animation & UX Resources” by Val Head (). Val has compiled a mega-list of resources regarding web animation, including links to tutorials, articles, tools, galleries, and more. It is not limited to CSS keyframe animation, but as long as you’re delving into animation, you can trust Val to point you to good stuff.
  • “CSS: Animation” course by Val Head on Lynda.com (). You’ll need a subscription to Lynda.com, but if you are in web-design-learning mode, it may be a good investment.
  • “CSS Animation for Beginners” by Rachel Cope (). This is a clearly written tutorial with lots of examples.
  • “The Guide to CSS Animation: Principles and Examples” by Tom Waterhouse (). This tutorial goes beyond CSS code to include tips for creating natural animation effects.

Establishing the Keyframes

The animation process has two parts:

  1. Establish the keyframes with a @keyframes rule.
  2. Add the animation properties to the elements that will be animated.

Here is a very simple set of keyframes that changes the background color of an element over time. It’s not a very action-packed animation, but it should give you a basic understanding of what a @keyframes rule does.

@keyframes colors {  0% { background-color: red; }  20% { background-color: orange; }  40% { background-color: yellow; }  60% { background-color: green; }  80% { background-color: blue; }  100% { background-color: purple; }}

The keyframes at-rule identifies the name of the animation, the stages of the animation represented by percentage (%) values, and the CSS properties that are affected for each stage. Here’s what a @keyframes rule looks like abstracted down to its syntax:

@keyframes animation-name {   keyframe { property: value; }   /* additional keyframes */}

The sample @keyframes rule says: create an animation sequence called “colors.” At the beginning of the animation, the background-color of the element should be red; at 20% through the animation runtime, the background color should be orange; and so on, until it reaches the end of the animation. The browser fills in all the shades of color in between each keyframe (or tweens it, to use the lingo). This is represented the best I could in Figure 18-18.

Figure 18-18. Animating through the colors of the rainbow by using keyframes.

Each percentage value and the property/value declaration defines a keyframe in the animation sequence.

As an alternative to percentages, you can use the keyword from for the start of an animation sequence (equivalent to 0%) and the keyword to for denoting the end (100%). The following example makes an element slide in from right to left as the left margin reduces to 0:

@keyframe slide {  from { margin-left: 100% }  to { margin-left: 0%; }}

Adding Animation Properties

Now we can apply this animation sequence to an element or multiple elements in the document by using a collection of animation properties that are very similar to the set of transition properties that you already know.

I am going to apply the rainbow animation to the #magic div in my document:

<div id="magic">Magic!</div>

In the CSS rule for #magic, I make decisions about the animation I want to apply:

  • Which animation to use (animation-name) (Required).
  • How long it should take (animation-duration) (Required).
  • The manner in which it should accelerate (animation-timing-function). This property uses the same timing function keywords that we covered for CSS Transitions.
  • Whether to pause before it starts (animation-delay).

Looks familiar, right? There are a few other animation-specific properties to know about as well:

animation-iteration-count

How many times the animation should repeat. This can be set to a whole number or infinite.

animation-direction

Whether the animation plays forward (normal), in reverse (reverse), or alternates back and forth starting at the beginning (alternate), or alternates starting from the end (alternate-reverse).

animation-fill-mode

The animation fill mode determines what happens with the animation before it begins and after it ends. By default (none), the animation shows whatever property values were not specified via @keyframes. If you want the last keyframe to stay visible after the animation plays, use the forwards keyword. If there is a delay set on the animation and you want the first keyframe to show during that delay, use backwards. To retain the beginning and end states, use both.

animation-play-state

Whether the animation should be running or paused when it loads. The play-state can be toggled on and off based on user input with JavaScript or on hover.

The animation-name property tells the browser which keyframe sequence to apply to the #magic div. I’ve also set the duration and timing function, and used animation-iteration-count to make it repeat infinitely. I could have provided a specific number value, like 2 to play it twice, but how fun are only two rainbows? And for fun, I’ve set the animation-direction to alternate, which makes the animation play in reverse after it has played forward. Here is the resulting rule for the animated div:

#magic {  …  animation-name: colors;  animation-duration: 5s;  animation-timing-function: linear;  animation-iteration-count: infinite;  animation-direction: alternate;}

That gets a bit verbose, especially when you consider that each property may also follow a prefixed version. You can also use the animation shorthand property to combine the values, just as we did for transition:

#magic {  animation: colors 5s linear infinite alternate;} 

Those are the bare bones of creating keyframes and applying animations to an element on the page. To make elements move around (what we typically think of as “animation”), use keyframes to change the position of an element on the screen with translate (the best option for performance) or with the top, right, bottom, and left properties. When the keyframes are tweened, the object will move smoothly from position to position. You can also animate the other transform functions such as scale and skew.

When to Use Keyframe Animation

To keep my example simple, I chose to change only the background color of a button element, but of course, keyframe animations can be used to create real animations, especially when combined with the CSS transform functions for spinning and moving elements around on the page. If you only need to change an element from one state to another, a transition is the way to go. But if you have a linear animation such as moving a character, an object, or its parts around, keyframe animation is the most appropriate choice.

For more complex keyframe animations, particularly those that change with user interaction or require complex physics, using JavaScript for animation may be a better choice than CSS animation. JavaScript animation also has better support in older browsers, making it preferable if animation is critical to the mission of the page. CSS keyframe animation is a good solution for simple animations used as an enhancement to a baseline experience.

I should note that as I write this, there is a lot of excitement in the web community for animating SVG graphics. When you place the source code for an SVG directly in the HTML document, the elements in it are available to be animated. As of this writing, there are still limitations and browser support issues around using CSS to animate SVGs, but as browser support grows, this approach looks very promising. In the meantime, JavaScript has better access to SVG properties, has better browser support, and is the more common solution for SVG animation.

Wrapping Up

I hope I’ve helped you to wrap your head around how CSS can be used to add a little motion and smoothness to your pages. For adding motion to a web page, we have CSS Transitions to smooth out changes from one state to another and CSS Keyframe Animation for animating a series of states. We also looked at CSS Transforms for repositioning, spinning, resizing, or skewing an element when it is rendered on the screen.

Used thoughtfully, animation can make your interfaces more intuitive and enhance your brand personality. It’s powerful stuff, but with great power comes great responsibility. To learn how to use web animation to enhance the user experience in a meaningful way, I recommend the book Designing Interface Animation: Meaningful Motion for User Experience by Val Head (Rosenfeld Media).

Now let’s see if you were paying attention with a 12-question quiz!

Test Yourself

Think you know your way around transitions, transforms, and keyframe animations? Here are a few questions to find out (answers in Appendix A):

  1. What is tweening?
  2. If a transition had keyframes, how many would it have?
  3. Write out the transition declaration (property and value) you would use to accomplish the following:
    1. Wait .5 seconds before the transition starts.
    2. Make the transition happen at a constant speed.
    3. Make the transition last .5 seconds.
    4. Make the lines of text slowly grow farther apart.
  4. Which of the following can you not animate?
    1. width
    2. padding
    3. text-transform
    4. word-spacing
  1. Which timing function will be used if you omit the transition-timing-function property? Describe its action.
  2. In the following transition, what does .2s describe?
    transition: color .2s linear;
  3. Which transition will finish first?
    1. transition: width 300ms ease-in;
    2. transition: width 300ms ease-out;
  4. Write the transform declaration to accomplish the following:
    1. Tilt the element 7 degrees clockwise.
    2. Reposition the element 25 pixels up and 50 pixels to the left.
    3. Rotate the element from its bottom-right corner.
    4. Make a 400-pixel-wide image display at 500 pixels wide.
  5. In the following transform declaration, what does the 3 value describe?
    transform: scale(2, 3)
  6. Which 3-D transform would look more angled and dramatic?
    1. perspective: 250;
    2. perspective: 1250;
  7. What happens halfway through this animation?
    @keyframes border-bulge {  from { border-width: 1px; }  25% { border-width: 10px; }  50% { border-width: 3px; }  to { border-width: 5px; }}
  8. Write the animation declaration you would use to accomplish the following:
    1. Make the animation play in reverse.
    2. Make the entire animation last 5 seconds.
    3. Wait 2 seconds before running the animation.
    4. Repeat the animation three times and then stop.
    5. The end state of the animation stays visible after the animation is done playing.

CSS Review: Transitions, Transforms, and Animation

Here is a summary of the properties covered in this chapter.

Property

Description

animation

A shorthand property that combines animation properties

animation-name

Specifies the named animation sequence to apply

animation-duration

Specifies the amount of time the animation lasts

animation-timing-function

Describes the acceleration of the animation

animation-iteration-count

Indicates the number of times the animation repeats

animation-direction

Specifies whether the animation plays forward, in reverse, or alternates back and forth

animation-play-state

Specifies whether the animation is running or paused

animation-delay

Indicates the amount of time before the animation starts running

animation-fill-mode

Overrides limits to when animation properties can be applied

backface-visibility

Determines whether the reverse side of an element may be visible in 3-D transforms

perspective

Establishes an element as a 3-D space and specifies the perceived depth

perspective-origin

Specifies the position of your viewpoint in a 3-D space

transform

Specifies that the rendering of an element should be altered via one of the 2-D or 3-D transform functions

transform-origin

Denotes the point around which an element is transformed

transform-style

Preserves a 3-D context when transformed elements are nested

transition

A shorthand property that combines transition properties

transition-property

Defines which CSS property will be transitioned

transition-duration

Specifies the amount of time the transition animation lasts

transition-timing-function

Describes the manner in which the transition happens (changes in acceleration rates)

transition-delay

Specifies the amount of time before the transition starts