Switching Themes
I got the bug recently to finally do something that’s been annoying me for some time: set up a dark theme for this website that is user selectable and that will persist.
Normally I prefer the white background (default) that I originally designed but I understand that some prefer a dark background. When it’s in the evening and I want to write/edit a new post (like I’m doing right now) so do I. When it’s daytime, though, I prefer to be back on the white background.
The Dark (Style)
Creating a dark version of the site is relatively easy. I originally styled things sparingly here to keep it light and simple.
With plenty of thanks to past-Pat for being proactive there aren’t many rules to change to make a dark theme work. Here they are:
body, hr:after {
background-color: #222;
color: #ccc;
}
h1, h2, h3, h4, h5 {
color: #eee;
}
code, pre {
background-color: #111;
}
That’s pretty much it.
I set the body background-color
and color
to dark versions (the hr:after
is for the new horizontal rules I added a few days ago).
The headings are slightly brighter than the body text (just like how they’re slightly darker in the light theme) and the code blocks use a darker background. The link text colors were tweaked from looking at the Solarized color scheme by Ethan Schoonover so they were already on the path to being used in either light or dark versions and didn’t need modification.
I went with a more neutral dark gray background vs. the slightly blue-tinted Solarized background because I didn’t want the background to clash with any photos I share.
Applying the Styles
The application of these styles was pretty straightforward.
I had all of the main styles for the site load first (in the document <head>
), where I set the dark theme as part of my default styles.
Immediately after these default styles, I added a new <style>
element that actually set the colors to the light theme version.
I did it this way because later I will talk about the JavaScript I use to switch the styles as well as add persistence for whatever a visitor chooses. This way if a user has JavaScript disabled they will get the light theme by default.
The pseudo code for my styles now looks like this:
<head>
<style>
... default styles for everything
{dark theme rules}
</style>
<style id='lightcss'>
{light theme rules}
</style>
This will make sense a little later.
Inverting Colors
As an aside, while looking around at others works to see if I was missing something, I came across an awesome method for creating a dark version of a website.
It’s in the blog post A Theme Switcher over on the Inclusive Components site and it’s an elegant use of the invert
filter.
The post is well worth a read, but the general gist is to use the invert
filter to invert all of the colors on a site.
They explicitly give a background color to the :root
html element, and then trickle it down to all other elements on the page through the *
selector (so you don’t end up with white text on a white background).
A little selector magic to avoid images and videos and you have a very tersely implemented theme inversion!
Here is the final CSS they used:
:root {
background-color: #fefefe;
filter: invert(100%);
}
* {
background-color: inherit;
}
img:not([src*=".svg"]), video {
filter: invert(100%);
}
Wonderful! I almost went this route as well, but my colors are not quite set to be how I’d like them as straight inversions. I will keep this in my bag of tricks for later though!
Theme Switching
I now needed a way to allow visitors to use this new theme. A small bit of JavaScript at the end of my pages takes care of enabling/disabling the default light theme colors:
document.getElementById('theme').addEventListener("click", function() {
light = document.getElementById('lightcss');
if( light.disabled == true ){
light.disabled = false;
localStorage.removeItem("theme");
}else{
light.disabled = true;
localStorage.setItem("theme", "dark");
}
});
I attach an event listener to a link on the page (see the “Theme” button in my header).
Targeting the lightcss
style element, we can toggle it through the disabled attribute.
This might seem odd if you’ve done this before.
Normally you’d probably consider setting the dark/light rules and applying them to a class. Then you’d toggle them by adding/removing the classes. While this works great, it tends to be slightly problematic when used for applying stored user preferences…
The Persistence of Memory
You’ll notice in the previous JavaScript snippet the lines that set/remove a localStorage parameter theme
.
This allows me to remember what theme the user wants.
Another small piece of JavaScript in the <head>
allows me to check for the setting:
if( localStorage.getItem('theme') == 'dark' ){
document.getElementById("lightcss").disabled = true;
}
This could be done with cookies as well but many folks might have them turned off for security reasons.
This helps get around the problem of using a class to switch styles. If the setting is for the dark theme, it needs to be applied before the page has a chance to load (and show a flash of the white theme). Using the disabled attribute we can set (well, unset) these styles before the page has a chance to render.
Give It a Try
Whew! That was a mouthful. Well, go ahead and give it a try if you’d like! In my header under my name and main site links you should now find a “THEME” link that will swap things around for you. Let me know if anything seems wonky.