How to make a CSS3 and JavaScript analog clock

1 2 3 4 5 6 7 8 9 10 11 12

This is a "How To" guiding the ones that are interesting in the creation of an analog clock, similar to the one on the left, with CSS3 and jQuery. First of all, I would like to say that this is in no way a useful method of placing an analog clock on your website. It's only meant as an experiment with CSS3. If you do want an analog clock, you could probably use Flash or an image based JavaScript clock.

I used as a reference the Windows 7 sidebar clock, but it's not a perfect copy. I could have added a lot more details to make it look closer to the real one, but it would have made it a lot more difficult to implement.

Internet Explorer (any version) has not been targeted for this experiment due to it's lack of CSS3 support, so don't be surprised if it doesn't work. Making it functional in Internet Explorer would have over-complicated the JavaScript, attempting to make it work with IE's filters. With that out of the way, let's get started.

The HTML

All the styles will be set in the stylesheet later on, so don't bother with it for now. You can see there are a lot of tags that make the clock very heavy. That's why this is not a good way to create an analog clock for use on your website.

The div#clock will hold everything together. The div.hour-marks holds all the numbers and hour marks. We could have done without it, but it's good to have some sort of container just for those. The next 3 divs are the clock handles (hours, minutes, seconds). The last span is the round circle in the center of the clock.

<div id="clock">
 <div class="back"></div>

 <div class="hour-marks">
  <span class="mark m1"></span>
  <span class="mark m2"></span>
  <span class="mark m3"></span>
  <span class="mark m4"></span>
  <span class="mark m5"></span>
  <span class="mark m6"></span>
  <span class="mark m7"></span>
  <span class="mark m8"></span>
  <span class="mark m9"></span>
  <span class="mark m10"></span>
  <span class="mark m11"></span>
  <span class="mark m12"></span>
  <span class="hour h1">1</span>
  <span class="hour h2">2</span>
  <span class="hour h3">3</span>
  <span class="hour h4">4</span>
  <span class="hour h5">5</span>
  <span class="hour h6">6</span>
  <span class="hour h7">7</span>
  <span class="hour h8">8</span>
  <span class="hour h9">9</span>
  <span class="hour h10">10</span>
  <span class="hour h11">11</span>
  <span class="hour h12">12</span>
 </div>

 <div class="hour-handle"><span class="handle"></span></div>
 <div class="minute-handle"><span class="handle"></span></div>
 <div class="seconds-handle"><span class="handle"></span></div>

 <span class="center"><span></span></span>
</div>

The CSS

Oh man, this is a lot of CSS for something so trivial. Let's try to lay it out.

We're going to start with our clock div, or better yet, div#clock and standard CSS. I gave it a width and height of 200px, but you can change that to whatever you like. Take note however that you will need to modify the position of the dandles and marks also. Actually you will need to modify pretty much everything. Position:relative will allow us to position everything else inside it with position:absolute.

div#clock{
 width: 200px;
 height: 200px;
 position: relative;
 }

Now for the fun part. We don't need a rectangular clock. That would look unattractive. Instead we will make it round with the border-radius feature in CSS3. This allows you to make the corners of a box element round. But if you take it all the way up, that box will turn into a circle. For that you need to specify at least half of the width or height, in this case 100px. This works best for squares as it creates a perfect circle. The first rule is the standard CSS3 declaration. This is used by Opera also. The next one is the proprietary declaration for Gecko based browsers (Firefox, Flock) and the last, but not least, is the proprietary declaration for Webkit based browsers (Safari, Chrome).

div#clock{
 border-radius: 100px;
 -moz-border-radius: 100px;
 -webkit-border-radius: 100px;
 }

To make it look a little nicer, we'll add a gradient for the background. First the standard CSS background. Opera will use this as it does not have a linear gradient method implemented yet. The second one is for Mozilla and the third for Webkit.

