Categories:

Creating a live CSS clock using CSS3 and requestAnimationFrame()

Created: May 15th, 14'

In this tutorial we'll summon the inner clocksmith in us to build a clock interface using pure CSS, then bring it to life with JavaScript's requestAnimationFrame() method. And if like most of us you discover no such propensity for watch making at the end of your search, fret not, as what may initially seem like a daunting task boils down to just the churning out of a series of simple lines and round objects and dropping them into the right place. The final result looks like this:

Demo (works in IE9+, all modern versions of Chrome and Firefox):

Time is ticking, so lets get started, first, on the clock interface.

The outer clock face and main 12-6, 9-3 hour markers

We'll begin with the outer clock configuration, which is just a single DIV with two pseudo elements to render the 12-6 and 9.3 time markers:

CSS Code:

.outer_face {
position: relative;
width: 250px;
height: 250px;
border-radius: 250px;
background: white;
box-shadow: inset 0 0 10px gray;
border: 14px solid black
}

.outer_face::before, .outer_face::after{ /* 12-6 and 9-3 hour markers */
content: "";
position: absolute;
width: 10px;
height: 100%;
background: black;
z-index: 0;
left: 50%;
margin-left: -5px;
top: 0
}

.outer_face::after { /* 9-3 time marker specifically*/
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg)
}

HTML Markup:

<div class="outer_face">
</div>

Result:

".outer_face" is the main face of the clock, which is simply a DIV with a border-radius value equal to its width/height, creating a round element. A thick border is used to create a frame.

Since every element in HTML is capable of showing two "pseudo" elements (as of CSS2-3), we'll use our .outer_face DIV to render the primary 12-6 and 9-3 hour markings on the clock face, via .outer_face::before{} and .outer_face::after{}. As you can see, both elements are 10px thin strips centered within the face through a combination of absolute positioning and a negative left margin. The second element is rotated 90 degrees from its center to form the 9-3 marker.

Adding the other hour markers

To add in the remaining hour markers on the clock face, we'll define four DIVs inside the clock face, each carrying the CSS class "marker". This class will share the same basic style as the primary 12-6 and 9-3 hour markings, but with a gray background and rotated varying degrees depending on the hours they indicate:

CSS Code:

.outer_face::before, .outer_face::after, .outer_face .marker{ /* All hour markers */
content: "";
position: absolute;
width: 10px;
height: 100%;
background: black;
z-index: 0;
left: 50%;
margin-left: -5px;
top: 0
}s

.outer_face .marker {
background: gray;
width: 8px;
margin-left: -4px
}

.outer_face .marker.oneseven {
-moz-transform: rotate(30deg);
-ms-transform: rotate(30deg);
-webkit-transform: rotate(30deg);
transform: rotate(30deg)
}

.outer_face .marker.twoeight {
-moz-transform: rotate(60deg);
-ms-transform: rotate(60deg);
-webkit-transform: rotate(60deg);
transform: rotate(60deg)
}

.outer_face .marker.fourten {
-moz-transform: rotate(120deg);
-ms-transform: rotate(120deg);
-webkit-transform: rotate(120deg);
transform: rotate(120deg)
}

.outer_face .marker.fiveeleven {
-moz-transform: rotate(150deg);
-ms-transform: rotate(150deg);
-webkit-transform: rotate(150deg);
transform: rotate(150deg)
}

HTML Markup:

<div class="outer_face">
<div class="marker oneseven"></div>
<div class="marker twoeight"></div>
<div class="marker fourten"></div>
<div class="marker fiveeleven"></div>
</div>

Result:

At this point we have something resembling a wheel with its spokes exposed, which would bring this article to a satisfactory conclusion if we were building carriage wheels. But lets read on.

The inner clock face

Now we begin work on the inner clock face. It will be a smaller circular element with class .inner_face and centered inside the outer face, with a black "dot" in the middle where the hands will later emanate from:

CSS Code:

.inner_face {
position: relative;
width: 88%;
height: 88%;
background: white;
-moz-border-radius: 1000px;
-webkit-border-radius: 1000px;
border-radius: 1000px;
z-index: 1000;
left: 6%; /* center DIV horizontally (100%-88% / 2) */
top: 6% /* center DIV vertically (100%-88% / 2) */
}

.inner_face::before {
/* clock center dot small */
content: "";
width: 18px;
height: 18px;
border-radius: 18px;
margin-left: -9px;
margin-top: -9px;
background: black;
position: absolute;
top: 50%;
left: 50%;
box-shadow: 0 0 30px gray
}

.inner_face::after {
content: "QUARTZ";
position: absolute;
width: 100%;
font: normal 0.8em Arial;
color: gray;
text-align: center;
top: 85%
}

HTML Markup:

<div class="outer_face">

<div class="marker oneseven"></div>
<div class="marker twoeight"></div>
<div class="marker fourten"></div>
<div class="marker fiveeleven"></div>


<div class="inner_face">

</div>



</div>

Result:

The dimensions of the inner clock face as you can see is 88% of the outer's; to center it within the later, we set its left and top position to 6% each, or (100%-88%)/2.

Once again pseudo elements are used to augment reality with two additional objects- a big black "dot" inside the inner clock face, plus a "QUARZ" text.

The clock hands

Last but certainly not least, we arrive at the clock hands, where we will reserve our best workmanship for! They will be made up of three DIVs of varying heights and width (thickness), each hand positioned so its bottom is anchored at the center of the clock:

CSS Code:

.hand, .hand.hour {
position: absolute;
width: 4px; /* default width of hands/ hour hand */
height: 30%; /* default height of hands/ hour hand */
top: 20%; /* offset hand vertically by 20% so its bottom is at center of clock */
left: 50%; /* offset hand horiz by 50% so its left is at center of clock */
margin-left: -2px; /* Account for hand width to center hand horizonally */
background: black;
-moz-transform: rotate(0deg);
-ms-transform: rotate(0deg);
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
-moz-transform-origin: bottom; /* Set transform origin to center bottom */
-ms-transform-origin: bottom;
-webkit-transform-origin: bottom;
transform-origin: bottom;
z-index: -1;
-moz-box-shadow: 0 0 3px gray;
-webkit-box-shadow: 0 0 3px gray;
box-shadow: 0 0 3px gray
}

.hand.minute { /* minute hand (2nd longest) */
height: 45%;
top: 5%;
width: 6px;
margin-left: -3px;
}

.hand.second { /* second hand (longest) */
height: 50%;
width: 2px;
margin-left: -1px;
top: 0;
background: red
}

HTML Markup:

<div class="outer_face">

<div class="marker oneseven"></div>
<div class="marker twoeight"></div>
<div class="marker fourten"></div>
<div class="marker fiveeleven"></div>


<div class="inner_face">

<div class="hand hour"></div>
<div class="hand minute"></div>
<div class="hand second"></div>


</div>


</div>

Result:

See/Edit Code

Lets break down some of the ancient watch making techniques employed here with the hands:

  • We set the default height of the hands- and also specifically the hour hand - to be 30% of the parent container's height. To position the hand dead center inside the clock face, we set its top position to be 20% (30% + 20% = 50%), and its left to 50%. To account for the 4px width of the hand creating a slight misalignment horizontally, we set its margin-left to -2px so it's perfectly centered again. We apply the same principles to the minute and second hands.
  • We set the transform-origin property to "bottom" (shorthand value for "center bottom"). This is so that when we rotate the hands, the pivot point is at the bottom of each hand where it meets the center point of the clock. Now that's some precision work!

Note: I've rotated the minute and second hands in the demo you see above (by modifying their transform: rotate(0deg) property) so each hand is visible (instead of overlapping one another). This is not reflected in the code to the left.

We've managed to put together an entire clock interface using pure CSS, which deserves a good pat on the back. However, inevitably, we are confronted with both the urge and requests on how to make it into a actual functional clock. And that's what we'll look at next, with help from a bit of JavaScript.

Bringing our CSS clock to life using JavaScript's requestAnimationFrame() method