Using CSS3 Transitions, Transforms and Animation

Intro

First things first - these demos are showing of CSS transitions, transforms (2D and 3D) and animations. Currently (January 2012), 2D transforms are available in all current browsers including IE9, transitions are available in all browsers except Internet Explorer 9 and less, while 3D transforms are in Safari, Chrome, Firefox and IE10 and up. Animations are available in Safari, Chrome, Firefox 5+ and IE10. Most examples degrade nicely, so if you are using a legacy browser you can still use a site using these, you just won't get animation. 3D transforms generally don't degrade nicely, so be careful when using them.

For a quick table summarising this, check out the supported browsers page.

None of the actual animation uses javascript, though I am using jQuery to add and remove classes to add interactivity. You can use the :target pseudo selector to do this in some cases, but I'm keeping it simple and extensible here.

If you are using iOS, replace hover with touch and hold wherever appropriate. (Check the script in the head if you are wondering how to replicate that effect.)

For most transitions I've just used two images to keep the examples simple. Most should be easy to extend to different content (videos, text, etc) or more than two images, though you may need to add and remove appropriate classes with JS in order to get the desired effect.

Of particular note is that animations that don't require a repaint (transforms and opacity) are hardware accelerated on Webkit, so if you are building a mobile site you definitely should be using this rather than things like the jQuery animate method.

To see a demo showing exactly how much faster transitions are than the older methods of doing things, check the speed comparison.

To make it easier to view source and copy, I'm putting style and script tags in each section, just before the demo. This isn't recommended for production use, but in this instance it will hopefully help.

Any suggestions/improvements/etc, contact me via my gmail account (rich.bradshaw), or on Twitter (richbradshaw).

Keep reading to see the main explanations, or for some more experimental demos with less explanation, check out the demos page.

Will it work for me?

Green means yes, red means no.

CSS Transitions

CSS 2D Transforms

CSS 3D Transforms

CSS Animations

Contents

  1. How to use transitions
  2. How to use transforms
  3. How to use animations
  4. Cross fading images
    1. On hover
    2. On button press
    3. With timer
    4. More than just fades
    5. Animating the background-image property
  5. Sliding content
    1. Sliding by transitions
    2. Sliding by transitions + translations
  6. 3D Flipping content
  7. Animated Accordions
  8. Notes on browser support
  9. How will it look in legacy browsers?
  10. Further Reading

Comments/Questions?

Please add any questions/corrections/extra info below. Please be courteous to other users.

blog comments powered by Disqus

How to use transitions

If you haven't used transitions before, here's a brief introduction.

On the element you want to have animate, add the following CSS:

#id_of_element {
	-webkit-transition: all 1s ease-in-out;
	-moz-transition: all 1s ease-in-out;
	-o-transition: all 1s ease-in-out;
	-ms-transition: all 1s ease-in-out;
	transition: all 1s ease-in-out;
}

There is a lot of duplication due to vendor prefixes - until the specification if finalised, this will persist. If this bothers you, there are various tools such as CSS Scaffold, LESS, or my preference - SASS, that allow you to define mixins to avoid repetitive code.

Another approach is simply to write the CSS without the prefixes, then use Lea Verou's -prefix-free to add them in at runtime.

Something you definitely shouldn't do is to only include the webkit prefix. Tempting though it seems, particularly when developing for mobile devices, webkit isn't the only rendering engine!

The syntax is pretty straightforward, you specify the property you want to animate, all or border-radius or color or whatever, the time to run, then the transition timing function. The options for the timing function are shown below.

Whenever any property changes, then it will animate instead of changing directly. This can be due to a different set of properties set on a pseudo class such as hover, or a new class or properties set by javascript. The example below uses :hover to change the properties – no javascript is needed.

To see the difference in speed, have a look at the speed test.

Demo - Different timing functions

Ease

Ease In

Ease Out

Ease In Out

Linear

Custom

Hover on me

In addition to the built in timing functions, you can also specify your own. The excellent Ceaser CSS Easing Tool makes this very easy.

Full syntax

The syntax for a CSS3 transition is of the form:

transition:  [ <transition-property> ||
               <transition-duration> ||
               <transition-timing-function> ||
               <transition-delay> ]

