Ognjen Regoje bio photo

Ognjen Regoje
But you can call me Oggy


I make things that run on the web (mostly).
More ABOUT me and my PROJECTS.

me@ognjen.io LinkedIn

Accessing time in CSS without JavaScript

#nginx #trivia

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:

<style>
  ::root{
    --currentHour: <%= Time.now.hour%>;
    --currentMinutes: <%= Time.now.min%>;
  }
</style>

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 included into the CSS.

# app/assets/stylesheets/time.css.erb

::root{
  --currentHour: <%= Time.now.hour%>;
  --currentMinutes: <%= Time.now.min%>;
}

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>:

<style>
  ::root{
    <HERE>
  }
</style>

<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:

::root{
  <HERE>
}

Nginx config

There are two steps:

  1. Extract the hour and minute
  2. 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:

  1. sub_filter_types *; is required in order to replace content in text/css. Explicitly adding just that didn’t work for me.
  2. I have an older version of nginx that doesn’t allow multiple sub_filters.

This results in the html having the following content:

<style>
::root{
  --currentHour: 22;
  --currentMinute: 38;
}
</style>

or a css file that looks like this https://ognjen.io/css-time.css.

Downsides

There are a couple of downsides with these approaches:

  1. 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.
  2. It doesn’t take into account the users’ timezone. The time returned is the timezone that’s set up on the server.
  3. 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.