Animated CSS3 cube using 3D transforms

Last week Web­Kit included the much anti­cip­ated (at least on my part) 3D trans­forms in its latest nightly build, announced prac­tic­ally along­side the awe­some Snow Stack demo that provides a 3D inter­face for brows­ing Flickr images (use left, right and space-bar). Today the Surfin Safari blog has updated with some more excit­ing demos, includ­ing “Morphin Power Cubes” and “Poster Circle”. It is now pos­sible to cre­ate all sorts of crazy three-dimensional and anim­ated user inter­faces; the power comes largely in -webkit-perspective and a num­ber of updated trans­forms – adap­ted to incor­por­ate the Z axis.

Since work­ing on the 3D cube using 2D trans­forms back in April I’ve exper­i­mented with per­spect­ive to cre­ate some­thing more power­ful, play­ing around with 3D trans­forms on the iPhone a few times (e.g. this early rotat­ing demo). Now I’ve got some­thing worth sharing.

A 3D cube can be cre­ated solely in CSS, with all six faces. Using JavaS­cript to detect key presses and update inline styles this cube can be intu­it­ively navigated.

Res­ult

A 3D cube that rotates using the Up, Down, Left and Right arrow keys.
Sup­por­ted browsers: Web­Kit Nightly r46042+

3D cube interface using new WebKit transforms

How To

I’ll start with the markup, because it’s simple. Each of the six cube faces is given a face class and another relat­ing to it’s num­ber. These six faces sit within a cube con­tainer, which sits in another wrap­per, each is necessary.

<div id="experiment">
	<div id="cube">
		<div class="face one">
			One face
		</div>
		<div class="face two">
			Up, down, left, right
		</div>
		<div class="face three">
			Lorem ipsum.
		</div>
		<div class="face four">
			New forms of navigation are fun.
		</div>
		<div class="face five">
			Rotating 3D cube
		</div>
		<div class="face six">
			More content
		</div>
	</div>
</div>

The outer wrap­per serves as a cam­era, on which you apply some per­spect­ive — appro­pri­ate 3D trans­form­a­tions are then applied to des­cend­ants. -webkit-perspective defines the depth of the Z-plane and rel­at­ive sizes of ele­ments above and below it, -webkit-perspective-origin spe­cifies the perspective’s ori­gin. View a per­spect­ive example (webkit.org)

    #experiment {
      -webkit-perspective: 800;
      -webkit-perspective-origin: 50% 200px;
    }

The second con­tainer, the actual cube, has a spe­cified height, mar­gin, pos­i­tion, etc. as usual. The height and width are neces­sary to cre­ate some con­fines for the cube face trans­form­a­tions — altern­at­ively the width defaults to 100% and the cube’s appear­ance would vary with win­dow width. -webkit-transition (doc­u­ment­a­tion) defines the anim­ated prop­erty, dur­a­tion and timing-function — we’re anim­at­ing the 3d trans­form­a­tion (via -webkit-transform) lin­early for two seconds. -webkit-transform-style determ­ines whether child ele­ments lie flat against their par­ent (“flat”) or remain in 3D space (“preserve-3d”).

    #cube {
      position: relative;
      margin: 0 auto;
      height: 400px;
      width: 400px;
      -webkit-transition: -webkit-transform 2s linear;
      -webkit-transform-style: preserve-3d;
    }

Using the .face class com­mon styles are applied to the six sides; col­or­ing, size, pad­ding, etc. Import­antly they are each posi­tioned abso­lutely, rel­at­ive to the cube con­tainer. The back­ground rgba prop­erty is included to make the cube look pretty and transparent.

    .face {
      position: absolute;
      height: 360px;
      width: 360px;
      padding: 20px;
      background-color: rgba(50, 50, 50, 0.7);
    }

Each of the faces, one through six, needs to be rotated in 3D space to its cor­rect start­ing pos­i­tion. Using translateZ the ele­ments are brought 200px (half their width) off the Z-plane. Each of the faces must be at 90 degrees. Rotat­ing solely in the X plane pos­i­tions the top and bot­tom faces (one, six), before rotat­ing the last four faces in the Y plane, much like ori­gami. The extra rotate on the sixth face rotates the con­tent in 2D space to cor­rect its orientation.

    #cube .one  {
      -webkit-transform: rotateX(90deg) translateZ(200px);
    }

    #cube .two {
      -webkit-transform: translateZ(200px);
    }

    #cube .three {
      -webkit-transform: rotateY(90deg) translateZ(200px);
    }

    #cube .four {
      -webkit-transform: rotateY(180deg) translateZ(200px);
    }

    #cube .five {
      -webkit-transform: rotateY(-90deg) translateZ(200px);
    }

    #cube .six {
      -webkit-transform: rotateX(-90deg) translateZ(200px) rotate(180deg);
    }

