An analogue clock using only CSS

Hav­ing read the blurb around Safari’s CSS tran­si­tions I opted to famil­iar­ize myself with a quick project — the aim of which was to cre­ate a func­tional, CSS only, ana­logue clock.

Result

Exper­i­ment: CSS Ana­logue Clock
Exper­i­ment works in Safari 4 Beta and Google Chrome. A work­ing clock that option­ally resorts to JavaScript to grab the cur­rent time (can be achieved by other means).

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.

How To

Before get­ting into the nitty gritty I cre­ated four images, a clock face and three trans­par­ent PNG hands (sec­onds, min­utes and hours), ensur­ing that each of these were the same size so that when over­layed their cen­tres would align. The HTML and CSS to get us going is as follows:

<div id="clock">
	<div id="hour"><img src="images/hourHand.png" /></div>
	<div id="minute"><img src="images/minuteHand.png" /></div>
	<div id="second"><img src="images/secondHand.png" /></div>
</div>
#clock {
position: relative;
width: 378px;
height: 378px;
background-image: url('../images/clockFace.png');
left: 50%;
margin: 5em 0 0 -189px;
}

#clock div {
position: absolute;
}

The magic that rotates the clock’s hands comes via two WebKit spe­cific CSS prop­er­ties, -webkit-transition (doc­u­men­ta­tion) and -webkit-transform (doc­u­men­ta­tion). The trans­form prop­erty can alter the appear­ance of an ele­ment via a two dimen­sional trans­for­ma­tion, for instance: scal­ing, rotat­ing and skew­ing a DIV ele­ment. In this case it is used to rotate the clock hands to the cor­rect angles; the CSS below puts the hour hand at 3 o’clock:

#clock img[src*='hour'] {
-webkit-transform: rotate(90deg);
}

The tran­si­tion prop­erty cre­ates an ani­ma­tion of a spec­i­fied prop­erty between two val­ues when trig­gered, for instance fad­ing the opac­ity on a DIV ele­ment from 1 to 0 — trig­gered using the :hover pseudo class. Tran­si­tion dura­tion and the tran­si­tion tim­ing func­tion (e.g. lin­ear) should also be set, amongst other optional prop­er­ties. In this exam­ple the tran­si­tion is from one trans­for­ma­tion angle to another with dura­tions that match the appro­pri­ate clock hand, so the sec­ond hand takes 60 sec­onds to com­plete a 360 degree rota­tion. The tran­si­tion is trig­gered using the :tar­get pseudo ele­ment — if the URI con­tains the ‘clock’ frag­ment then the time piece shall start ticking.

#clock img[src*='second'] {
/* -webkit-transition: property duration timing-function */
-webkit-transition: -webkit-transform 60s linear;
}

#clock:target img[src*='second'] {
-webkit-transform: rotate(360deg);
}

The above tran­si­tion lasts only one rota­tion but by alter­ing the dura­tion length and degree of rota­tion in accor­dance the sec­ond hand can keep on going (e.g. 600 sec­onds and 3600 degrees rota­tion gives a bat­tery life of 10 min­utes), a fairly safe assump­tion that users will not stay on the page for too long.

#clock img[src*='second'] {
-webkit-transition: -webkit-transform 600000s linear;
}

#clock:target img[src*='second'] {
-webkit-transform: rotate(3600000deg);
}

#clock img[src*='minute'] {
-webkit-transition: -webkit-transform 360000s linear;
}

#clock:target img[src*='minute'] {
-webkit-transform: rotate(36000deg);
}

Grab the cur­rent time

Although the ani­ma­tion works beau­ti­fully, CSS alone is not capa­ble of obtain­ing the cur­rent time. To start the clock at the cor­rect time a dynamic trans­for­ma­tion needs to be applied to the clock hand con­tain­ers, this is eas­i­est done with inline styles and can be set in any num­ber of ways by the back­end when the page loads, thereby erad­i­cat­ing any need for JavaScript.

Alter­na­tively, if you’ve no objec­tions to using JavaScript, I’ve cre­ated a small startClock() func­tion to do the job (albeit using Pro­to­type 1.6.0.3 for my own con­ve­nience):

function startClock() {
	var angle = 360/60;
	var date = new Date();
	var hour = date.getHours();
	if(hour > 12) {
		hour = hour - 12;
	}
	var minute = date.getMinutes();
	var second = date.getSeconds();
	var hourAngle = (360/12)*hour + (360/(12*60))*minute;
	$('minute').setStyle('-webkit-transform: rotate('+angle*minute+'deg)');
	$('second').setStyle('-webkit-transform: rotate('+angle*second+'deg)');
	$('hour').setStyle('-webkit-transform: rotate('+hourAngle+'deg)');
}

A word of warn­ing — apply­ing the inline style directly to the image will over­ride the tran­si­tion effects defined in the CSS file.

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.