Creating a 3D Cube using 2D CSS transformations

The impres­sion of a three dimen­sional cube can be cre­ated using mod­ern CSS tech­niques, with­out the need for JavaScript, imagery, can­vas or SVG. Using the pro­pri­etary trans­form prop­erty to skew and rotate shaded rec­tan­gles, indi­vid­ual cube faces can com­bine to form a 3D object. Cur­rently only sup­ported in recent WebKit and Gecko based browsers, most impor­tantly Fire­fox 3.5+ -moz-transform (doc­u­men­ta­tion) and Safari 3.2+ -webkit-transform (doc­u­men­ta­tion).

To demon­strate the power of this effect a sec­ond exper­i­ment with mul­ti­ple cubes and pro­pri­etary WebKit tran­si­tions is also available.

Results

A 3D cube cre­ated with CSS
Sup­ported browsers: Safari 3.2+, Google Chrome, Fire­fox 3.5+

Altered CSS slightly to use skew(x,y) rather than skewY, the lat­ter of which is not sup­ported in Safari 3 / Chrome.
Using newly released (cur­rently only in Webkit Nightly releases) 3D trans­forms, a 3D rotat­ing cube with fixed per­spec­tive is pos­si­ble. The cube can also be rotated.
Exper­i­ment updated. Trans­forms are now widely sup­ported. Sup­port includes Opera, Fire­fox and IE9. Tran­si­tions are com­ing in IE10.

Exper­i­ment with mul­ti­ple cubes and CSS tran­si­tions, still no JavaScript
Sup­ported browsers: Safari 4+, Google Chrome

Multiple cubes created using CSS

How To

Sim­i­lar to my pre­vi­ous exper­i­ments, the HTML markup is very sim­ple. Each of the faces has its own DIV, class and con­tent. The top face requires some extra markup to aid the trans­for­ma­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 geom­e­try in this exam­ple is ‘fudged’, in that the val­ues have been adjusted 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­e­mat­ics and to get the con­cept out there quickly for peo­ple to see. With that said, let’s crack on with the CSS.

Each of the three rec­tan­gles is given a slightly dif­fer­ent shade of gray to give the impres­sion of depth, in this exam­ple the left face is in shadow. The faces are each posi­tioned absolutely, rel­a­tive to the cube con­tainer. Each face is 200 x 200 pix­els, includ­ing 10 pix­els 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 rec­tan­gles are skewed by ±30˚ along the ver­ti­cal axis, with the right face shifted left by 200px, cleanly lin­ing up the two edges to cre­ate a cor­ner 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­ti­cal axis, this skewed rec­tan­gle must then be rotated clock­wise by 60˚. Rotat­ing the rec­tan­gle itself leads to a change in ori­en­ta­tion of its con­tent, a con­tainer must be added and then rotated.

A sim­ple way of cre­at­ing a top face with­out resort­ing to maths is to dupli­cate the left and right rec­tan­gles, skew them in the oppo­site direc­tions (by invert­ing the sign, e.g. left face is now skewed by –30˚) and posi­tion them against the exist­ing faces to cre­ate a dia­mond shape between the two sets. Now use posi­tion­ing and scal­ing to fill this dia­mond and form the top face, delet­ing the dupli­cates when fin­ished. My results led to a scal­ing fac­tor of 1.16 in the Y direc­tion which I have accounted for by reduc­ing 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;
}
Paul Hayes

Paul Hayes is a developer at Last.fm. You should follow him on Twitter, where he talks about UX, HTML, CSS and JavaScript, amongst other cool stuff.