Understanding CSS3 Transitions

It was 1997 and I was sitting in a terribly run-down apartment in beautiful Allston, Massachusetts. A typical late night of viewing source and teaching myself HTML followed a day of packing CDs at a local record label for peanuts (hence the run-down apartment). I’m sure you can relate.

One triumphant night, I pumped my fist in sweet victory. I’d just successfully coded my first JavaScript image rollover. Remember those?

I still remember the amazement of seeing a crudely designed button graphic I’d cobbled together “swap” to a different one when hovered over by the mouse. I barely had a clue as to what I was doing at the time, but making something on the page successfully change, dynamically, was, well…magical.

We’ve come a long way over the past decade in regard to interaction and visual experience on the web. Historically, technologies like Flash and JavaScript have enabled animation, movement, and interaction effects. But recently, with browsers rolling out support for CSS transitions and transforms, some of that animation and experience enrichment can now be comfortably moved to our stylesheets.

My first JavaScript rollover back in 1997 took me several nights of head scratching, many lines of code that seemed alien to me at the time, and multiple images. CSS3 today enables far richer, more flexible interactions through simple lines of code that thankfully degrade gracefully in the browsers that don’t yet support it.

We can start to use CSS3 transitions right now as long as we carefully choose the situations in which to use them. They certainly won’t replace existing technologies like Flash, JavaScript, or SVG (especially without broader browser support)—but they can be used to push the experience layer a notch higher. And most importantly, they’re relatively easy to implement for the web designer already familiar with CSS. It only takes a few lines of code.

Tail wagging the dog

Initially developed solely by the WebKit team for Safari, CSS Transitions are now a Working Draft specification at the W3C. This is a nice example of browser innovation being folded back into a potential standard. I say potential since it’s merely a draft today. However, Opera has recently added CSS transitions support in version 10.5 and Firefox has pledged support for version 4.0. In other words, while it is a draft specification and evolving, it’s stable enough for Opera and Firefox to be taking it seriously and adding support for it. Most importantly, CSS transitions are no longer proprietary Safari-only experiments.

Let’s take a look at how transitions work, shall we?

What are CSS transitions?

I like to think of CSS transitions like butter, smoothing out value changes in your stylesheets when triggered by interactions like hovering, clicking, and focusing. Unlike real butter, transitions aren’t fattening—they’re just a few simple rules in your stylesheet to enrich certain events in your designs.

The W3C explains CSS transitions quite simply:

CSS Transitions allow property changes in CSS values to occur smoothly over a specified duration. This smoothing animates the changing of a CSS value when triggered by a mouse click, focus or active state, or any changes to the element (including even a change on the element’s class attribute).

A simple example

Here’s a simple example, where we’ll add a transition to the background color swap of a link. When hovered over, the link’s background color will change, and we’ll use a transition to smooth out that change—an effect previously only possible using Flash or JavaScript, but now possible with a few simple lines of CSS.

The markup is a simple hyperlink, like so:

 <a href="#">Transition me!</a> 