You will notice the final parameter is a delay - this let's you trigger things after an event has occurred. Below is a small demo showing this functionality.

Demo - Transition delays

Hover on me

Demo - advanced delays

You can set the way different properties animate differently. In this example the normal (blue) circle has this CSS (with the appropriate vendor prefixes):

#dd_main2 {
  transition: all 1s ease-in-out;  
}  

The delays (green) circle has this CSS instead:

#dd_main2 {
  transition-property: top, left;
  transition-duration: 1s, 1s;
  transition-delay: 0s, 1s;
}

This allows us to animate top and left differently, meaning we can make it move in an L shape, rather than diagonally. This technique can be used to create very complex animations, if needed.

Normal

Delays

Hover on me

Animatable properties

Regarding the properties you can animate, the best way is to experiment. The W3C maintain a list of properties that can be animated on the CSS Transitions spec. These include everything from background-color and letter-spacing to text-shadow and min-height. Many of these properties are not supported by default by jQuery animation, making CSS transitions much more useful out of the box. In addition, many browsers hardware accelerate animations that don't require repaints, namely opacity, 3D transforms and filters. To see the methods that Webkit accelerates, take a look at the AnimationBase.cpp code from the Webkit source. At the time of writing there are three classes defined here: PropertyWrapperAcceleratedOpacity, PropertyWrapperAcceleratedTransform and PropertyWrapperAcceleratedFilter. These are the animations that Webkit accelerates. Other browsers do things differently, but as Webkit is popular on mobile where these things matter most, it's worth noting this special case.

In reality, browsers are allowing more properties than these to be animated - box-shadow springs to mind as an obvious example. The table below is taken from the link above, and is can be considered the minimum number of properties you would expect to be animatable.

Property Name Type
background-color color
background-image only gradients
background-position percentage, length
border-bottom-color color
border-bottom-width length
border-color color
border-left-color color
border-left-width length
border-right-color color
border-right-width length
border-spacing length
border-top-color color
border-top-width length
border-width length
bottom length, percentage
color color
crop rectangle
font-size length, percentage
font-weight number
grid-* various
height length, percentage
left length, percentage
letter-spacing length
line-height number, length, percentage
margin-bottom length
margin-left length
margin-right length
margin-top length
max-height length, percentage
max-width length, percentage
min-height length, percentage
min-width length, percentage
opacity number
outline-color color
outline-offset integer
outline-width length
padding-bottom length
padding-left length
padding-right length
padding-top length
right length, percentage
text-indent length, percentage
text-shadow shadow
top length, percentage
vertical-align keywords, length, percentage
visibility visibility
width length, percentage
word-spacing length, percentage
z-index integer
zoom number

In addition to this, all browsers with transitions support animating CSS transforms, which proves to be invaluable.

To find out more about CSS3 transitions, read through the W3C specification.

How to use transforms

There are two categories of transform - 2D transforms and 3D transforms. As of May 2010, 3D transforms only work in Safari (both desktop and mobile). 2D transforms are more widely supported.

2D examples

This div has been skewed - note that the text is still selectable.
This div has been scaled - again, the text is real text.
This div has been rotated - you get the idea about the text!
This div has been translated 10px down, and 20px across.
This div has all four types!

The code for these looks like this, but with the appropriate vendor prefixes added:

#skew {
	transform:skew(35deg);	
}
#scale {
	transform:scale(1,0.5);	
}
#rotate {
	transform:rotate(45deg);	
}
#translate {
	transform:translate(10px, 20px);
}
#rotate-skew-scale-translate {
	transform:skew(30deg) scale(1.1,1.1) rotate(40deg) translate(10px, 20px);
}

These can also be animated using transitions - try hovering on the div below.

Hover on me and I'll spin and scale!

3D Examples

3D transforms work in Safari, Chrome, Firefox 10+ and IE10

3D transforms are similar to 2D transforms. The basic properties are translate3d, scale3d, rotateX, rotateY and rotateZ. Translate3d and scale3d take three arguments for x,y and z, whereas the rotates just take an angle. Here are some examples:

rotateX
rotateY
rotateZ

Hover me

The simplified code for those looks like this:

