DEV Community

Siddharth
Siddharth

Posted on • Originally published at blog.siddu.tech on

A PURE HTML/CSS functional calendar?! 😲🤯 (+CSS APIs and all the fun things we can do with them)

Yesterday I went back to my frontend experiments after a load of JavaScript work. I experimented with the CSS custom property toggle trick (more on that later) which can allow me to have if/else logic in CSS, and with a bit of experimenting I made this CSS only calendar!

(It's embedded in a CodePen, if you wanna see the real thing go over to https://experiments.siddu.tech/css-api/)

It shows the current month, year, day and date completely in CSS! And it also updates (check it tomorrow and see!). It can even handle leap years! 🤯

Before I tell you what's really happening, go over to the site and inspect to see what you can find. Once you are done with your experimenting, scroll.


Spoilers in:

3

2

1


Alright, here's how it works: I'm including an external stylesheet with the data. The data is generated by a GitHub action which runs a Deno script which creates a CSS file with the data in custom properties (see below).

(Yeah I know I kinda lied, but not really because the site is still 0 JS 😋)

This opens up a whole new door of CSS APIs which can be used to do stuff like change color themes based on the season (spooky theme in October, grass theme in March, you get the idea), or maybe even expose content with content: var(--content)!

That explanation too quick for you? Here's some more detail on how I did it.

Now that we have the data in CSS custom properties, we need to somehow change the data into content in the HTML. To understand how I did it, it will be nice to take a look at the CSS which was generated.

:root {
    --year: '2021';
    --november: ;
    --monday: ;
    --day-1: ;

}
Enter fullscreen mode Exit fullscreen mode

This is the CSS which was generated for the day I'm writing (Nov 1 2021, Monday)

With this CSS we can easily embed the year with :after, but the other properties look strange. Those properties are totally valid according to spec (the properties contain a value which is " ") and we can use them to make if else logic.

Here's a simpler CodePen showing how the thing works:

(Open the pen in a new tab and resize to see the full effect)

Let me explain:

  • There is a breakpoint setup here at 350px. This is where the variable --color changes from initial to an empty space.
  • When the browser window is wider than 350px, the value of --color is initial
    • That makes the variable --color-when-small contain the value initial red which is invalid.
    • So when we actually set the color and call that variable like background-color: var(--color-when-small, var(--color-when-big));, the second value (the fallback) is used because the first is invalid.
  • When the browser window is narrower than 350px, the value of --color is a space.
    • That makes the variable --color-when-small contain the value (space)red, which is valid
    • So when we actually set the color and call that variable like background-color: var(--color-when-small, var(--color-when-big));, the first value is used

So, now we can flip the color between two values by changing a placeholder variable. I hope that clicks for you.

I used the same trick in this calendar too. I first added a bunch of spans with all my months:

<span class='january'>January</span>
<span class='february'>February</span>
<span class='march'>March</span>
<span class='april'>April</span>
<!-- ... --->

Enter fullscreen mode Exit fullscreen mode

Then in the CSS I set the display value in the same way as above:

.january {
  --display-when-correct: var(--january) block;

  display: var(--display-when-correct, none);
}

.february {
  --display-when-correct: var(--february) block;

  display: var(--display-when-correct, none);
}
/* ... */

Enter fullscreen mode Exit fullscreen mode

So when --january is set to " ", --display-when-correct becomes (space)block, which is valid. And since --february, --march all don't exist, they become invalid (and so they are display: none.

If this trick seems confusing (or helpful) then I'll write another detailed post on it!

I did a similar thing for highlighting days and dates, here's how:

.monday {
    --bg: var(--monday) #1abc9c;
    --fg: var(--monday) white;
    --space: var(--monday) 5px;

    background: var(--bg, initial);
    border-radius: var(--space, initial);
    padding: var(--space, initial);
    color: var(--fg, initial) !important;
}

Enter fullscreen mode Exit fullscreen mode

It's the same old trick, just a few extra things to add.

And also there is a fix for leap years and some other minimal stuff which you can learn by reading the source

CSS APIs

We can apply the same trick I used to generate this data to make more stuff like a fun fact generator, or for having different color themes like a seasonal color palette which changes (I think GitHub uses this trick for their Halloween theme, if not they should 😉)

Also, if you are up for a challenge, try implementing what I just used to make a site with a different color palette every season and share it over here! It's going to be much easier than building a calendar, trust me!

If you think this can be helpful in the real world, spread the word so that more people can use it in real world situations.

If you love my content, follow me on twitter or over here to stay updated on my blog posts!

Latest comments (4)

Collapse
 
afif profile image
Temani Afif

A small rectification about the initial value: When you have --color:initial it doesn't mean that --color-when-small contain the value initial red. initial is not stored, it's applied to --color and it has a specific meaning described here: w3.org/TR/css-variables-1/#invalid...

You can read

When a custom property has its initial value, var() functions cannot use it for substitution. Attempting to do so makes the declaration invalid at computed-value time,

So the fact that your variable has initial will make the whole property invalid at computed time. It's not the value ( initial red) that is invalid

Collapse
 
siddharthshyniben profile image
Siddharth

That makes sense, thanks for the new info!

Collapse
 
afif profile image
Temani Afif

By the way, why instead of this

.tuesday {
    --bg: var(--tuesday) #1abc9c;
    --fg: var(--tuesday) white;
    --space: var(--tuesday) 5px;
    background: var(--bg, initial);
    border-radius: var(--space, initial);
    padding: var(--space, initial);
    color: var(--fg, initial) !important;
}
Enter fullscreen mode Exit fullscreen mode

You don't use this:

.tuesday {
    background: var(--tuesday, #1abc9c);
    border-radius: var(--tuesday, 5px);
    padding: var(--tuesday, 5px);
    color: var(--tuesday, #fff) !important;
}
Enter fullscreen mode Exit fullscreen mode

?

if --tuesday is initial you get the fallback value, if it's (space) all the properties will have invalid values and will fallback to their initial (default) value as well

Collapse
 
siddharthshyniben profile image
Siddharth

Now that I think of it that make sense, I guess when I was coding it didn't come to mind. Will do that next time!