Our cube is now com­plete — but it doesn’t move! With a key­down event listener we can incre­ment X and Y angles based on dif­fer­ent key presses, before apply­ing them as inline styles on the cube con­tainer. In com­bin­a­tion with the trans­ition effect on #cube, all six faces rotate in sync from their ori­ginal pos­i­tion to the newly defined angle, cre­at­ing a seam­less 3D cube interface.

  	var xAngle = 0, yAngle = 0;
	document.addEventListener('keydown', function(e)
	{
		switch(e.keyCode)
		{

			case 37: // left
				yAngle -= 90;
				break;

			case 38: // up
				xAngle += 90;
				break;

			case 39: // right
				yAngle += 90;
				break;

			case 40: // down
				xAngle -= 90;
				break;
		};

		$('cube').style.webkitTransform = "rotateX("+xAngle+"deg) rotateY("+yAngle+"deg)";
	}, false);

Accordion using only CSS

An accor­dion effect can be achieved using CSS3’s :target pseudo-class, without requir­ing JavaS­cript. Using the pro­pri­et­ary -webkit-transition prop­erty this accor­dion can also be animated.

Res­ult

CSS3 Accor­dion
Works in browsers that sup­port the :target pseudo-class, see the Quirks Mode com­pat­ib­il­ity tables. Anim­a­tion works in recent Web­Kit based browsers.

How To

Each part of the accor­dion has an ID, head­ing and con­tent region. The header includes a link that matches the section’s ID, whilst the con­tent is wrapped in a con­tainer which will con­trol its display.

<div class="accordion">
	<h2>Accordion Demo</h2>
	<div id="one" class="section">
		<h3>
			<a href="#one">Heading 1</a>
		</h3>
		<div>
			<p>Content</p>
		</div>
	</div>
	<div id="two" class="section">
		<h3>
			<a href="#two">Heading 2</a>
		</h3>
		<div>
			<p>Content</p>
		</div>
	</div>
</div>

The CSS then relies on the :target pseudo-class to apply dif­fer­ent styles to the chosen sec­tion — increas­ing the height and, in large con­tent cases, alter­ing the over­flow beha­viour to allow scrolling. To anim­ate the open­ing and clos­ing of sec­tions the -webkit-transition prop­erty is needed (doc­u­ment­a­tion), in this case act­ing on the height attrib­ute for a dur­a­tion of 0.3 seconds using the ease-in tim­ing function.

Strip­ping out the styl­ing, the CSS boils down to:

.accordion h3 + div {
	height: 0;
	overflow: hidden;
	-webkit-transition: height 0.3s ease-in;
}

.accordion :target h3 + div {
	height: 100px;
}

.accordion .section.large:target h3 + div {
	overflow: auto;
}

Cri­tique

Obvi­ously this approach has its lim­it­a­tions. Mul­tiple open accor­di­ons on one page wouldn’t be pos­sible — restric­ted by a URI’s one frag­ment iden­ti­fier limit; as one accor­dion opens the other would lose the tar­get and auto­mat­ic­ally close. Sim­il­arly, pages that use a frag­ment iden­ti­fier for every­day use will notice oddit­ies — take for instance when using top links to return to the top of the page, any accor­dion would, in this case, reset. Other uses include access­ib­il­ity links and sim­u­lated page his­tor­ies when using Ajax.

Moving markup towards HTML5

Hav­ing read John Resig’s “HTML5 Shiv” art­icle and Remy Sharp’s “HTML5 enabling script”, it felt like the right time to begin the full fledged migra­tion from XHTML to a cross browser com­pat­ible HTML5 blog. All in all the pro­cess of updat­ing the tem­plates was pain­less, tak­ing about an hour or so to modify the Word­press Sand­box theme.