#transDemo4 div {
	transition:all 2s ease-in-out;		
	perspective: 800;
	perspective-origin: 50% 100px;	
}
#transDemo4:hover #rotateX {
	transform:rotateX(180deg);
}
#transDemo4:hover #rotateY {
	transform:rotateY(180deg);
}
#transDemo4:hover #rotateZ {
	transform:rotateZ(180deg);
}

A cube made with 3d transforms

1

2

3

4

5

6

Have a play with the controls - there's no transition here, just the sliders to control it. Note that I'm only using javascript to update the css values - all the maths needed is done by the browser automatically.

3D transforms playground

Original

Transformed

For more information read both the Webkit blog entry from when 3D transforms were first implemented, David Desandro's awesome examples and Microsoft's 3D transforms test drive.

Advanced usage

Though it's rare that you'll need it, it's worth noting that the raw matrix implementations are exposed as well. As an example, the skew transform above has a skew of 35 degrees. To find the internal representation, you can use javascript to find the computed style. In this case, skew(35deg) is represented by matrix(1, 0, 0.7002075382097097, 1, 0, 0). The astute among you will note that this is a 2×3 matrix. To use them for normal arithmetic, add a third row of 0, 0, 1.

3D transforms are represented similarly, with a 4×4 matrix.

Understanding how to create these matrices is probably out of the scope of this tutorial, but an undergraduate understanding of matrix algebra should suffice. Read the Wikipedia article on transformation matrices for a quick primer.

For the exact methods, read the part of the spec about 2D matrix decomposition and 3D matrix interface.

How to use animations

As of September 2011, this works in all Webkit browsers, Firefox 5+ and IE10

CSS animations were introduced into Webkit in 2007, and added to Firefox by David Barron in 2011.

In 2009 a working draft was written and added to the w3c site.

To use CSS animation, you first specify some keyframes for the animation - basically what styles will the element have at certain times. The browser does the tweening for you.

Demo

Hover over me

Code

The interesting bit of this code is this bit of CSS (remember to add vendor prefixes):

@keyframes resize {
	0% {
		padding: 0;
	}
	50% {
		padding: 0 20px;
		background-color:rgba(255,0,0,0.2);		
	}
	100% {
		padding: 0 100px;
		background-color:rgba(255,0,0,0.9);
	}
}
	
#box {
	animation-name: resize;
	animation-duration: 1s;
	animation-iteration-count: 4;
	animation-direction: alternate;
	animation-timing-function: ease-in-out;
}	

Note that the 4 iterations makes the box pulse twice - the animation runs forwards then backwards, then forwards then backwards.

You can have as many keyframes as you like, at whatever intervals you like.

A useful setting is to set animation-iteration-count to infinite, making the animation continue for ever.

Demo

The key to using these animations is subtlety - nice delicate animations, rather than extreme over the top ones! It's also worth noting that the WCAG (Web Content Accessibility Guidelines) 2.0 specifies that a website shouldn't contain things that flash more than 3 times a second to avoid causing seizures in people susceptible to them.

Cross fading images

Commonly used as part of image galleries, or to show detail on products. This has traditionally been done in javascript by iterating over the opacity - using CSS transitions makes this very easy to add to your site.

Demo 1 - One image to another, on hover (transitions)

Plan

  1. Put one image on top of the other
  2. Change the opacity of the top image on hover

Demo

Code

First up, the HTML markup. Without CSS enabled, you just get two images. Remember to add alt text for production use.

<div id="cf">
	<img class="bottom" src="/tests/images/Stones.jpg" />
	<img class="top" src="/tests/images/Summit.jpg" />
</div>

Then the CSS:

#cf {
	position:relative;
	height:281px;
	width:450px;
	margin:0 auto;	
}
#cf img {
	position:absolute;
	left:0;
	-webkit-transition: opacity 1s ease-in-out;
	-moz-transition: opacity 1s ease-in-out;
	-o-transition: opacity 1s ease-in-out;
	-ms-transition: opacity 1s ease-in-out;	
	transition: opacity 1s ease-in-out;
}

#cf img.top:hover {
	opacity:0;
}			

Demo 2 - One image to another, when a button is pressed (transitions)

Plan

