This is

Ben's Blog

Creating a clock in pure CSS

Not seen the demo? Don’t want to read the post? See the pure CSS clock here.

Do you ever get the urge to just make something? I do, quite often. Turns out having a blog is a pretty good way to do exactly that without having to buy *yet another* domain. Also gives me a good excuse to put something up that’s not worth buying a domain for…

Anyway. I was laying in bed a couple of nights ago thinking about making things on the Internet (cool, huh?!). I love doing exactly that; it feels like it did when I built Lego as a kid. In my head I was picturing a page with some spinning circles, with one colour replacing another. I was trying to figure out how to do it, and what I could use it for. I thought ‘timer’, and mulled it over in my head until I went to sleep.

Yesterday evening I sat down to have a play. With no real direction, I started writing some CSS for a timer. I pretty quickly realised that the lack of decent events in CSS meant I’d have to add some Javascript to the page to start and stop it and whatever else. So I adapted and decided to try and make a pure CSS clock. It worked out pretty well. If you haven’t seen it yet, go and have a look.

Looking at the clock

I’ve only tested it in the latest Chrome and Firefox versions on OSX (where it works fine). I’m pretty sure it won’t work in IE9 (no transitions, right?), and I don’t have a VM for IE10 yet, so can’t test on that. It runs the CPU (/GPU?) kinda hard, so probably best you don’t leave it running all day if you’re on a laptop battery. A quick look on Chrome Android (beta) seems like it kinda works, but the split second face doesn’t.

That said, I think it’s kinda pretty. Not a bad job, and I’m surprised how smooth it is. CSS has come a long way in the last few years!

Feel free to look at the source. I haven’t minimised any of the CSS (for the clock), so you can see how I did it. Apologies if it’s a little scrappy – as I said, there was no real planning – I just wanted to see if I could do it, and adapted on the fly.

Explaining the source

The clock is made of 4 faces. Each one uses the same set of CSS keyframe animations. Because they’re sharing (the keyframes), they have the same colours. I changed the opacity of each face so it sits better (and because it’s kinda of neat to see the faces underneath each other).

The top face is the hour (1-12), the next is the minutes, then seconds, then a single second. In the PM the hour disc will be red, with the white replacing it, in the AM the hour disc will be white, with the red replacing it. Hours are white being replaced with red when they’re even, and the other way round when they’re odd. Minutes are the same – white being replaced with red when they’re even, the other way round when they’re odd.

Each face is made of 4 elements. One base, with 3 inside. The faces are absolutely placed inside a container with a z-index set, so they sit on top of each other nicely. We’ll look at the first face, the one that runs every second.

<div id="timer1" class="timerSize1">
	<div id="timer1a" class="timerSize1"></div>
	<div id="timer1b" class="timerSize1"></div>
	<div id="timer1c" class="timerSize1"></div>
</div>

All of the faces (top to bottom) share some common CSS:

.timerSize1 {
	-moz-box-sizing: border-box; 
	-webkit-box-sizing: border-box; 
	box-sizing: border-box;
	-webkit-animation-iteration-count: infinite;
	animation-iteration-count: infinite;
}

This sets our sizing as border-box (so the padding and border counts in our width measurements), and sets are animation count to keep going infinitely. It’s important to note here I’ve dropped all the vendor specific prefixes except -webkit. Firefox doesn’t need them now, fingers crossed Chrome will join them soon.

Each element in a face also has some specific CSS:

.timerSize1 {
	position: absolute;
	z-index: 1;
	width: 360px;
	height: 360px;
	border-radius: 180px;
}

Here we’re setting the position of the face (the actual position is given later), the z-index for the elements in that face, and the face size. By setting the border-radius to half the size we can create a lovely circle.

#timer1 {
	opacity: .2;
	top: 10px;
	left: 10px;
	background-color: #ddd;
	-webkit-animation-timing-function: steps(1);
	animation-timing-function: steps(1);
	-webkit-animation-duration: 2s;
	animation-duration: 2s;
	-webkit-animation-name: timerBGChange;
	animation-name: timerBGChange; 
}

In the base element we’re setting the opacity for the face, the actual position within our container, the *initial* background colour, and some animation specifics for the base element. Here we’re saying – use the keyframe animation ‘timerBGChange’, run it every two seconds, and do it in a step (ie not smoothly).

Inside the base element, we style 3 other elements:

#timer1a {
	background-color: #f43928;
	clip:rect(0px,180px,360px,0px);
	-webkit-animation-timing-function: linear,steps(1);
	animation-timing-function: linear,steps(1);
	-webkit-animation-duration: 1s,2s;
	animation-duration: 1s,2s;
	-webkit-animation-name: timerRotate,timerBGChange2;
	animation-name: timerRotate,timerBGChange2; 
}
#timer1b {
	background-color: #f43928;
	clip:rect(0,360px,360px,180px);
	-webkit-animation-timing-function: steps(1);
	animation-timing-function: steps(1);
	-webkit-animation-duration: 2s;
	animation-duration: 2s;
	-webkit-animation-name: timerBG1;
	animation-name: timerBG1;
}
#timer1c {
	background-color: #f43928;
	clip:rect(0px,180px,360px,0px);
	-webkit-animation-timing-function: steps(1);
	animation-timing-function: steps(1);
	-webkit-animation-duration: 2s;
	animation-duration: 2s;
	-webkit-animation-name: timerBG2;
	animation-name: timerBG2;
}