To enable IE6 and IE7 sup­port for new HTML5 tags, which are not nat­ur­ally styled, some JavaS­cript is neces­sary. As per the ‘shiv’ art­icle, Remy Sharp has a small script that cre­ates DOM ele­ments, one for each type of new HTML5 tag, the simple act of doing so leads Inter­net Explorer to apply styles to said tags. I slightly mod­i­fied the exist­ing script to add the recently pro­posed hgroup.

(function(){
	if(!/*@cc_on!@*/0) return;
	var e = "abbr,article,aside,audio,bb,canvas,datagrid,datalist,details,dialog,
		eventsource,figure,footer,hgroup,header,mark,menu,meter,nav,output,
		progress,section,time,video".split(','),i=0,length=e.length;
	while(i<length){
		document.createElement(e[i++])
	}
})();

Even though these tags accept style they don’t come with their default ren­der­ings. For that we need a bit of CSS to make block ele­ments behave as they should.

article, aside, dialog, footer, header, section, footer, nav, figure {
	display: block;
}

I’ve also updated the Eric Meyer reset script, remov­ing now deprec­ated HTML 4 tags and apply­ing reset to the new ele­ments, so they do not unex­pec­tedly inherit pad­ding, mar­gin, etc. in the future. These changes are not yet exhaustive.

Mov­ing onto the page’s actual markup, the new DOCTYPE and char­ac­ter encod­ing set­tings are remark­ably simple. Stand­ards based web devel­op­ment is get­ting easier. For browsers that do not sup­port HTML5, the new DOCTYPE still trig­gers stand­ards mode. The xmlns HTML attrib­ute is no longer neces­sary and the profile attrib­ute has been dropped.

<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
	<meta charset="UTF-8" />
	<title>FofR Online</title>

The header sec­tion has been placed in the appro­pri­ate <header> tags, and sim­il­arly with the footer. I’d hoped to include the ‘About Me’ sec­tion within this, but as part of the spe­cific­a­tion you can­not include head­ings within a <footer> element.

Each of the posts comes wrapped in an <article> tag, i.e. an inde­pend­ent ele­ment with con­tent that could stan­dalone. Within are the respect­ive <header> (con­tain­ing title and date) and <footer> (con­tain­ing meta links) ele­ments. Tech­nic­ally the meta links could be marked as <nav>, but the former is more fit­ting and still accept­able use.

The date makes use of HTML5’s <time> ele­ment, with a datetime attrib­ute that gives the pre­cise post­ing time, includ­ing timezone offset.

The pre­vi­ous and next links that fol­low the art­icle can com­fort­ably sit within a <nav> tag. Sim­il­arly, my side­bar region is pre­dom­in­antly nav­ig­a­tion based with lists of archives and cat­egor­ies, it’s been marked as such.

<article id="post-67" class="">
	<header>
		<h2 class="entry-title"><a href="" title="" rel="bookmark">POST TITLE</a></h2>
		<div class="entry-date">
			<time datetime="2009-04-30T15:54:28-07:00" class="published" title="2009-04-30T15:54:28-07:00">April 30, 2009 &#8211; 3:54 pm</time>
		</div>
	</header>
	<div class="entry-content">
		POST
	</div>
	<footer class="entry-meta">
		META LINKS
	</footer>
</article>

<nav id="nav-below" class="navigation clearfix">
	<div class="nav-previous"></div>
	<div class="nav-next"></div>
</nav>

One avenue I should explore is the inclu­sion of the <section> tag, which I’d like to break up indi­vidual posts, prob­ably by split­ting the con­tent at level three head­ings down­wards; thereby becom­ing the header of each new section.

It’ll be a while before the real bene­fits of HTML5 can be fully appre­ci­ated by every­one, but it feels good to make a start, how­ever small that step may be.

3D Cube using CSS transformations

Update (July 21st): Using newly released (cur­rently only in Web­kit Nightly releases) 3D trans­forms, a three dimen­sional, rotat­ing cube with cor­rect per­spect­ive is pos­sible, and as a bonus – the cube can be nav­ig­ated using arrow keys. Check out the latest demo, “Anim­ated CSS3 cube using 3D trans­forms”.

