A few days ago there was a post on HackerNews about a css-only clock. It was quite interesting and I learned a few things about CSS. But there was a downside:
It’s simply not possible to access the local time with HTML and CSS to automatically set our clock.
That statement nerd-sniped me and over the past 3 days I tried to think of ways to do that.
I came up with two approaches: one using a server-side language and the other using nginx config. Each of those solutions can be implemented in two ways: inject the CSS into the HTML, or generate a separate CSS stylesheet.
Server-side language
I didn’t consider using a server-side language “in the spirit” of what I was trying to solve.
Nevertheless, it’s the obvious solution.
Inject the CSS
If you have a server-side language running, you can simply inject a <style>
tag with the variables into the <head>
.
In rails, that would look like this:
Now you have the variables with the current time in CSS.
Generate a new stylesheet
The other approach is to use a preprocessor such as erb
to generate a new stylesheet that can be include
d into the CSS.
This would then generate the stylesheet with the variables at example.com/assets/time.css
.
Nginx
By using the nginx config it means a server-side language doesn’t have to be set up. This approach can then be used for static sites.
Injecting content in nginx can be done with the ngx_http_sub_module
. I believe it’s one of the default modules that nginx is compiled with.
Injecting it into the page
To inject the current time directly into the head the following needs to be added to the <head>
:
<HERE>
is just the placeholder the nginx will overwrite.
Generating a new stylesheet
In order to generate a new stylesheet, you can just set up a separate static CSS file with similar content:
Nginx config
There are two steps:
- Extract the hour and minute
- Replace the placeholder
To extract the hour and the minute we can use the ngx_http_map_module
with the $time_iso8601
like so:
map $time_iso8601 $hour {
default "";
"~.{11}(?<hour_r>.{2})" $hour_r;
}
map $time_iso8601 $min {
default "";
"~.{14}(?<min_r>.{2})" $min_r;
}
This makes $hour
and $min
variables accessible. Note that this needs to be placed outside of the server
directive.
Then, to replace the placeholder using sub_filter
:
sub_filter_types *;
sub_filter '<HERE>' "--currentHour: ${hour};\n --currentMinute: ${min};";
Two things to note:
sub_filter_types *;
is required in order to replace content intext/css
. Explicitly adding just that didn’t work for me.- I have an older version of nginx that doesn’t allow multiple
sub_filters
.
This results in the html having the following content:
or a css file that looks like this https://ognjen.io/css-time.css.
Downsides
There are a couple of downsides with these approaches:
- You can only briefly cache the CSS. If the page has the analog clock, it would be fine to cache it up to a few minutes. For the digital, it would immediately be inaccurate.
- It doesn’t take into account the users’ timezone. The time returned is the timezone that’s set up on the server.
- It will be off by however long it takes to process the rest of the page. And slightly more if the external file is used.
Still, an interesting exercise, but I’m disappointed I couldn’t come up with anything more creative.
I think there is probably an existing CSS file out there somewhere that contains a timestamp that could be used instead of having to modify the server config.