div#clock{
 background: #3a3937;
 background: -moz-linear-gradient(right 240deg, #3a3937, #1b1d1c);
 background: -webkit-gradient(linear, right top, left bottom, from(#3a3937), to(#1b1d1c));
 }

Now we're adding a drop-shadow around our clock, just to make it stand out a bit. This can be changed to look like a halo on a dark background. The color can be changed and also the size of the shadow.

div#clock{
 box-shadow: 0 0 10px #666;
 -moz-box-shadow: 0 0 10px #666;
 -webkit-box-shadow: 0 0 10px #666;
 }

These methods will be used throughout the stylesheet, so there's no point in describing each of them. Here's some more of the CSS:

div#clock div.back{
 width: 184px;
 height: 184px;
 position: absolute;
 top: 8px;
 left: 8px;

 border-radius: 92px;
 -moz-border-radius: 92px;
 -webkit-border-radius: 92px;

 background: #fffdf4;
 background: -moz-linear-gradient(top 260deg, #fffdf4, #e2dfce);
 background: -webkit-gradient(linear, center top, center bottom, from(#fffdf4), to(#e2dfce));

 box-shadow: inset 0 0 5px #000;
 -moz-box-shadow: inset 0 0 9px #000;
 -webkit-box-shadow: inset 0 0 9px #000;
}
div#clock div.hour-marks span{
 display: block;
 position: absolute;
}
div#clock div.hour-marks span.mark{ background: #333; width: 8px; height: 3px;}
div#clock div.hour-marks span.hour{
 font-size: 14px;
 text-align: center;
 font-family: Helvetica, Arial, sans-serif;
 color: #444;
}
div#clock span.center,div#clock span.center span{
 position: absolute;
 top: 95px;
 left: 95px;
 background: #333;
 width: 10px;
 height: 10px;

 border-radius: 5px;
 -moz-border-radius: 5px;
 -webkit-border-radius: 5px;
}
div#clock span.center span{ width: 6px; height: 6px; top: 2px; left: 2px; background: #666;}

In order to rotate our hour marks so they face towards the center of the clock, we will be using -*-transform: rotate(). This rotates the element by a specific number of degrees. Since we have 12 hours and a circle has 360 degrees, each mark will be rotated at 30 degree intervals. The positioning of these marks are more of a guess. I didn't feel like finding a method of calculating where each of them should go on a circle. So I positioned them by eye.

div#clock div.hour-marks span.m1 {-moz-transform: rotate(-60deg); -webkit-transform: rotate(-60deg); -o-transform: rotate(-60deg); top:  22px; right:  52px;}
div#clock div.hour-marks span.m2 {-moz-transform: rotate(-30deg); -webkit-transform: rotate(-30deg); -o-transform: rotate(-30deg); top:  52px; right:  22px;}
div#clock div.hour-marks span.m3 {-moz-transform: rotate(0deg);   -webkit-transform: rotate(0deg);   -o-transform: rotate(0deg);   top: 100px; right:   8px;}
div#clock div.hour-marks span.m4 {-moz-transform: rotate(30deg);  -webkit-transform: rotate(30deg);  -o-transform: rotate(30deg);  top: 146px; right:  22px;}
div#clock div.hour-marks span.m5 {-moz-transform: rotate(60deg);  -webkit-transform: rotate(60deg);  -o-transform: rotate(60deg);  top: 176px; right:  54px;}
div#clock div.hour-marks span.m6 {-moz-transform: rotate(90deg);  -webkit-transform: rotate(90deg);  -o-transform: rotate(90deg);  top: 188px; right:  95px;}
div#clock div.hour-marks span.m7 {-moz-transform: rotate(120deg); -webkit-transform: rotate(120deg); -o-transform: rotate(120deg); top: 176px; right: 140px;}
div#clock div.hour-marks span.m8 {-moz-transform: rotate(150deg); -webkit-transform: rotate(150deg); -o-transform: rotate(150deg); top: 146px; right: 170px;}
div#clock div.hour-marks span.m9 {-moz-transform: rotate(180deg); -webkit-transform: rotate(180deg); -o-transform: rotate(180deg); top: 100px; right: 184px;}
div#clock div.hour-marks span.m10{-moz-transform: rotate(210deg); -webkit-transform: rotate(210deg); -o-transform: rotate(210deg); top:  52px; right: 170px;}
div#clock div.hour-marks span.m11{-moz-transform: rotate(240deg); -webkit-transform: rotate(240deg); -o-transform: rotate(240deg); top:  22px; right: 140px;}
div#clock div.hour-marks span.m12{-moz-transform: rotate(270deg); -webkit-transform: rotate(270deg); -o-transform: rotate(270deg); top:  10px; right:  95px;}

