Rotating Feature Boxes

Published by Chris Coyier

This is what we're going to build:

The full effect of it (with transition animations) will work in newish WebKit and Opera browsers and Firefox 4 (in real beta as of today). Any other browser will rotate the blocks without transition animation.

View Demo   Download Files

The Interesting Part

All the animation is CSS3 transitions. JavaScript only watches for the clicks and applies and removes classes as needed. Why not use JavaScript for the animations as well and get wider browser support? CSS3 is far better suited for this. The animations are better (hardware accelerated). It doesn't slow down other JavaScript. It is also easier to write keeping the animations in CSS. With JavaScript we aren't able to animate to classes* easily.

As more time passes, more and more animations will be relegated to CSS and away from JavaScript libraries.

HTML

We'll give ourselves a semantic wrapper, which also provides the relative positioning context for all the blocks. The blocks are positioned with absolute positioning and ultimately animated by changing their top/left values, so that relative context is important. Each block has a div wrapper with a unique ID, as well as a class name indicating its current state. There are three self-explanatory state classes, starting with the obvious active which is the block currently in the fully viewable left-most state.

Each block has a subtitle which is viewable only when non-active and another div wrapper containing everything to be displayed when the block is active. The subtitle idea gives the opportunity to provide some kind of short teaser and entice clicks.

<div id="rotator">

	<div id="block-1" class="active">
		<h2>Subtitle #1</h1>
		<div>
			<h1>Seven Space Frogs Descend On Canada's Largest City</h1>
			<img src="spacefrog.jpg" alt="space frog">
			<p>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</p>
		</div>
	</div>
	
	<div id="block-2" class="non-active-top">
		<h2>Subtitle #2</h2>
		<div>
			<h1>The Power of the Voodoo. Who do? You do.</h1>
			<img src="goblins.jpg" alt="goblins">
			<p>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</p>
		</div>			
	</div>
	
	<div id="block-3" class="non-active-bottom">
		<h2>Subtitle #3</h2>
		<div>
			<h1>You May Find Yourself Living in a Shotgun Shack</h1>
			<p>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</p>
		</div>			
	</div>

</div>

CSS

Set up the wrapper and context:

#rotator { width: 920px; height: 280px; position: relative; background: white; padding: 20px; }

Unique styling to any of the blocks can be done through ID:

#block-1 { background: #d5fcff; }
#block-2 { background: #e1ffd5; }
#block-3 { background: #ffffd8; }

Each block shares some properties, in that the overflow is hidden, they are positioned absolutely, and will the animated at the same speed:

#rotator > div { 
	position: absolute; 
	overflow: hidden;
	-webkit-transition: all 0.5s ease;
	-moz-transition: all 0.5s ease;
	-o-transition: all 0.5s ease;
}

The three states of the boxes determine their size and position:

#rotator .active { top: 20px; left: 20px; width: 580px; height: 280px; }
#rotator .non-active-top { top: 20px; left: 620px; height: 130px; width: 320px; }
#rotator .non-active-bottom { top: 170px; left: 620px; height: 130px; width: 320px; }

The subtitle (h2) ends up being the exact size of the box by setting its line-height equal to the height of the box. This pushes everything else out of the way and hidden, since the overflow is hidden. Then when the box is in it's active state, we hide the subtitle and everything else is revealed naturally.

#rotator h2 {
	text-align: center; 
	line-height: 130px;
}
#rotator .active h2 {
	display: none;
}

jQuery

We make a rotate function which applies the proper classes to the proper blocks depending on what the current value is. There is probably a cleaner way to do this which scales better...

function rotate() {
				
	if (current == 1) {
		$("#block-1").removeClass().addClass("active");
		$("#block-2").removeClass().addClass("non-active-top");
		$("#block-3").removeClass().addClass("non-active-bottom");
	} else if (current == 2) {
		$("#block-1").removeClass().addClass("non-active-bottom");
		$("#block-2").removeClass().addClass("active");
		$("#block-3").removeClass().addClass("non-active-top");
	} else {
		$("#block-1").removeClass().addClass("non-active-top");
		$("#block-2").removeClass().addClass("non-active-bottom");
		$("#block-3").removeClass().addClass("active");
	}

}

Then we attach a click handler to each of the blocks. When they are clicked, we figure out which one it is through it's ID, set the current value to that, and call the rotate function.

$("#rotator div").click(function() {
	current = this.id.substr(6);			
	rotate();
});

Credit to Andrea Canton for the idea on checking which block was clicked, which enables the blocks to rotate in either direction which is more satisfying.

All Together Now

So you click a block, that block's class' are adjusted. The new classes have different size and position values. Because the block has transition CSS applied, those new sizes and postion values are animated to. Easy cheezy.

View Demo   Download Files


*jQuery UI has the ability to animate to classes by extending jQuery's addClass. It falls short in this scenario though, as we need to remove the previous class first (moving it back to a default location) before adding the new one, which makes the animations jumpy and wrong.