Same as before, but instead of using the :hover pseudo class, we are going to use javascript to add a toggle a class. I'm using jQuery here because it's easy to understand, though you could just use plain old JS.

Demo

Click me to toggle

Code

First up, the HTML markup. Again, with no CSS enabled, you just get two images.

<div id="cf2" class="shadow">
	<img class="bottom" src="/tests/images/Stones.jpg" />
	<img class="top" src="/tests/images/Summit.jpg" />
</div>
<p id="cf_onclick">Click me to toggle</p>

Then the CSS. I've added a class with the opacity value.

#cf2 {
	position:relative;
	height:281px;
	width:450px;
	margin:0 auto;
}
#cf2 img {
	position:absolute;
	left:0;
	-webkit-transition: opacity 1s ease-in-out;
	-moz-transition: opacity 1s ease-in-out;
	-o-transition: opacity 1s ease-in-out;
	-ms-transition: opacity 1s ease-in-out;	
	transition: opacity 1s ease-in-out;
}

#cf2 img.transparent {
	opacity:0;
}
#cf_onclick {
	cursor:pointer;
}			

Then the extremely short JS. Note that the browser is smart enough to realise that it can animate to the new properties, I didn't have to set them in javascript (thought that works too).

$(document).ready(function() {
	$("#cf_onclick").click(function() {
		$("#cf2 img.top").toggleClass("transparent");
	});
});			

Have a look at the multiple image demo to see how to extend this idea to more than two images.

Demo 3 - One image to another with a timer (Webkit/Firefox 5/IE10 only, transitions and animations)

Plan

You could implement this by using Javascript to toggle classes with a delay - that would allow older browsers to still have the images change. As we are looking forward though, we'll use CSS keyframes.

  1. Start with demo 1
  2. Use CSS keyframes to define two states, one with top image transparent, one with it opaque.
  3. Set the animations number of iterations to infinite.

Demo

Each image is visible for 9 seconds before fading to the other one.

Code

Everything's the same as Demo 1, but I've added this to the CSS and removed the hover selector

@keyframes cf3FadeInOut {
	0% {
		opacity:1;
	}
	45% {
		opacity:1;
	}
	55% {
		opacity:0;
	}
	100% {
		opacity:0;
	}
}

#cf3 img.top {
	animation-name: cf3FadeInOut;
	animation-timing-function: ease-in-out;
	animation-iteration-count: infinite;
	animation-duration: 10s;
	animation-direction: alternate;
}			

To make sense of that, I've defined 4 keyframes, specified that whatever has this animation attached will be opaque for the first 45%, then transparent for the last 45%. The animation will repeat forever, will last 10 seconds, and will run forward then backwards. In other words, image 1 will be visible for 4.5 seconds, followed by a 1 second fade, followed by 4.5 seconds of image 2 being visible. Then it will reverse, meaning that image 1 and 2 will both be visible for 9 (4.5 x 2) seconds each time.

Demo with multiple images

Staggering the animations can result in a multiple image fader.

This time I've created an animation that goes from 0 to 1 opacity, then staggered the animations so only one is visible at once. It's not great, but it is maybe a start. Any suggestions on how to make this better would be gladly received!

	#cf4a img:nth-of-type(1) {
 		animation-delay: 0;		
	}
	#cf4a img:nth-of-type(2) {
 		animation-delay: 2s;		
	}
	#cf4a img:nth-of-type(3) {
 		animation-delay: 4s;		
	}
	#cf4a img:nth-of-type(4) {
 		animation-delay: 6s;		
	}

Demo 4 - More than just fades

This technique isn't limited to just fades, you can animate almost every property. Here are a couple of examples.

Zooming in and out

Hover on the image

Rotation

Hover on the image

Demo 5 - Animating the background-image property

Right now this only works on webkits built from 2012 onwards. It's not part of the spec (yet?).

Plan

  1. Make a div with a width and height
  2. Change the background-image property

Demo

Code

This only works on Chrome 18+ and on Webkit Nightlies built in 2012 onwards. It seems to be a side effect of the CSS4 crossfading work, though this is a lot more useful.

<div id="cf6_image" class="shadow"></div>

Then the CSS:

#cf6_image {
	margin:0 auto;		
	width:450px;
	height:281px;
	transition: background-image 1s ease-in-out;
	
	background-image:url("/images/Stones.jpg");
}

