A compilation of any information I have for making color schemes and theme switchers (ex. darkmode vs lightmode).
Update March 5, 2025: added a way to switch themes using buttons and pure CSS, no JS needed! Also added switching stylesheets to the JS method.
This one is for people who like making minimally styled HTML pages, keeping the default white background and black text. If you like doing this, but would like to make it darkmode instead you can use color-scheme
!
html {
color-scheme: dark;}
I have a more detailed CSS variable guide, but heres a quick run down.
Maybe you love using a ton of different colors, but find it difficult to keep track of which color is which, especially if you're using hex color codes. With variables you can assign color codes a name, and use said name in the CSS instead of a color code. This is particularly helpful when you want to change a color value but you've already used said color code a whole bunch in your CSS. Now all you have to do is change the value of the variable. Variables will also be very useful if you want to make different themes for the same page.
:root {
--bg: rgb(20,10,10);
--text: #5A78BC;
--head: #F4A719;
--link: hotpink;}
You can name the variables whatever you want, they just need to be in the :root
selector and have two dashes (--) infront of the name. The value of them will of course have to be defined as a valid color in whichever format you prefer (rgb, hex, html names, hsl, etc)
The variables will look a little different in the CSS though. You have to put it in var(--variable)
form.
body {
color: var(--text);
background: var(--bg);}
a {
color: var(--link);}
The background of the page will be dark grey (rgb(20,10,10)), the text of the page will be whatever #5A78BC is, and links will be hot pink!
This section will be much longer, detailing a variety of ways you can provide different themes or light/dark modes for your readers :3.
This media query defines CSS styles for viewers who have dark or light mode selected as a preference in their browser settings.
@media (prefers-color-scheme: dark;) {
body {
color: white;
background: black;}}
@media (prefers-color-scheme: light;) {
body {
color: black;
background: white;}}
There are two options for prefered color scheme: light and dark. Like with any other media query (@media
) you can put specific CSS styling for any elements inside the media query brackets.
You can make one mode "default" by only using a query for the non-default color scheme.
body {
color: white;
background: black;}
@media (prefers-color-scheme: light;) {
body {
color: black;
background: white;}}
Here I have made darkmode the default style, but if a viewer has lightmode as their prefered browser setting, they will see the styles defined in the media query.
You can of course combine this method with color-scheme
or variables.
html {
color-scheme: light;}
@media (prefers-color-scheme: dark;) {
html {
color-scheme: dark;}}
:root {
--bg: navy;
--text: ivory;}
@media (prefers-color-scheme: light;) {
:root {
--bg: ivory;
--text: navy;}}
body {
color: var(--text);
background: var(--bg);
border: 1px solid var(--text);}
First the default color of the variables are defined (a dark mode in this case), then the variables are redefined for users who prefer light mode, lastly the variables are used to give colors to elements. This makes it so that you only have to redefine the varibles in the media query rather than redefining every property in the CSS that uses color.
The thing about these media queries is that the user can't switch between your color schemes unless they do it in their browser settings, not really convienient. They are also limited to one lightmode and one darkmode, where as you may want to create a bunch of different themes for your viewers to be able to pick from. JavaScript does not become necessary! (Previously this guide said it was, but I learned a new method!)
This CSS and HTML only method allows the viewer to change the mode by buttons, and is unlimited by number or browser preferences. It uses HTML input elements and the CSS :has
selector.
First we are going to make two "buttons" to control our theme (or more if you have more than two themes). They will not actually be button elements though, but input
elements wrapped inside of its label.
<label>
<input type="radio" id="dark" name="mode">
dark
</label>
<label>
<input type="radio" id="light" name="mode">
light
</label>
The input
element is just the small circle or rounded box that users can select. The label
is the text next to it, but it is wrapped around the input so that when the user hits the label, the input will be activated, making it easier to select. This will also help with styling, so you don't have to add a span or div to put the label and input in a box together.
type="radio"
defines what kind of input it is, in this case a "radio button" allows the viewer to select one option out of a group.
Don't forget to give each input its own unique id, likely something similar to its label name. However, all inputs for the theme selection should have the same name="example"
attribute, to indicate they are a part of the same group.
These buttons look a little more complex than they do in the upcoming JS example, but the CSS is stupid easy:
body:has(#light:checked) {
color: black;
background: white;}
body:has(#dark:checked) {
color: white;
background: black;}
Okay, there are a few things going on here, so lets break it up. body:has()
selects the body element only if it has whatever is in the parenthesis. #light:checked
is selecting the element with the id "light" (our lightmode input), but only if it was checked by the viewer. Thus the body only has a white background and black text if the #light element inside of it was selected.
To set a defualt, just add the plain body
selector to the one you want default, separated by a comma:
body, body:has(#dark:checked) {
color: white;
background: black;}
You can of course combine this with variables!
body:has(#light:checked) {
--text: black;
--bg: white;}
body, body:has(#dark:checked) {
--text: white;
--bg: black;}
body {
color: var(--text);
background: var(--bg);}
To change what the radio buttons look like, check out the last section of my CSS collection filter guide which also uses radio buttons.
This method is great for single pages with their own themes, but it will not save the users preference across all of your pages, nor for the next time they visit. This is where JS actually becomes necessary.
We can use JavaScript to either add a stylesheet, or change the value of the :root
variables.
Either way we will need at least two buttons and functions (more if you have three or more themes)
<button onclick="darkmode()">dark theme</button>
<button onclick="lightmode()">light theme</button>
Each theme button is given a different function that will be applied when the user clicks the button. These buttons will go in the body where ever you want them to be on your page.
If you're lazy like me and put your CSS in the style
element rather than using style sheets, you can use JS to change the variable values. I usually put this JS in the head element after the style element.
<script>
var rootsel = document.querySelector(':root');
function darkmode() {
rootsel.style.setProperty('--bg', 'rgb(30,30,30)');
rootsel.style.setProperty('--text', 'rgb(220,190,170)');}
function lightmode() {
rootsel.style.setProperty('--bg', 'rgb(250,220,190)');
rootsel.style.setProperty('--text', 'rgb(35,35,35)');}
</script>
The variable rootsel
is defined to select :root
, this just makes the following JS a little neater rather than repeating the same chunk over and over. Note that this won't work if you are not using variables in the :root
selector.
A function for each theme is named, and in each function every color variable is redefined.
Note that you have to redefine the colors for even your default theme, otherwise viewers will not be able to switch back to it. Unfortunately this means that if you change the default colors in your CSS, you will have to copy the changes in your JS manually.
You may want to store a users prefered theme, so whenever they visit your site, they dont have to manually change it every time. This is done using JS localStorage
, which stores a tiny bit of info in the users browser. This info is just a value you define and what group it belongs to.
To our light and dark functions we are going to add a line like this:
localStorage.setItem('keyname', 'valuename');
Where "keyname" is any name we are going to give our tiny set of items. All themes will have the same key name. If you wanted the users browser to save number of clicks on a button, you would make a different set of items by giving it a different key name. The value represented by "valuename" is the name we are going to assign that specific theme, like dark or light. setItem
means we are defining these bits.
In our functions they will look like this:
<script>
var rootsel = document.querySelector(':root');
function darkmode() {
rootsel.style.setProperty('--bg', 'rgb(30,30,30)');
rootsel.style.setProperty('--text', 'rgb(220,190,170)');
localStorage.setItem('theme', 'dark');}
function lightmode() {
rootsel.style.setProperty('--bg', 'rgb(250,220,190)');
rootsel.style.setProperty('--text', 'rgb(35,35,35)');
localStorage.setItem('theme', 'light');}
</script>
This stores the item for each theme, but nothing will actually happen with the saved item. Now we have to retrieve the data and define what happens depending on its value. After and outside of our theme functions we will have these conditions:
if(localStorage.getItem('theme') == 'dark'){
darkmode();}
if(localStorage.getItem('theme') == 'light'){
lightmode();}
We retrieve the data by using getItem
instead of set item. We see if it equals one of our values, and if it does the JS will then preform the function we put inside of it, one of the theme functions we already made. Just make sure the function corresponds to the value (ex. 'dark' and 'darkmode()' )
That's it! you can make as many as you need for all your themes.
Updated section March 5 2025! Here is a more elegant way to use JS for theme switching.
Your buttons will be the same, but the HTML, CSS, and JS will be different.
We will have two tiny CSS files (or more depending on how many themes you have). All they will have is the variables for its respective theme.
:root {
--text: white;
--bg: #black;}
This one might be called "darkmode.css". Make similar ones for your lightmode or other themes, just defining variable colors in the :root
selector. Make a seperate CSS file for the rest of your CSS (though you can use your variables in it).
Since we have a main CSS file and a theme colors CSS file, our stylesheet HTML in the head will look like this:
<link id ="colortheme" href="lightcolors.css" rel="stylesheet" type="text/css" media="all">
<link href="style.css" rel="stylesheet" type="text/css" media="all">
The theme color file goes first in order for the second file to use the variables it defines. Be sure to give this element an ID for the JS to use. Which little CSS file we put in the href=""
attribute depends on which theme we want to be default.
Although the CSS gets split up some, as a result the JS gets a lot cleaner. You also won't have to worry about making sure the variable values in your JS match those in your CSS if you decide to change them. Just change them in your CSS and the JS will use the same ones rather than overiding.
function lightmode() {
document.getElementById('colortheme').setAttribute('href', 'lightcolors.css');}
function darkmode() {
document.getElementById('colortheme').setAttribute('href', 'darkcolors.css');}
Each function selects the element with the ID colortheme
which we gave to the stylesheet in the head for the theme colors. We then change the value inside of the href
attribute, aka the link. For the lightmode function we change the href to the light color stylesheet, and for the darkmode function we change the href to the dark color stylesheet.
We can also make the browser remember the visitors choice with localStorage
which follows the same ideas as explained in the previous section.
function lightmode() {
document.getElementById('colortheme').setAttribute('href', 'lightcolors.css');
localStorage.setItem('theme', 'light');}
function darkmode() {
document.getElementById('colortheme').setAttribute('href', 'darkcolors.css');
localStorage.setItem('theme', 'dark');}
if(localStorage.getItem('theme') == 'dark'){
darkmode();}
if(localStorage.getItem('theme') == 'light'){
lightmode();}
In short each function assigns itself a value in the theme set, which the users browser stores, and if the browser detects one of these values later on, it activates the theme function associated with it.
Have fun making themes!