In each element, we’re setting the background colour to red. We clip each layer so the circle is cut in half. You’ll note 1c sits directly over the top of 1a. 1b is the opposite side. Two halves giving the impression of a circle.

If you look carefully, you’ll see in this face the animation is 1s and 2s. If subsequent faces it’s 60s and 120s or 3600s and 7200s, etc. This is because each colour counts as one second (or minute / hour / half day) – 1 second red, 1 second white. Elements 1b and 1c are set to run in steps. Element 1a is a little bit special, because we’re running two keyframe animations on it, one linear (smoothly) and one stepping, the linear one every second and the step one every 2 seconds.

Let’s look at the keyframe animations (note, a webkit version of these has also been added):

@keyframes timerBGChange {
    0% { background-color: #f43928; }    
    50% { background-color: #ddd; }    
}
@keyframes timerRotate {
    0% { transform:rotate(0); }
    100% { transform:rotate(360deg); }    
}
@keyframes timerBGChange2 {
    0% { background-color: #ddd; }
    50% { background-color: #f43928; }    
}
@keyframes timerBG1 {
    0% { opacity: 0; background-color: #ddd; } 
    25% { opacity: 1; }
    50% { opacity: 0; background-color: #f43928; } 
    75% { opacity: 1; }
}
@keyframes timerBG2 {
    0% { opacity: 1; } 
    25% { opacity: 0; background-color: #ddd; }  
    50% { opacity: 1; } 
    75% { opacity: 0; background-color: #f43928; }
}

The first animation is for our base element. Remember this is a step, and runs every two seconds. So this says – switch the background colour, from red to white (ok, it’s really a very light grey) every 1 second. We can see exactly the same process in the animations used by all 3 other elements. The only difference is 1b and 1c, which have four steps (each every half second). On these, we’re changing the background colour, and we’re also changing the opacity from 1 to 0 – essentially hidden and visible. Lastly, we have a rotate. This is our linear animation and basically says – every second, smoothly rotate this element (one of our half circles) 360 degrees.

So what’s happening?

We have an element (1a) with a half circle that’s rotating 360 degrees every second. Every second, this element changes colour from red to white. Every half second this element will have covered either 1b or 1c. As it does we change the opacity to either display or hide the element (1b or 1c) and we change the colour. Every second we change the colour of the rotating element, and the background. This gives the impression of smooth circles eating each other up.

It’s a little tricky to explain (possibly moreso because we’re doing it with two colours), but I do recommend you save a local copy, remove some of the faces and tinker with the colours. It’s much easier to actually understand if you’re playing with it :-)

What else?

The keylines and numbers are pretty self explanatory. We’re just absolutely placing them to add some effects on the clock. I’ve also added some shadows just to make it stand out a little more.

The minute markers on the clock face deserve a little more explanation. If you look at the underlying HTML, you’ll see lots of <div> tags. Yeah, it’s pretty sucky, but there’s 60 of them, all nested. These are the notches. It’s done like this so the CSS to position them can be pretty simple:

#notches {
	top: 190px;
	left: 190px;
	position: absolute;
}
	#notches div {
		position: absolute;
		height: 181px;
		border-bottom: 10px solid #f43928;
		width: 1px;
		padding: 0;
		top: 0;
		left: 0;
		-webkit-transform-origin: 0 0;
		transform-origin: 0 0;
		background-color: transparent;
	}
		#notches div div {
			-webkit-transform: rotate(6deg);
			transform: rotate(6deg);
		}

We set the our base in the middle. We create the first notch by creating a long thin transparent element, and setting a border at the botom. We then set the origin for rotation and create a rule that says every other div inside a div should be rotated 6 degrees (60 * 6 = 360). Because the divs are nested, they rotate with each one going 6 degrees from the next. Despite a bit of fritting around, I couldn’t get half the elements to work in the same way in a cartwheel effect. I *think* the border was being taken into account for the transform origin, and throwing it out. So I gave up and did them individually. I’ve also had to set an overflow hidden on an outer div because these caused massive scrolling somewhere off the page. I have no idea why and didn’t spend any time figuring it out. Long and short, I probably shouldn’t be nesting 60 elements…

Finishing it off

We get the time using the server, and creating a negative second value to drop in to an animation delay (this css is set at the top of the document). This starts the animation midway through the cycle, putting the hands of the clock in place.

Then obviously we’re layering and sizing and changing the opacity on the other faces. There’s a little bit of extra styling on the numbers and the notches, and a background pattern from the wonderful (and wonderfully time saving) Subtle Patterns. But it all comes together quite nicely, IMO. Again, the clock can be found here. Hope it’s interesting to some of you. Enjoy!

1 Comment

  1. Kev

    Just tested it on browserstack ie10 and it works. But as it’s a virtual remote machine through the browser I can’t really tell how smooth it is. And yeh it doesn’t work in ie9, still it’s a great piece of code and a really pretty outcome.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2014 Ben's Blog. All rights reserved.