Next, we’ll add a declaration for the normal link state with a little padding and a light green background, followed by the background swap to a darker green on hover:

 a.foo { padding: 5px 10px; background: #9c3; } a.foo:hover { background: #690; } 
Normal and Hover Link ExampleFig. 1: The normal and :hover state of the link.

Now let’s add a transition to that background color change. This will smooth out and animate the difference over a specified period of time.

Fig. 2: Here we can see the smooth transition of light green to darker green background.

For the time being, we’ll use only the vendor-prefixed properties which currently work in WebKit-based browsers (Safari and Chrome) to keep things simple. Later, we’ll add vendor prefixes for Mozilla and Opera.

 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition-property: background; -webkit-transition-duration: 0.3s; -webkit-transition-timing-function: ease; } a.foo:hover { background: #690; } 

You’ll notice the three parts of a transition in the declaration:

  • transition-property: The property to be transitioned (in this case, the background property)
  • transition-duration: How long the transition should last (0.3 seconds)
  • transition-timing-function: How fast the transition happens over time (ease)

Timing functions (or, I really wish I’d paid attention in math class)

The timing function value allows the speed of the transition to change over time by defining one of six possibilities: easelinearease-inease-out,ease-in-out, and cubic-bezier (which allows you to define your own timing curve).

If you slept through geometry in high school like I did, don’t worry. I recommend simply plugging in each of these timing function values to see how they differ.

For our simple example, the duration of the transition is so quick (just a mere 0.3 seconds) that it’d be difficult to tell the difference between the six options. For longer animations, the timing function you choose becomes more of an important piece of the puzzle, as there’s time to notice the speed changes over the length of the animation.

When in doubt, ease (which is also the default value) or linear should work just fine for short transitions.

We could simplify the declaration significantly by using the transition shorthand property:

 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition: background 0.3s ease; } a.foo:hover { background: #690; } 

Now we have a much more compact rule that accomplishes the same result.

All of this wonderful transitioning works just fine in WebKit browsers, but what about the others?

Browser support

As I mentioned earlier, transitions were initially developed by WebKit, and have been in Safari and Chrome since version 3.2, but Opera supports them as well in version 10.5 and support has been promised in Firefox 4.0. Because of that present and near-future support, it’s important to add the appropriate vendor prefixes so that our transitions will work in more browsers as support is rolled out.

Building the full transition stack

Here’s a revised declaration, adding the –moz- and –o- prefixes as well as the actual CSS3 transition property. We’re putting the non-prefixed property last in the stack to ensure that the final implementation will trump the others as the property moves from draft to finished status.

 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition: background 0.3s ease; -moz-transition: background 0.3s ease; -o-transition: background 0.3s ease; transition: background 0.3s ease; } a.foo:hover { background: #690; } 

With that stack, we’ll be smoothing out that background color change in current versions of Safari, Chrome, and Opera, as well as future versions of any browser that chooses to support it.

Transitioning states

I remember being slightly confused when I first started playing around with CSS Transitions. Wouldn’t it make more sense if the transition properties were placed in the :hover declaration, since that’s the trigger for the transition? The answer is that there are other possible states of an element besides :hover, and you’ll likely want that transition to happen on each of those without duplication.

For instance, you may want the transition to also happen on the :focus or:active pseudo-classes of the link as well. Instead of having to add the transition property stack to each of those declarations, the transition instructions are attached to the normal state and therefore declared only once.

The following example adds the same background switch to the :focusstate. This enables triggering the transition from either hovering over or focusing the link (via the keyboard, for example).

 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition: background 0.3s ease; -moz-transition: background 0.3s ease; -o-transition: background 0.3s ease; transition: background 0.3s ease; } a.foo:hover, a.foo:focus { background: #690; } 

Transitioning multiple properties

Let’s say that along with the background color, we also want to change the link’s text color and transition that as well. We can do that by stringing multiple transitions together, separated by a comma. Each can have their varying duration and timing functions (fig. 3).

Fig. 3: The normal and :hover states of the link.
 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition: background .3s ease, color 0.2s linear; -moz-transition: background .3s ease, color 0.2s linear; -o-transition: background .3s ease, color 0.2s linear; transition: background .3s ease, color 0.2s linear; } a.foo:hover, a.foo:focus { color: #030; background: #690; } 

Transitioning all possible properties

An alternative to listing multiple properties is using the all value. This will transition all available properties.

Let’s drop all into our simple example instead of listing background and color separately. They’ll now share the same duration and timing function.

 a.foo { padding: 5px 10px; background: #9c3; -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; -o-transition: all 0.3s ease; transition: all 0.3s ease; } a.foo:hover, a.foo:focus { color: #030; background: #690; } 

This is a convenient way of catching all the changes that happen on :hover,:focus, or :active events without having to list each property you’d like to transition.

Which CSS properties can be transitioned?

Now that we’ve successfully transitioned the background and color of a hyperlink, there are many other CSS properties that can be transitioned, including widthopacity, position, and font-size. A chart of all the possible properties (and their types) that can be transitioned is available from the W3C.

Why not use JavaScript instead?

You might be wondering, with not all browsers supporting (or at least promising support for) CSS Transitions, why not use a JavaScript solution to handle the animation? Popular frameworks such as jQueryPrototype, and script.aculo.us have enabled animations via JavaScript that work cross-browser for some time now.

It all depends on how crucial the transitions are to the experience. I’m stressing here in this little book that you can embrace the simplicity and flexibility of CSS3 if you choose the appropriate parts of the user experience to apply it: enriching the interactions that happen on the page. Quite often, the animation of these interactions when handled by CSS Transitions aren’t integral to the brand, readability, or layout of the website. Therefore, a few simple lines of CSS to trigger a simple animation that’s native to the browsers that support it (rather than tapping into a JavaScript framework) seems like a smart choice. And one I’m glad we have at our disposal.

Be smart, be subtle

Like all shiny new tools, it’s important to use transitions appropriately. One can easily go overboard adding transitions to everything on the page, resulting in some sort of annoying, pulsating monster. It’s key to decide where transitions rightfully enrich the user experience and when they are just extraneous noise. Additionally, making sure the speed of the transition doesn’t slow down an otherwise snappy action from the user is crucial. Use with care and caution.