Time-based background colour transitions with Temporal and CSS color-mix -
localghost
Time-based background colour transitions with Temporal and CSS color-mix
5 July 2026
Tags:
site
temporal
javascript
css
I've given my website a bit of a refresh! There's a slightly updated layout if you're on desktop, plus I ditched the etc page and I've revamped my links page to be powered by raindrop.io. The minimalist theme is still minimalist, but a bit more fancy. The vaporwave theme has a newly jazzed-up nav bar with some adorable little icons. But the biggest change is to the city theme, which was previously a starry-sky dark mode theme.
If you're reading this between the hours of 9pm - 5am, you might be wondering what all the fuss is about - it looks pretty much the same as it did before. That's because the theme changes depending on the time of day!
You can select the time of day using the picker in the top right, after the theme switcher. I'm persisting the choice in session storage so you don't get attacked by sudden light mode when changing pages, but if you visit again in the future it'll reset back to "now".
I was going to just turn the layout into a pastel lo-fi-aesthetic thing, but then I realised that a) I needed some kind of dark mode and b) I'd miss the stars! So I thought... why not both? And why stop at just night and day? (Hat tip to Alistair Shepherd who did something similar with his beautiful Firewatch-inspired website.)
Then I remembered that the Temporal API was available experimentally in Chrome and Firefox, and I'd been looking for an excuse to try it out.
Introducing Temporal
For the uninitiated, Temporal is a solution to the objectively terrible Date API in JavaScript. Date was based on Java's Date library, which was also objectively terrible and has long been deprecated.
It's always really confusing that Date instances show either local or UTC time depending on which function you use to display them, and date operations are so fiddly that most of us turn to third party libraries like date-fns or luxon.
Temporal massively simplifies the API, introducing some new concepts:
PlainDateTime: a date and time with no timezone (TZ)
PlainDate: a date with no time information and no TZ
PlainTime: a time with no date information and no TZ
ZonedDateTime: a date and time in a specified TZ
PlainTime came in useful for this project, as we don't really care what the day is - only what time it is, so we know what colours to show.
Getting the user's local time
The first thing to do was figure out the time according to the user's browser.
The Temporal.Now namespace has various methods for interacting with the current time, including plainTimeISO() which by default gives us a PlainTime in local time. (You can also pass in a time zone to get a zoned time.)
const timeNow = Temporal.Now.plainTimeISO();<br>Now we need to know when to show the different colours.
Defining the stages
The day is split into four stages: sunrise, daytime, sunset and night. Daytime and night are long - 11.5 hours each - whereas sunrise and sunset each last 90 minutes.
The background of the page has a two-colour gradient:
--background: fixed linear-gradient(var(--bg-gradient-top), var(--bg-gradient-mid) 80%);
The footer has an additional colour that's created with a linear gradient from transparent oklch(0 0 0 / 0) to the chosen third colour.
background: linear-gradient(oklch(0 0 0 / 0) 40%, var(--bg-gradient-bottom));<br>This means the last colour sticks to the bottom of the page rather than stretching across the viewport height (it's hard to control even when you specify a percentage in the gradient). It also gives more of a glow that really looks like the sun rising/setting or the glow of the city, which I love.
I defined an object for the stages and colours:
const stages = {<br>sunrise: {<br>start: Temporal.PlainTime.from("06:30:00"),<br>next: "day",<br>color1: "oklch(0.618 0.3157 265.76)",<br>color2: "oklch(0.8867 0.1222 328.24)",<br>color3: "oklch(0.9529 0.1222 106.94)",<br>},<br>day: {<br>start: Temporal.PlainTime.from("08:00:00"),<br>next: "sunset",<br>color1: "oklch(58% 0.15433 300)",<br>color2: "oklch(85% 0.22133 302)",<br>color3: "oklch(98 0.22133 302)",<br>},<br>sunset: {<br>start: Temporal.PlainTime.from("19:30:00"),<br>next: "night",<br>color1: "oklch(0.6933 0.1899 297.53)",<br>color2: "oklch(75.504% 0.24612 357.26)",<br>color3: "oklch(88.591% 0.1422 62.595)",<br>},<br>night: {<br>start: Temporal.PlainTime.from("21:00:00"),<br>next: "sunrise",<br>color1: "oklch(25.27% 0.0919 276.73)",<br>color2: "oklch(47.35% 0.284 283.78)",<br>color3: "oklch(62.831% 0.23521 310.291)",<br>},<br>};<br>CSS custom properties are easy to set via JS - you can use root.style.setProperty:
root.style.setProperty(<br>"--bg-gradient-top",<br>"oklch(25.27% 0.0919 276.73)",<br>);<br>Unlike Date, we don't have to do any gymnastics to compare Temporal instances: there's literally a compare function on each type of instance. Just like with other JS comparison functions, it returns 1 if the first instance is greater than the...