Behind the scenes: CSS-only Clock

11 March 2013

I like to create little experiments on Codepen.io and I get a lot of questions about how I made certain elements or effects. In a series of "behind the scenes" I try to dissect a few interesting pens. In this first episode I'll show you how I made the CSS-only clock with the single element clock face.



Breaking it apart

We can break apart this pen in three parts: the clock face with it's complicated background rule, the clock hands and the CSS animations that power the clock hands. The clock face is one single element with a complex set of background rules that form the hour and minute marks. The hands are a simple set of three div's (one with an extra pseudo class to form the red circle). And the animations look difficult, but the concept is really simple.

The clock face

The clock face is made up by a background color, a few box shadows and a complex backgorund image definition. It looks really complex, but once you grasp the concept it is really simple. It just needs a lot of code.

Let me first explain the main styles of this element:

.clock {
  margin: 35px auto;
  width: 260px;
  height: 260px;
  border-radius: 50%;
  box-shadow:
    0 0 0 15px white,
    0 4px 15px 10px rgba(0,0,0,.6),
    0 5px 10px rgba(0,0,0,.3) inset;
}


The margin places the element in the middle of the page, 35 pixels from the top. We need this offset to get a little distance, but also to compensate for the box shadow which creates the white ring around the clock. The clock has a 260 pixel diameter, and the border-radius of 50% makes the element round, regardless of the size you enter.

The box shadow
Box shadows can be stacked as many as you like. First shadow is on top, last at the bottom, so "read" the from bottom to top. At first there's an inset shadow, 5 pixels down, sized 10 pixels with a semi-transparent black color. Then a 15 pixels shadow, with a threshold of 10 pixels, 4 pixels down, also with a semi-transparent black. At last there's a 15 pixels thick, white shadow with no offset and no size. The shorthand for box shadows reads like this:

3px 5px 10px 5px rgba(0,0,0,.3) inset;
x-axis (3px), y-axis 9 (5px), size (10px), threshold (5px), color (rgba(0,0,0,.3)), inset (optional)

Now on to the hardest part of this pen, the stacked background images that form the minute and hour marks on the clock face.

Stacked Background Images
Background images can be stacked this also goes for linear- and radial gradients. The complex background image in this clock can be cut in 5 parts (bottom up, like the box shadow):

