Dark Mode Toggle without Javascript
Written by Lucie Zdeňková on 2025-01-02
htmlcssThe following code snippets of HTML and CSS demonstrates a simple setup to enable dark mode using only HTML and CSS, without any JavaScript. This solution takes user preferences into account and allows the user to manually toggle between light and dark mode. Practical example can be seen in the header of this page.
HTML code
The HTML code structure relies on a hidden checkbox as the toggle, paired with a
label styled to visually represent the dark mode switch. To ensure correct behaviour
it's necessary to have <input>
element on the same level as a
wrapper <div>
element with class="backgroud"
. Element
<label>
represents the UI of the toogle and can be placed anywhere
in the <div class="background>
element.
<body>
<input type="checkbox" id="darkmode-toggle" />
<div class="background">
<!-- Replace this section with your desired HTML content -->
<label for="darkmode-toggle" class="darkmode-label">
<span class="sun"></span>
<span class="moon"></span>
</label>
<!-- Replace this section with your desired HTML content -->
</div>
</body>
CSS variables
Lets's get into CSS part. Key feature for creating dark mode function is setting so
called CSS variables. Every property that requires dynamic behaviour based on color
preference has to be defined here. We need to start with the selector
:root
that represents the default light mode. Variables --bg:
white;
and --text: black;
represents change of the visual
background and the text color on the website. Rest of the variables --toggle-bg,
--gradient-bg1, --gradient-bg2, --label-left, --transformantion, --img-sun,
--img-moon
defines changes of toggle UI itself.
:root {
--bg: white;
--text: black;
--toggle-bg: #ebebeb;
--gradient-bg1: #ffcc89;
--gradient-bg2: #d8860b;
--label-left: 2px;
--img-sun: url('img/sun-white.svg');
--img-moon: url('img/moon.svg');
}
Next comes definition of colors for user's default dark mode using selector
@media (prefers-color-scheme: dark)
. As you can see, variables --bg,
--text
have switched values, the rest of the variables for the toggle UI has
altered values for dark visual.
@media (prefers-color-scheme: dark) {
:root {
--bg: black;
--text: white;
--toggle-bg: #242424;
--gradient-bg1: #777;
--gradient-bg2: #3a3a3a;
--label-left: 98px;
--transformantion: translateX(-100%);
--img-sun: url('img/sun.svg');
--img-moon: url('img/moon-white.svg');
}
}
Third selector #darkmode-toggle:checked~*
represents behaviour when the
dark mode is toggled manually by the user on the website. It is sufficient to simply
copy and paste the variables with it's values from the previous step.
#darkmode-toggle:checked~* {
--bg: black;
--text: white;
--toggle-bg: #242424;
--gradient-bg1: #777;
--gradient-bg2: #3a3a3a;
--label-left: 98px;
--transformantion: translateX(-100%);
--img-sun: url('img/sun.svg');
--img-moon: url('img/moon-white.svg');
}
Lastly, behaviour for switching from dark mode to light mode has to be defined using
selector @media (prefers-color-scheme: dark) { #darkmode-toggle:checked~*
{
. Because this represents light mode, we can simply copy and paste all
variables from the very first step. It's important and unevitable to use all those four
selectors in this exact order, due to CSS overriding rules.
@media (prefers-color-scheme: dark) {
#darkmode-toggle:checked~* {
--bg: white;
--text: black;
--toggle-bg: #ebebeb;
--gradient-bg1: #ffcc89;
--gradient-bg2: #d8860b;
--label-left: 2px;
--img-sun: url('img/sun-white.svg');
--img-moon: url('img/moon.svg');
}
}
Illustrations
This toggle user interface contains two illustrations of sun and moon in svg format.
In order to change their color with toggle state, they are implemented as backgroud svg
images of <span class="sun">
and <span
class="moon">
element. Those illustrations were downloaded from UXWING. In total, four illustrations are needed, two black
ones and two white ones. Save them to your /img
directory.
Styling div wrapper
The .background
class styles the main container of the webpage and
ensures that website adapts to the selected theme using properties from variables in
the previous code snippet. Properties backgroud: var(--bg);
and
color: var(--text);
are using previously defined colors in variables.
min-height: 100vh;
ensures that the container always occupies at least the
full height of the viewport (100% of the visible browser window), in order to extend
backgroud color across the entire page, even if the content is minimal. display:
flex;
activates a flexbox layout for the container. This makes it easy to align
and distribute child elements within the container. flex-direction:
column;
aligns child elements vertically (in a column) from top to bottom. This
is useful for stacking content like headers, main sections, and footers.
.background {
background: var(--bg);
color: var(--text);
min-height: 100vh;
display: flex;
flex-direction: column;
}
This blogpost outlined how to define CSS variables for dark mode and how to style the wrapper div container. In the next part, we will demonstrate how to style the dark mode toggle itself using our defined variables.
Thank you for reading and see ya in the second part!