div#clock div.hour-marks span.h1 { top:  27px; right:  62px;}
div#clock div.hour-marks span.h2 { top:  50px; right:  35px;}
div#clock div.hour-marks span.h3 { top:  90px; right:  20px; font-weight: bold; font-size: 16px}
div#clock div.hour-marks span.h4 { top: 130px; right:  35px;}
div#clock div.hour-marks span.h5 { top: 153px; right:  62px;}
div#clock div.hour-marks span.h6 { top: 163px; right:  95px; font-weight: bold;font-size: 16px}
div#clock div.hour-marks span.h7 { top: 153px; right: 132px;}
div#clock div.hour-marks span.h8 { top: 130px; right: 160px;}
div#clock div.hour-marks span.h9 { top:  90px; right: 170px; font-weight: bold;font-size: 16px}
div#clock div.hour-marks span.h10{ top:  50px; right: 160px;}
div#clock div.hour-marks span.h11{ top:  27px; right: 132px;}
div#clock div.hour-marks span.h12{ top:  20px; right: 100px; font-weight: bold;font-size: 16px}

The handles are also rotated using CSS3 and JavaScript. To make them rotate around a certain axis, we need to set the transformation origin. If we don't do that, they would just spin in one place, like the needle on a compass.

div#clock div.hour-handle, div#clock div.minute-handle, div#clock div.seconds-handle{
 height: 55px;
 width: 6px;
 position: absolute;
 top: 45px;
 left: 97px;
 overflow: hidden;

 -moz-transform-origin: 50% 100%;
 -webkit-transform-origin: 50% 100%;
 -o-transform-origin: 50% 100%;
}
div#clock div.minute-handle{ height: 80px; top: 20px; width: 4px}
div#clock div.seconds-handle{ height: 80px; top: 20px; left: 100px; width: 1px;}

The color for the handles is given by the inside span.handle. This will also suffer some transformations, in order to become pointed towards one end. In this case we will skew them along the Y axis by a huge amount. The number of degrees indicates the angle at which the skewing is make. This increases the height of the handle, but it's parent container has overflow:hidden and the only part that we can see is the one that we nee: a nice sharp clock handle.

div#clock div.hour-handle .handle, div#clock div.minute-handle .handle, div#clock div.seconds-handle .handle{
 display: block;
 background: #333;
 width: 180%;
 height: 100%;

 -moz-transform: skewY(-84deg);
 -webkit-transform: skewY(-84deg);
 -o-transform: skewY(-84deg);
}
div#clock div.minute-handle .handle{
 -moz-transform: skewY(-86deg); 
 -webkit-transform: skewY(-86deg); 
 -o-transform: skewY(-86deg);
}
div#clock div.seconds-handle .handle{
 -moz-transform: skewY(-88deg); 
 -webkit-transform: skewY(-88deg); 
 -o-transform: skewY(-88deg); 
 background: #f00;
}

That's about all the CSS required for our analog clock.

The JavaScript - jQuery, what else?

After loading the jQuery framework (1.4.2 in this case, but older versions might work) we can add the JavaScript required for the clock to function. It does this by repeating a function every second with the help of setInterval(). Read the comments below for more information. The position / angle of the handles is modified each time the function is run using the same -*-transform: rotate() method described in the CSS.

/* make sure everything is loaded */
$(document).ready(function(){
 /*
 *  this function is run every second to update the clock handles
 */
 function setClock(){
  /* get the handles of the clock */
  var hourHandle = $('div#clock div.hour-handle');
  var minuteHandle = $('div#clock div.minute-handle');
  var secondsHandle = $('div#clock div.seconds-handle');
  /* get the current date from te users computer */
  var now = new Date();

  /* 
  * determine the degrees we need to rotate each handle 
  * we need to create this variable as a string here, before applying it
  */
  var hourDeg = 'rotate(' + ((now.getHours() * 30) + Math.floor(now.getMinutes() / 2)) + 'deg)';
  var minDeg = 'rotate(' + (now.getMinutes() * 6) + 'deg)';
  var secDeg = 'rotate(' + (now.getSeconds() * 6) + 'deg)';

  /* change the CSS for each of the handles */
  hourHandle.css({
   '-moz-transform': hourDeg,
   '-webkit-transform': hourDeg,
   '-o-transform': hourDeg
  });
  minuteHandle.css({
   '-moz-transform': minDeg,
   '-webkit-transform': minDeg,
   '-o-transform': minDeg
  });
  secondsHandle.css({
   '-moz-transform': secDeg,
   '-webkit-transform': secDeg,
   '-o-transform': secDeg
  });
 }

 /* call the function every second */
 setInterval( setClock, 1000);
 });

This is all there is to it. A working example can be seen at the top of this page. I would like to repeat myself: this is not a good way to add an analog clock to your website. It's just a way of showing what can be done with CSS3. Here is the complete code used to make the clock.