#cf6_image:hover {
	background-image:url("/images/Summit.jpg");		
}	

Pretty cool - this can easily be extended by simply changing the background-image property with JS, and makes things much much simpler. I'm not sure if this behaviour is part of the spec or not, and I haven't seen support anywhere other than in the afore mentioned browsers.

For a slightly more detailed example, have a look at a simple gallery using filters and fades.

Sliding content

Often used as part of an image gallery or to show additional information, again this can be done in javascript by gradually changing the padding of elements. This often looks choppy on mobile devices, and frames can be missed if the animation is quick. CSS transitions plus transforms help out to make this a simple effect to create.

Have a look at a more complete example on the demos page.

Demo 1 - Sliding by adding padding (transitions)

For more information on these, check this updated post: cross browser implementation.

Plan

  1. Create a container with overflow set to hidden.
  2. Inside that container, create another container with width equal to the width of all the images added together.
  3. Inside that, float the images left with no padding or margin.
  4. When clicking a control, change the left position of the second container to show the required image.

Demo

Image 1Image 2Image 3Image 4

Code

Firstly, the mark up:

<div id="slide1_container">
	<div id="slide1_images">
		<img src="/images/Cirques.jpg" />
		<img src="/images/Clown%20Fish.jpg" />
		<img src="/images/Stones.jpg" />
		<img src="/images/Summit.jpg" />		
	</div>
</div>
<p id="slide1_controls">
	<span id="slide1-1">Image 1</span>
	<span id="slide1-2">Image 2</span>
	<span id="slide1-3">Image 3</span>
	<span id="slide1-4">Image 4</span>
</p>	

The CSS:

#slide1_controls span {
	padding-right:2em;
	cursor:pointer;
}
#slide1_container {
	width:450px;
	height:281px;
	overflow:hidden;
	position:relative;
}
#slide1_images {
	position:absolute;
	left:0px;
	width:1800px;
	-webkit-transition:all 1.0s ease-in-out;
	-moz-transition:all 1.0s ease-in-out;
	-o-transition:all 1.0s ease-in-out;
	-ms-transition:all 1.0s ease-in-out;	
	transition:all 1.0s ease-in-out;
}
#slide1_images img {
	padding:0;
	margin:0;
	float:left;
}

Again, I'm using javascript to bind events to the clickable controls. This time I'm adding the number of pixels to slide into the js, though I could have defined classes for this.

$(document).ready(function() {
	$("#slide1-1").click(function() {
		$("#slide1_images").css("left","0");
	});
	$("#slide1-2").click(function() {
		$("#slide1_images").css("left","-450px");
	});
	$("#slide1-3").click(function() {
		$("#slide1_images").css("left","-900px");
	});
	$("#slide1-4").click(function() {
		$("#slide1_images").css("left","-1350px");
	});
});

This code could be abstracted and improved, but we are looking to keep it simple here.

Demo 2 - Sliding by translating the images (transitions and transforms)

Note: Animating by transitioning transforms is hardware accelerated on iOS, making this a good option there.

Plan

  1. Create a container with overflow set to hidden.
  2. Inside that container, create another container with width equal to the width of all the images added together.
  3. Inside that, float the images left with no padding or margin.
  4. When clicking a control, translate the second container to show the required image.

Demo

Image 1Image 2Image 3Image 4

Code

Exactly the same as Demo 1, but the JS looks like this: (times 5 for the vendor specific markup)

$(document).ready(function() {
	$("#slide2-1").click(function() {
		$("#slide2_images").css("-webkit-transform","translate(0px, 0px)");
	});
	$("#slide2-2").click(function() {
		$("#slide2_images").css("-webkit-transform","translate(-450px, 0)");
	});
	$("#slide2-3").click(function() {
		$("#slide2_images").css("-webkit-transform","translate(-900px, 0)");
	});
	$("#slide2-4").click(function() {
		$("#slide2_images").css("-webkit-transform","translate(-1350px, 0)");
	});
});

Flipping content

Demo 1 - Flipping a simple image to a div (transitions and 3d transforms)

As of January 2012, this works in Safari (inc. iOS), Chrome, Firefox 10 and IE10.