The impres­sion of a three dimen­sional cube can be cre­ated using mod­ern CSS tech­niques, without the need for JavaS­cript, imagery, can­vas or SVG. Using the pro­pri­et­ary trans­form prop­erty to skew and rotate shaded rect­angles, indi­vidual cube faces can com­bine to form a 3D object. Cur­rently only sup­por­ted in recent Web­Kit and Gecko based browsers, most import­antly Fire­fox 3.5+ -moz-transform (doc­u­ment­a­tion) and Safari 3.2+ -webkit-transform (doc­u­ment­a­tion).

To demon­strate the power of this effect a second exper­i­ment with mul­tiple cubes and pro­pri­et­ary Web­Kit trans­itions is also available.

Res­ults

A 3D cube cre­ated with CSS
Update (June 7th): Altered CSS slightly to use skew(x,y) rather than skewY, the lat­ter of which is not sup­por­ted in Safari 3 / Chrome.
Sup­por­ted browsers: Safari 3.2+, Google Chrome, Fire­fox 3.5+

Exper­i­ment with mul­tiple cubes and CSS trans­itions, still no JavaS­cript
Sup­por­ted browsers: Safari 4+, Google Chrome

Multiple cubes created using CSS

How To

Sim­ilar to my pre­vi­ous exper­i­ments, the HTML markup is very simple. Each of the faces has its own DIV, class and con­tent. The top face requires some extra markup to aid the trans­form­a­tion, more on that shortly.

<div class="cube">
	<div class="topFace">
		<div>
			Content
		</div>
	</div>
	<div class="leftFace">
		Content
	</div>
	<div class="rightFace">
		Content
	</div>
</div>

A short dis­claimer, the geo­metry in this example is ‘fudged’, in that the val­ues have been adjus­ted to appear roughly cor­rect. I know that the dimen­sions are slightly out of whack, this is merely to save my head from math­em­at­ics and to get the concept out there quickly for people to see. With that said, let’s crack on with the CSS.

Each of the three rect­angles is given a slightly dif­fer­ent shade of gray to give the impres­sion of depth, in this example the left face is in shadow. The faces are each posi­tioned abso­lutely, rel­at­ive to the cube con­tainer. Each face is 200 x 200 pixels, includ­ing 10 pixels of padding.

.cube {
	position: relative;
	top: 200px;
}

.rightFace,
.leftFace,
.topFace div {
	padding: 10px;
	width: 180px;
	height: 180px;
}

.rightFace,
.leftFace,
.topFace {
	position: absolute;
}

Now for the fun bit. The left and right rect­angles are skewed by ±30˚ along the ver­tical axis, with the right face shif­ted left by 200px, cleanly lin­ing up the two edges to cre­ate a corner that is cen­ter aligned.

.leftFace {
	-webkit-transform: skewY(30deg);
	-moz-transform: skewY(30deg);
	background-color: #ccc;
}

.rightFace {
	-webkit-transform: skewY(-30deg);
	-moz-transform: skewY(-30deg);
	background-color: #ddd;
	left: 200px;
}

The top face proves more prob­lem­atic; it needs to be skewed, scaled, rotated and posi­tioned. The skew is the same, –30˚ along the ver­tical axis, this skewed rect­angle must then be rotated clock­wise by 60˚. Rotat­ing the rect­angle itself leads to a change in ori­ent­a­tion of its con­tent, a con­tainer must be added and then rotated.

A simple way of cre­at­ing a top face without resort­ing to maths is to duplic­ate the left and right rect­angles, skew them in the oppos­ite dir­ec­tions (by invert­ing the sign, e.g. left face is now skewed by –30˚) and pos­i­tion them against the exist­ing faces to cre­ate a dia­mond shape between the two sets. Now use pos­i­tion­ing and scal­ing to fill this dia­mond and form the top face, delet­ing the duplic­ates when fin­ished. My res­ults led to a scal­ing factor of 1.16 in the Y dir­ec­tion which I have accoun­ted for by redu­cing the font-size by the same factor.

.topFace div {
	-webkit-transform: skewY(-30deg) scaleY(1.16);
	-moz-transform: skewY(-30deg) scaleY(1.16);
	background-color: #eee;
	font-size: 0.862em;
}

.topFace {
	-webkit-transform: rotate(60deg);
	-moz-transform: rotate(60deg);
	top: -158px;
	left: 100px;
}

The final CSS looks like this:

.cube {
	position: relative;
	top: 200px;
}