The HTML

The CSS

The JavaScript

If you have any comments or questions, please let me know.

To validate or not to validate?

This post is intended to address the issue of HTML validation from my point of view. I will try to point out the advantages and the reasons a web page needs to validate (again from my point of view of course), but I will try to line out a few other opinions on the matter. Long story short, I will try to answer a simple question: "Should I care about validation or not ?"

What is HTML validation?

For those of you that still don't know what validation really is, here is the short explanation. Validation is the process by which an HTML or XHTML page, meaning it's source, is brought in accordance with world wide accepted standards. What this means is actually writing the source code of the page in such a manner that is respects all the guidelines implemented by the W3C (that's the World Wide Web Consortium). In order to make sure your web page is valid from an HTML or XHTML point of view, you can use a lot of different tools, but my favorite is the online one provided by W3C at http://validator.w3.org. Simply type in the URI of your page or copy/paste your code and hit the "Check" button. That's it.

Does it even matter?

Believe it or not, it does. There are people that say that validation is just something that developers thought up to brag about their skill. Others just don't care if it's not valid as long as it works. But here is where the funny thing comes in. For it to work it still needs to be coded after some specific guidelines, so unintentionally people do give a though to validation. After all, they do want their pages to look nice, don't they?

There are many situations when a page full of errors looks flawless, the layout is just the way it was planned. That doesn't mean it's right and that it doesn't need to be coded differently. It just means the browser is aware there are bad developers or that errors might slip by unnoticed and it just ignores the situations and renders things just like nothing happened, mostly "guessing" how things should have been.

So browsers fix things for me. Then why should I bother to validate?

The idea here is that browsers can't fix everything. There are situations when you do need a valid page and a well thought out structure. One of the most common situations i when you're using JavaScript, like the many wonderful frameworks available out there (jQuery, MooTools, Dojo to name just a few). These don't bother to check your code, they just use what they are given.

For example if you forget to close a paragraph (<p> tag), the browser will render just fine. When it finds the next paragraph tag or the next block element, it assumes you closed the previous one. But if you use some JavaScript code to work on your paragraph tags, let's say for example that you want to change their background when you hover over them, errors will appear. JavaScript will assume that the paragraph ends when it meets the closing tag (</p>), but that will cause the change to be applied to the next elements (maybe other paragraph or maybe lists), until that closing tag is met.

That's all? Closing tags?

No, that's not all. Because browsers continue to evolve and improve, a strict set of rules is required. And these rules must be respected by everyone, both website owners and browser developers. If they are ignored, progress will be just a long list of hacks and fixes from either side. Browser developer will try to support obsolete and sometimes wrong interpretations of standards just to please the users and not render the pages chaotically. And website owner and developers will try to work around browser incorrect renderings by various CSS, HTML or JavaScript hacks.


In closing, I would like to say that I try my best to make every page as valid from a W3 point of view as possible. There are situations when it's jut too much work and it's not worth rewriting the entire code just to shift a few pixels. But these days things are a lot more easy. Modern browsers even support standards not yet officially recognized in preparation for when they will be made generally accepted, like CSS3. The only thing developers need to do is focus on the new implementations (which are already pretty old by now) and leave behind the old hacks. And if a browser doesn't display the page the right way, it just means it's the browser's fault. Most likely it's an outdated version and the users should upgrade for their own sake.

On the same note, I would like to keep a moment of silence for the soon to be dead and hopefully forever forgotten, IE6. Your death's coming soon.

Source: StatCounter Global Stats - Browser Version Market Share

Social Share 1.5 - the new and improved social sharing Blogger widget

A new version of this widget has been released. You can find it here: Social Share 2.0

Having a little spare time and not being particularly happy with the first version of my "Social Share!" widget, I thought I can make a better one. And I believe I did. Introducing "Social Share 1.5" Blogger widget. It's not a great improvement when it comes to how it looks, but rather how it is generated.

The new generator uses jQuery to dynamically update the widget code and allows for a greater customization of the widget. You now have the possibility of choosing which links should appear in the widget. The order of the links can also be changed by drag & drop. You can also select from a range of color schemes for the icons.

From a design point of view, it's not at all brilliant. But the fact that the code can be changed easily allows a more flexible evolution of the widget, in the sens that future changes can be implemented with much more ease. So without any further ado, I invite you to use the generator bellow to generate your very own Social Share! 1.5 Blogger widget.

Appearence Below you can select the size of the widget and the icon set it will use. If you leave the sizes empty or auto, the widget will fill the entyre width of the container unless other CSS rules are applied to it by your template. px Auto

px Auto

Select items Select below the items you want to appear in your widget. Make sure you select at least one. An empty widget serves no purpose. You can also drag them to specify the order you would like them to have.

If you have already installed the previous version, make sure you remove it if you plan to use this one. I doesn't matter if you remove it before or after you install this version.

Please stay tuned for future releases. If you like this widget, all comments are welcomed and tune in soon for a new version. I plan to make the next one (maybe named "Social Share 1.6" as an external script. Hopefully it will allow dinamic updrade of the widget in the sense that you will not need to reinstall it manually. So make sure you come back soon.

PS: if you notice a long time has passed and no new version has been released, drop me a comment. I might have forgotten.

How to get Slimbox to work with Blogger

I planned to make this happen for some time now: getting Slimbox to work with Blogger's quirky platform. But the solutions I always come up with never seem to be any viable ones. But today I finally managed to make it work in a way that satisfies me. If you want to know how, read on.

Firstly, the problem is the incapability to host the files required for Slimbox on a server (unless you're running on your own server/hosting account and not the free *.blogspot.com). Slimbox provides a very nice way of presenting an image or even a collection of images. For those that do not know, Slimbox is a clone of the original Lightbox. I prefer Slimbox because it's more lightweight as file size and also file count. The original Lightbox was built using the Prototype framework, which I personally never liked, and Slimbox comes in two flavors: MooTools and jQuery. So if you use any of those, you are safe to go for Slimbox.

But the issue at hand is not the history or origins of Slimbox, but rather the way we need to go to implement it into Blogger. So let's get started.

  1. Include the jQuery framework in your Blogger layout's html. Because we can't just upload it, we'll use the one Google places at our disposal here. So just go like this in your <head> tag:
    <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'></script>
    I went here for version 1.3.2 but you can use whichever you'd like
  2. That's one down. Now we need to include the Slimbox files. Head on over and download them from digitalia.be. At the moment of this writing, the latest version is 2.03. Once downloaded unpack them somewhere on your hard drive where you can find them
  3. Uploading the files to our host is again an issue, as we don't have one. But Google yet again comes to our aid. Go to Google Sites and make a new website or use an existing site if you have one already. Having a Blogger account, I assume it shouldn't be too hard. On your Google Site click the "More Actions" button, go to Manage and click Attachments. Upload the slimbox2.js and image files you unpacked from the Slimbox archive. Don't upload slimbox2.css yet. You need to edit that first
  4. Open slimbox2.css with your favorite editor (please don't use Microsoft Word, I don't feel like explaining why it won't work if you have any problems) and edit the location of the image files. There are 4 of them, all of which you should have already uploaded to your Google site (closelabel.gif, loading.gif, nextlabel.gif and prevlabel.gif). Make sure you replace the url in the css file so they point to the location of your uploaded images. You can find out the location if you hover over the download link in your Google Sites Attachments section. You can skip the last part of the URL, after the file extension, the one with "?attredirects...". You don't need that. After you're done editing the slimbox2.css you can upload that one too
  5. Get back to editing your Blogger layout and add the JavaScript and CSS files from your Google site. It should look something like this:
    <link href='http://sites.google.com/site/your_site/slimbox2.css' rel='stylesheet' type='text/css'/>
    <script src='http://sites.google.com/site/your_site/slimbox2.js' type='text/javascript'></script>
    
    Make sure the HREF and SRC attributes point to your uploaded files.
  6. That' about it! Now all you have to do is place a rel="lightbox" (or rel="lightbox[some_name]" if you would like to group them) attribute on the links that point to an image and you want it/them to have Lightbox/Slimbox capabilities. More on how Slimbox works and available features can be found on the official website. For a working example you can check out this page. At the end you will find a gallery of just 5 images and you will be able to scroll through them left/right.

One issue I had was with the fact that the links to images uploaded through Blogger were not pointing to the image itself but rather to a Picasa page containing the image and the Lighbox was activated but no image was displayed. To fix this I had to right-click the image in that page (Picasa page) and click "View image" in Firefox. After that replace the SRC in the link tag with the one of the image. A little more work but it gets the things working.

That's all I went through to get Slimbox working on a Blogger blog. Hope anyone find this useful. If you have any questions/suggestions feel free to leave a comment.