
A couple of years ago, when CSS trigonometry functions became baseline, Iwrote an article about them. One of the examples I did, was a CSS-only analog clock:
Since then, CSS has introduced abunch of new features — one beingoffset-path
, which is perfect for creating indices on a clock (I sound like an horology expert, but I Googled that).
So, without further ado, let's expand my old example with some more, cool features! We'll wrap it within a Web Component for easier customization, but you can stick with CSS-only, if you want.
First, we set up a simple grid, divided into 3 rows:
:host{aspect-ratio:1;background:#f2f2f2;border-radius:50%;display:grid;grid-template-rows:repeat(3,1fr);}
The indices are a bunch of<li>
elements within a<ul>
, usingoffset-distance / path
to place them around the circle:
li{display:inline-block;list-style:none;offset-distance:var(--_d);offset-path:content-box;width:fit-content;}
Each<li>
has a degree (actually a percentage), defined in the--_d
custom property:
<listyle="--_d:0%">|</li>
This gets us:
By default,
offset-rotate
automatically rotates elements to follow the path direction. This behavior is exactly what we need for the indices, so we don't need to set any additional rotation.
Now, for the numerals, we'll also use<li>
, but this time within anordered list,<ol>
:
<ol><listyle="--_d:300deg">1</li></ol>
We'll usecos()
andsin()
to place the numerals, like in my original example.
li{--_r:calc((100%-15cqi)/2);--_x:calc(var(--_r)+(var(--_r)*cos(var(--_d))));--_y:calc(var(--_r)+(var(--_r)*sin(var(--_d))));aspect-ratio:1;display:grid;left:var(--_x);place-content:center;position:absolute;top:var(--_y);width:15cqi;}
And we get:
Now, let's create the markup for the hands and date. The cap will be added as a pseudo-element. I had a hard time trying to wrap my head around what good, semantic markup would be here? I gave up, and just used a bunch of<div>
s 😄
<navpart="hands"><divpart="seconds"></div><divpart="minutes"></div><divpart="hours"></div><timepart="date"></time></nav>
We position the<nav>
in the middle row of the main grid, and create a 3-column grid:
:host::part(hands){display:grid;grid-area:2/1/3/1;grid-template-columns:repeat(3,1fr);}
Finally, we place the label at the top center of the last row of the main grid:
Animating the hands
To animate the hands, we just need a single animation:
@keyframesturn{to{transform:rotate(1turn);}}
However, it needs to becalled in 3 very distinct ways:
:host::part(hours){animation:turn43200slinearinfinite;animation-delay:var(--_dh,0ms);}:host::part(minutes){animation:turn3600ssteps(60,end)infinite;animation-delay:var(--_dm,0ms);}:host::part(seconds){animation:turn60slinearinfinite;animation-delay:var(--_ds,0ms);}
And that's it! ... if you don't mind the clock always starting at noon!
To initialize the clock with theactual time, we need to update the delay properties:--_dh
,--_dm
and--_ds
— and for that, we need a small snippet of #"http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24">
Variants
Styling variants is dead simple (see the final demo at the end of the article).
How about a SAIKO:
Or a ROBEX (sorry for my unimaginative names!):
... or how about somereally colorful examples:
The latter can, of course, be done by adding the labels manually, but if we wrap it in a web component, it becomes a bit easier to maintain:
<analog-clocklabel="မြန်မာ"system="mymr"timezone="+6.5"class="burmese"indicesmarker="•"></analog-clock><analog-clocklabel="ประเทศไทย"system="thai"timezone="+7"class="thai"indicesmarker="·"marker-hour="•"></analog-clock><analog-clocklabel="अरुणाचल"system="wcho"timezone="+5.5"class="indian"></analog-clock>
Let's look into that.
Web Component
Wrapping the code in a<analog-clock>
web component offers a simple way to add an analog clock to your web projects. It's customizable through various attributes and CSS custom properties.
Installation & Usage
Install via npm:
npm i @browser.style/analog-clock
Or use directly via CDN:
<scriptsrc="https://browser.style/ui/analog-clock/index.js"type="module"></script>
Then, simply add the component to your HTML:
<analog-clock></analog-clock>
Basic Examples
Here are some common use cases:
<!-- Simple clock for New York time --><analog-clocklabel="New York"timezone="-4"></analog-clock><!-- Clock with date display and minute markers --><analog-clockindicesdate="day month"label="Current Time"></analog-clock><!-- Clock with custom markers and Roman numerals --><analog-clockindices="hours"system="roman"marker="•"marker-hour="●"label="Roma"></analog-clock>
Styling Examples
The component can be styled using CSS custom properties:
/* Gold luxury theme */.luxury{--analog-clock-bg:radial-gradient(circleat50%50%,#f4e5c350%,#e2ca7d51%,#5c4d2895%);--analog-clock-c:#2a2317;--analog-clock-ff:"Didot",serif;--analog-clock-second:#8b0000;--analog-clock-cap:#403428;}/* Minimalist theme */.minimal{--analog-clock-bg:#fff;--analog-clock-c:#333;--analog-clock-indices-c:#ddd;--analog-clock-second:#ff4444;--analog-clock-cap-sz:4cqi;}
Number Systems
Thesystem
attribute supports variousnumber systems, as we saw in the colorful examples earlier:
<analog-clocksystem="mymr"></analog-clock><analog-clocksystem="thai"></analog-clock>
Timezone Support
You can display different timezones using thetimezone
attribute:
<analog-clocklabel="New York"timezone="-4"></analog-clock><analog-clocklabel="London"timezone="0"></analog-clock><analog-clocklabel="Tokyo"timezone="+9"></analog-clock><analog-clocklabel="Mumbai"timezone="+5.5"></analog-clock>
Attributes
date
: Display date. Values: "day", "month", "year" or any combinationindices
: Show tick marks. Values: empty (60 marks) or "hours" (12 marks)label
: Text label below the clockmarker
: Character used for indices (default: "|")marker-hour
: Character used for hour indices (defaults to marker value)numerals
: Number of numerals to display (1-12, default: 12)steps
: Use stepping animation for seconds handsystem
: Number system. Values: "roman", "romanlow", or any validIntl numberingSystemtimezone
: UTC offset in hours (e.g., "-4", "+1", "+5.5")
Demo
Here's a Codepen with all the clocks and watches, we've coded:
Now go teach kids how to read an analog clock!