Plan

  1. Put an image on top of a div inside a container.
  2. Put that in another container with perspective defined.
  3. When hovering on the outside container, add a rotate around the Y axis to the inside container.

Demo

This is nice for exposing more information about an image.

Any content can go here.

Code

First, the markup.

<div id="f1_container">
	<div id="f1_card" class="shadow">
		<div class="front face">
			<img src="/images/Stones.jpg"/>
		</div>
		<div class="back face center">
			<p>This is nice for exposing more information about an image.</p>
			<p>Any content can go here.</p>
		</div>
 		</div>
</div>
	

Then the CSS, stripped of the vendor prefixes to keep it clean.

#f1_container {
	position: relative;
	margin: 10px auto;
	width: 450px;
	height: 281px;
	z-index: 1;
}
.face.back {
	display: none;
}

#f1_container {
	perspective: 1000;				
}
#f1_card {
	width: 100%;
	height: 100%;	
	transform-style: preserve-3d;
	transition: all 1.0s linear;	
}
#f1_container:hover #f1_card {
	transform: rotateY(180deg);
	box-shadow: -5px 5px 5px #aaa;
}
.face {
	position: absolute;
	width: 100%;
	height: 100%;
	backface-visibility: hidden;
}
.face.back {
	display: block;
	transform: rotateY(180deg);
	box-sizing: border-box;
	padding: 10px;
	color: white;
	text-align: center;
	background-color: #aaa;
}
	

You can also flip around the X and Z axes:

Note that I've had to change the shadow to keep it looking normal.

This is nice for exposing more information about an image.

Any content can go here.

Case Study: Flipping cards on the Southampton Hackney Association's Website

Part of the design for the Southampton Hackney Association included a grid of sponsors. The design was such that on hover or click, they would flip over revealing a contact number, email address or URL. We wanted this site to work on browsers that didn't support 3D transforms, as at the time, only webkit had support.

Code path for browsers with 3D transforms

The code used is exactly as above. The markup consists of a div, containing two divs for the back and front faces.

Fallback

In older browsers, jQuery Flip is used. Modernizr is used to detect support for 3D transforms and if not, the markup is altered to fit how jQuery flip requires it to be. The key part is that for normal browsers, normal 3D transforms are used, with none of the fluff required to get this to work.

Due to issues with getting jQuery flip to work on hover, the behaviour was changed to work on click.

Using this technique, the effect works on all browsers in use, back to IE6. The flip effect is of much higher quality on browsers that support 3D transforms, but still has the distinctive look and feel on older browsers. As we move forward, the percentage of users who hit the fallback will decrease.

Accordions

Plan

  1. Mark up a few sections with a title and content
  2. Set the height to all but the first to 0, and overflow to hidden on all of them
  3. Bind click events on the titles to change the heights

Demo

A long paragraph

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus

A medium paragraph

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.

Two short paragraphs

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante.

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante.

Not sure if the use/abuse of Unicode is really a good idea, but you can see that it's pretty easy to get a simple accordion working.

I'm using classes again here to define different states, then using jQuery to turn them on and off. As always, I could use the :target pseudo selector, but I'd want to use preventDefault() onClick anyway to prevent the page skipping up and down, so I might as well just do it all in jQuery.

The Code

HTML:

<div id="accordion">
	<section id="item1">
		<p class="pointer">&#9654;</p><h1><a href="#">A long paragraph</a></h1>
		<p>Pellentesque habitant... </p>
	</section>
	<section id="item2" class="ac_hidden">
		<p class="pointer">&#9654;</p><h1><a href="#">A medium paragraph</a></h1>				
		<p>Pellentesque habitant... </p>
	</section>
	<section id="item3" class="ac_hidden">
		<p class="pointer">&#9654;</p><h1><a href="#">Two short paragraphs</a></h1>				
		<p>Pellentesque habitant... </p>
		<p>Pellentesque habitant... </p>		
	</section>	
</div>	

CSS:

#accordion section,	#accordion .pointer, #accordion h1, #accordion p {
	-webkit-transition: all 0.5s ease-in-out;
	-moz-transition: all 0.5s ease-in-out;
	-o-transition: all 0.5s ease-in-out;
	-ms-transition: all 0.5s ease-in-out;	
	transition: all 0.5s ease-in-out;
}
#accordion {
	margin-bottom:30px;
}
#accordion h1 {
	font-size:20px;
	background-color:rgba(255,0,0,0.3);
	margin:0;
	padding: 10px 10px 10px 30px;
}

#accordion h1 a {
	color:black;		
}
#accordion section {
	overflow:hidden;
	height:220px;
	border:1px #333 solid;
}
#accordion p {
	padding:0 10px;
	color:black;
}
#accordion section.ac_hidden p:not(.pointer) {
	color:#fff;
}

#accordion section.ac_hidden {
	height:44px;
}
#accordion .pointer {
	padding:0;
	margin:10px 0 0 6px;
	line-height:20px;
	width:13px;
	position:absolute;		
}
#accordion section:not(.ac_hidden) h1 {
	background-color:rgba(255,0,0,0.7);		
}

#accordion section:not(.ac_hidden) .pointer {
	display:block;
	-webkit-transform:rotate(90deg);
	-moz-transform:rotate(90deg);
	-o-transform:rotate(90deg);
	-ms-transform:rotate(90deg);	
	transform:rotate(90deg);						
	padding:0;
}

Plus a bit of javascript to turn the classes on and off.

$(document).ready(function() {
	$("#accordion section h1").click(function(e) {
		$(this).parents().siblings("section").addClass("ac_hidden");
		$(this).parents("section").removeClass("ac_hidden");

		e.preventDefault();
	});
});

Notes on browser support

Everything here has been available in at least one browser since 2008. Firefox and Opera have only added support in 2010, hence the reason this technology is becoming more mainstream.

Transitions and 3D transforms were added in IE10, while 2D transforms are available in IE9.

CSS Transitions

First introduced

CSS 2D Transformations

First introduced

CSS Animations

First introduced

CSS 3D Transformations

First introduced

How will it look in legacy browsers?

With all these examples, no attempt has been made to hack around browsers with no support, other than adding the opacity filter in IE where appropriate. This ends up as two lines, as they changed it to a different incorrect syntax between versions 7 and 8.

In most cases, the transition happens, but with no animation - you see the beginning and end frames but nothing in between. Depending on the site, this may or may not be acceptable. Modernizr provides a nice feature detection library, allowing you to easily add different CSS/JS for legacy browsers.

The best way to do this in practice is to include Modernizr, then to use some javascript such as:

speed = 500;

var vP = "";
var transitionEnd = "transitionEnd";
if ($.browser.webkit) {
	vP = "-webkit-";
	transitionEnd = "webkitTransitionEnd";
} else if ($.browser.msie) {
	vP = "-ms-";
	transitionEnd = "msTransitionEnd";	
} else if ($.browser.mozilla) {
	vP = "-moz-";
	transitionEnd = "transitionend";
} else if ($.browser.opera) {
	vP = "-o-";
	transitionEnd = "oTransitionEnd";
}	
	
function animate(object, cssProperties, callback, ms) {
	if (!ms) {
		ms = speed;
	}

	if (Modernizr.csstransitions) {
		object.css(vP+"transition", "all "+ms+"ms ease-in-out");

		object.css(cssProperties);

		if ($.isFunction(callback)) {

			object.bind(transitionEnd,function(){
				object.unbind(transitionEnd);
				callback();
			});

		}

	} else {
		if ($.isFunction(callback)) {		
			object.animate(cssProperties, ms, callback);
		} else {
			object.animate(cssProperties, ms);			
		}
	}
}

You can then use javascript to animate something and know that in modern browsers it will use a transition, while in older ones you get the jQuery animation. On the iPhone and iPad particularly this approach gets you much higher frame rates than just using the jQuery animate method.

If you are using simple properties, you can call it like this:

	animate($("#someID"),{"left":"100px"});

For things where the property to be changed has the vendor prefix concatenated to it, use:

var cssArgs = {};
cssArgs[vP+"transform"] = "translate(100px,0px)";

animate($("#someID"),cssArgs);

If you are better at JS than me and can think of a way to make this less clumsy, please let me know!

References

Where to look to find out more.

Fork me on GitHub