.rightFace,
.leftFace,
.topFace div {
	padding: 10px;
	width: 180px;
	height: 180px;
}

.rightFace,
.leftFace,
.topFace {
	position: absolute;
}

.leftFace {
	-webkit-transform: skewY(30deg);
	-moz-transform: skewY(30deg);
	background-color: #ccc;
}

.rightFace {
	-webkit-transform: skewY(-30deg);
	-moz-transform: skewY(-30deg);
	background-color: #ddd;
	left: 200px;
}

.topFace div {
	-webkit-transform: skewY(-30deg) scaleY(1.16);
	-moz-transform: skewY(-30deg) scaleY(1.16);
	background-color: #eee;
	font-size: 0.862em;
}

.topFace {
	-webkit-transform: rotate(60deg);
	-moz-transform: rotate(60deg);
	top: -158px;
	left: 100px;
}

Auto-scrolling Parallax Effect without JavaScript

Here’s another quick CSS3/WebKit trans­itions pro­ject in the con­tro­ver­sial realm of CSS anim­a­tion. This time I have opted to recre­ate the pop­u­lar par­al­lax effect using mul­tiple back­ground images on a single ele­ment and the -webkit-transition prop­erty (doc­u­ment­a­tion). I have based this on Chris Coyier’s par­al­lax tutorial, reusing the star images with per­mis­sion, the tech­nique itself was coined by Paul Annett (explan­a­tion on Think Vit­amin). If you’re not quite sure what par­al­lax is, then Chris and Paul both go into some depth to explain it and Wiki­pe­dia is always helpful.

Res­ult

Exper­i­ment: Auto-scrolling CSS3 Par­al­lax Effect
Exper­i­ment works in Safari 4 Beta and Google Chrome. No JavaS­cript necessary.

Correctly rendered background images for parallax effect

How To

The HTML markup is fairly simple, one DIV for the back­ground and another for the con­tent, the example uses CSS3’s mul­tiple back­grounds, so no need for extra markup to accom­mod­ate all those other images:

<div id="background"></div>
<div id="content">
	Content
</div>

For the CSS the back­ground con­tainer is set to a fixed pos­i­tion (for con­veni­ence more than any­thing) and spread across the bot­tom of the page using the top, left, bot­tom and right prop­er­ties. The back­ground images are defined using the back­ground short­hand prop­erty with mul­tiple declar­a­tions being comma delim­ited, the first being the top-most. Each of the images has a dif­fer­ent pos­i­tion defined in per­cent­age, so as the size of the con­tainer changes (e.g. on win­dow res­ize) the images move dis­pro­por­tion­ately to each other; cre­at­ing the impress­ive par­al­lax effect.

#background {
	background: url('../images/foreground.png') 5% 5%,
		url('../images/midground.png') 20% 20%,
		url('../images/background.png') 90% 110%;
}

Ordin­ar­ily this effect is only seen when the page is re-sized or JavaS­cript is used for anim­a­tion. My first approach to anim­a­tion via CSS was to apply the trans­ition to the background-positions, with background-position being an animat­able prop­erty as defined in the pro­posed spe­cific­a­tion. How­ever this doesn’t yet work in the latest Web­Kit nightly build (r42142), it is a known bug.

As an altern­ate route, albeit a tem­por­ary one, I have opted to use trans­itions to anim­ate the left-most edge of the back­ground con­tainer (for instance from 0px to –100px). This gradu­ally alters the over­all width of the con­tainer caus­ing the back­grounds to shift dis­pro­por­tion­ately as per their per­cent­ages, cre­at­ing the par­al­lax effect. With a large enough dur­a­tion and left pos­i­tion the effect appears to be continuous.

#background {
	left: 0;
	-webkit-transition: left 300s linear;
}

#experiment:target #background {
	left: -5000px;
}

To make things a bit more fun I’ve increased the ‘fly­ing speed’ when the mouse hov­ers over the back­ground area. The final CSS looks like this:

#background {
	background: url('../images/foreground.png') 5% 5%,
		url('../images/midground.png') 20% 20%,
		url('../images/background.png') 90% 110%;
	top: 218px;
	left: 0;
	right: 0;
	bottom: 0;
	position: fixed;
	-webkit-transition: left 300s linear;
}

#experiment:target #background {
	left: -5000px;
}

#experiment:hover #background {
	left: -9999px;
}