1. Plain background color that forms the base background (#EEE)
2. 24 one-pixel lines that form the minute marks
3. one radial gradient overlay to cover all minute-mark-lines and leave the ends uncovered
4. 6 one-pixel lines that form the hour marks
5. one last radial gradient overlay to cover the hour marks, slightly smaller to make these lines longer

This image makes things more clear:

First there's this grey background, then a lot of lines, then the red radial gradient, then some more line and at last the orange overlay to cover everything again. This leaves us with the illusion of the short lines as minute and hour marks.

The gradient layers explained

background:
    radial-gradient(transparent 125px, #eee 125px),
    radial-gradient(#eee 110px, transparent 110px),
    linear-gradient(90deg, transparent 49.5%, black 50%, transparent 50%),
    radial-gradient(#eee 120px, transparent 120px),
    linear-gradient(96deg, transparent 49.5%, black 50%, transparent 50%),
    #eee;

This is the layered background with al repeating lines removed to clearify things. If you read it from the bottom upwards, you'll see the plain color first, that's easy. Then comes the first line:

linear-gradient(96deg, transparent 49.5%, black 50%, transparent 50%),

It starts transparent until 49,5% of the image, then from 49,5% to 50% it's black, and from 50% onwards it's transparent again. This creates a thin line in the middle of the transparent background. If you'd like thicker marks you'll need to make the black part bigger, for example from 49% to 51%, but remember to always have 50% as middle. For example if you'd want a really thick line of 10%, you'll need to define it from 45% to 55%, not from 40% to 50%, because the background images are rotated around there own centers.

The "96deg" definition rotates the "base line" of the image to 96 degrees. Every following line in the clock face is the same, but has another angle. The minute marks increase by 6 degrees, and the hour marks increase with 30% each.

Then the radial gradient:

radial-gradient(#eee 120px, transparent 120px),

This creates a radial gradient that's a solid color (#EEE) until 120px and fades from this color to transparent at 120 pixels. Since these numbers are the same, the "gradient" will be instant and thus create a solid circle with a 120 pixel diameter. If tou'd like the hour and minute marks to be bigger, you need to make this overlay circle smaller.

The clock hands

The clock hands are quite simple. Three regular divs with a default styling:

.hand {
  width: 5px;
  height: 100px;
  position: absolute;
  left: 50%;
  top: 50%;
  margin: -100px 0 0 -2.5px;
  background: black;
  transform-origin: 50% 100%;
}

The left: 50% rule places the element in the middle of the clock face, but it aligns it on the left edge of the div and thereby it's just off-centre. Therefor you need to set it back half it's own width and to do that we use the negative margin rule. The hand is 5 pixels wide, so it needs to be set back 2,5 pixels to be exactly aligned in the middle. And yes, you can use decimals in pixel definitions.

Every hand has a transform-origin set to 50% (x) 100% (y) to let the hands rotate around their own bottom centers. Default transform origin is 0% 0% for all elements and if you don't change that the hands will rotate around their left top corners.

Next are a few exceptions to make the hour hand thicker and the second hand thinner and with a little offset to create the "tail" of the red hand.

.hour {
  height: 75px;
  margin: -75px 0 0 -5px;
  width: 10px;
}
.seconds {
  width: 2px;
  background-color: red;
  height: 150px;
  margin-left: -1px;
  transform-origin: 50% 100px;
}

At last there's a pseudo class on the seconds hand to create the red circle in the middle:

.seconds:after {
  content: '';
  position: absolute;
  bottom: 35px;
  left: -14px;
  width: 30px;
  height: 30px;
  background: red;
  border-radius: 50%;
}

The seconds hand has an offset of 35 pixels, so this circle had to be set back 35 pixels to be in the center of the rotation point again.

The CSS keyframe animations

This is where the CSS only claim gets a little doubtful. This pen uses a remote PHP file that sets the keyframe animations to match the current time. It is still 100% CSS, but it's generated by a PHP file and therefor not 100% css only. Anyhow, that's not really important... Let me explain how this works:

Every hand in the clock has an animation set that rotates the element 360 degrees in a certain timespan. The second hand makes a full rotation in 60 seconds, the minute hands fully rotates in an hour (3600 seconds) and the hour hand rotates in 12 hours (43.200 seconds). The animations are the same, the only difference for each hand is the duration of the animation.

.hour {
  animation: arr1 43200s linear infinite;
}
.minute {
  animation: arr2 3600s linear infinite;
}
.seconds {
  animation: arr3 60s linear infinite;
}

arr1, arr2 and arr3 are the names of the keyframe animations (set in the remote PHP file) and the rest is the time (43200s), animation easing (no easing: linear) and the number of times the animation should be repeated (infinite).

The animations in the remote PHP file look like this:

@-webkit-keyframes arr1 {
  from {-webkit-transform: rotate(318deg)}
  to {-webkit-transform: rotate(678deg)}
}

If you want a clock hand to start at the top at 12 o'clock and make a full circle, you'd start the animation at 0 degrees and rotate to 360 degrees. But since you want to start the clock at the current time, you need to skip forward and start the rotation at the current time, converted into the correct amount of degrees and let it run to that number, plus 360 degrees, to let it make a full turn. There is no limit in the amount of degrees you enter in a transform-rotate rule.

Alternative solution
Another solution for this time-setting-issue would be to define one keyframe animation that starts at 0 degrees and ends at 360 degrees and set a negative animation-delay for each clock hand. The animation-delay function can be used to delay an animation, but also to skip ahead to a certain point in an animation. If you set an animation delay of 2 seconds to a 10 second animation, it will start after 2 seconds and end after 12 seconds. If you set an animation delay of -4 seconds on the same animation, it will start immediately, fast-forwarded to the 4 second keyframe and stop after 6 seconds. This trick is very useful in situations like this.
You must have JavaScript enabled to use this form!

Leave a comment!

  1. Your mail is safe with me. It's only only used to display your Gravatar image!

4 comments

  1. Gravatar

    Dave

    29 June 2015

    Thanks

  2. Gravatar

    Dave

    29 June 2015

    Thanks

  3. Gravatar

    G4

    04 May 2013

    http://rassadin.github.io/css3-experiments/wall-clock/
    is better done

  4. Gravatar

    Justus

    10 April 2013

    wow! Cool work. I really like it, thank you.