You asked for it, and you got it. Several people asked me after the digital clock demo at jQCon if I thought it was possible to create an analog theme for the clock using only CSS. I had wondered this as well, but came to the conclusion that it was impossible, given the current HTML structure. So, that’s the answer I gave at the conference.
The very next day, I thought of a way to do it, so here’s an analog theme (source on github) for the digital clock done entirely in CSS.
First, note a couple things:
- The first hour, minute, and second digits are hidden.
- The second hour, minute, and second digits have been styled to look like analog clock hands—i.e. long thin rectangles, and have been anchored at the center of the analog clock face.
The key is the use (in fact, abuse, read on!) of the immediate sibling selector to rotate the clock hands to the correct position, using CSS3 transforms. For example, the line above says when the first hour digit is a “0”, and the second hour digit is also a “1” rotate the second digit (remember, it’s been styled to look like an analog clock hand) by 30°. Each hour represents 30° (360 / 12 = 30), so looking at the rest of the hour selectors, you can see how it works.
Similarly, the minutes and seconds are rotated using the immediate sibling selector. The only difference is the degrees, since 360 / 60 = 6° per minute/second.
Ok, neat, it works, but …
How not to build an analog clock
At this point, you’re probably saying to yourself (and if you’re not, you should be) “This is a terrible way to build an analog clock”. You are absolutely right. There are many problems with this, and I’d even go so far as to call the whole thing an antipattern.
From the HTML element hierarchy, to the CSS classnames, to the time computations, most everything tailored to represent an LED-based digital clock.
However, the point of this little exercise was not to find the best way to engineer an analog clock. The point was to answer the question “could it be done?”. But maybe we can learn something about OOCSS anyway.
There is something to be learned here
As the saying goes, “when all you have is a hammer, everything looks like a nail”, and in this case of trying to use only OOCSS to transform the digital clock into analog, there’s certainly some overly-ambitious hammering going on.
If the HTML is poorly-suited to the task at hand, trying to apply OOCSS on top of it will probably just make things worse. In fact, in this example, the HTML and CSS are fighting each other rather than working together.
OOCSS is not just about CSS, it’s about identifying objects in the view first (as John said in our presentation, “ignore the HTML!”), and then structuring HTML and CSS around the containers, content, identity, and states of those objects.
The fact is that the objects in this clock are LED digits, not analog clock hands, and some mildly clever CSS doesn’t change that. Conversely, this bastardization means that the hands of the analog clock have the classes “display-area” and “digit”, as well as “d0” – “d9”, none of which seem like logical choices for the hands of an analog clock!
Antipattern: In any reasonably complex system, writing HTML and CSS first is an antipattern.
What to do?: Break out the wireframes, gather around the whiteboard, and start identifying objects! List their states. Talk about ways to translate them into well-structured HTML containers and content.
One of the results of this object/HTML/CSS mismatch is state explosion, aka combinatorial explosion. There are 72 CSS rules needed just to rotate the clock hands, whereas there are only 10 rules for the original digital clock LED digits. That certainly qualifies as state explosion, and just looking at the analog rules should give you an uneasy feeling that something is wrong.
In fact, modeling any analog clock, not just this bad example of an analog clock, as discrete OOCSS states seems wrong. Consider also the progress bar example John gave during our talk. Progress bars, in most cases, represent a continuous function rather than a discrete function, and therefore can require an infinite number of states to model their possible values—e.g. 30%, 30.1%, 30.15%, and so on.
Antipattern: Trying to model continuous values with OOCSS state is an antipattern.
What to do?: Use a mechanism better suited to continuous values/functions, such as a vector library, or yes, even direct (but well abstracted!) style manipulation.
One other thing that should be bothering you about this analog clock is that nearly half the HTML elements are permanently hidden by the analog theme’s CSS. This can be an indication that you’ve misidentified the view objects. In this case, I think it’s pretty obvious that the objects I originally identified when creating the digital clock, that is, active digits composed of lit or unlit LED bars, simply are not present in the analog clock.
Antipattern: Having sections of permanently hidden HTML is an antipattern.
What to do?: Review your objects and wireframes. You may have misidentified some objects, or your application may have changed significantly enough over time that the objects you had originally, and correctly, identified are no longer present. Either way, it’s time to revisit the wireframes and refactor.
Trying to shoehorn an analog display into the digital clock was fun, but more importantly, I think it helped to identify some OOCSS antipatterns. Hopefully these will help us all avoid some pitfalls!