CSS Theming in 2024
With the recent migration to Astro, I’ve been working on updating the theming to use new CSS techniques, avoid flash-of-unstyled-content, and handle OS-level dark/light preferences. It’s been a fun journey, and I’ve learned a lot about modern CSS and JavaScript. Here’s a quick overview of what I’ve done so far.
What I wanted
- Define themes using only CSS, using JavaScript only to switch between them.
- Respect the user’s system-level preferences, then site preferences if set.
- Save the user’s preference so it persists whenever they visit the site.
- Avoid flash-of-unstyled-content (FOUC) when restoring the saved theme.
CSS Improvements
After 8 years, it was time to update the theming to use modern CSS techniques. Since the original post, CSS has evolved significantly, and I wanted to take advantage of new features that allow me to write primarily in CSS instead of maintaining styles also in JavaScript.
Broadly, I’ve moved the previous configuration from JavaScript to CSS, using CSS custom properties to define the theme’s colors. This allows me to change the theme by updating a single CSS file, rather than modifying JavaScript and CSS.
You’ll notice that I use html[data-theme='light']
and html[data-theme='dark']
to define the theme’s colors. This is how I’ll switch between themes using JavaScript. Instead of updating the CSS properties directly, I’ll update the data-theme
attribute on the html
element and let CSS handle the rest.
color-scheme
Introduced in 2022, the color-scheme
property allows you to specify the color scheme for the page. Which in turn the browser can use to adjust the page’s appearance based on the user’s preferences. Things like scrollbars, form controls, and other elements can be styled based on how this property is set.
In my case, this meant setting the color-scheme
to light
or dark
to indicate which version each theme should use.
System Preferences
To respect the user’s system-level preferences, I use the prefers-color-scheme
media query. This query allows me to detect if the user prefers a light or dark color scheme and adjust the site’s theme accordingly. With the following code, if the user prefers a dark color scheme, the site will automatically switch to the dark theme, even if they haven’t set a preference.
By default, the site will use the light theme as we styled html
to use the light color scheme. If the user prefers a dark color scheme, the site will automatically switch to the dark theme.
While there is a bit of duplication between the prefers-color-scheme
and the html[data-theme='dark']
styles, it’s a small price to pay and can be easily managed with a CSS preprocessor.
JavaScript Improvements
There is still some JavaScript involved, but it’s minimal compared to the previous implementation. The JavaScript is responsible for:
- Switching between themes
- Saving the user’s preference
prefers-color-scheme
is a great feature that can also be queried from JavaScript. This allows me to detect the user’s system-level preference and adjust the site’s theme programatically.
Flash of Unstyled Content (FOUC)
Normally, best practices for loading JavaScript are to defer loading it until after the page has loaded. However, this can lead to a flash of unstyled content (FOUC) when the user’s preference is restored. To avoid this, I load the script at the end of the body, after the page has loaded.
First we need to check what the theme should be:
Then, we set the theme on the html
element using the dataset API to allow us to avoid any FOUC:
No more flash of unstyled content! 🎉
Switching Themes
The code to switch between themes didn’t change that much. I still use the same logic to determine the current state. This time however, I don’t need to manually update each of the custom properties that needs to change. Instead, I update the data-theme
attribute on the html
element by using a click event listener on the theme switcher icon.
Tada
It’s been a fun journey that allowed me to delete more code and improve the site’s performance. I’m excited to see how the site evolves over the next few years as new CSS and JavaScript features are introduced. If you have any questions or suggestions, feel free to reach out.
No comments yet. Share on Mastodon and see your comment or write a post on your blog if you support Webmentions
No reposts yet. Share on Mastodon and see your repost or write a post on your blog if you support Webmentions
No likes yet. Share on Mastodon and see your like or write a post on your blog if you support Webmentions
No bookmarks yet. Share on Mastodon and see your bookmark or write a post on your blog if you support Webmentions
Powered by Webmentions