Edit: I managed to get it working by using :has and nesting css classes!
body:has(#theme-toggle:checked) {
background-color: #eff1f5;
.content {
color: #4c4f69;
}
.header {
color: #8839ef
}
.nav {
background-color: #dce0e8;
color: #4c4f69;
}
}
I’m making a website for my school’s robotics team and I’m trying to create a dark theme toggle but it’s just not working. I’m trying to avoid javascript and I’ve seen this kind of thing done with only css and html before so I know it’s possible. any advice?
repo: https://github.com/WrenHavoc/JudgeMent-Call-Website
edit: currently my code looks something like this:
#theme-toggle:checked ~ body {
background-color: #eff1f5;
color: #fff;
}
#theme-toggle:checked ~ html {
background-color: #eff1f5;
}
#theme-toggle:checked ~ .content {
background-color: #eff1f5;
}
the button itself is a checkbox that has display set to none and the label set as an svg so when you click the icon, it gets checked.
<input style="display: none;" type="checkbox" id="theme-toggle">
<label for="theme-toggle" class="theme-button">
<img class="theme-button-svg" src="./icons/half-moon.svg">
</label>
I used a similar strategy when making the menu for the site so I know it should work
.menu {
position:absolute;
margin:0%;
right:20px;
top:20px;
}
.menu-button {
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
width: 30px;
height: 22px;
cursor: pointer;
z-index: 2; /* above menu */
}
.menu-button span {
display: block;
height: 4px;
background-color: #cba6f7;
border-radius: 2px;
transition: all 0.3s ease;
}
.menu-items {
top: 30px;
right: -20px;
width: 200px;
background-color: #181825;
position: absolute;
display: none;
}
.menu-items li {
margin: 20px 0;
}
.menu-items a {
text-decoration: none;
color: #cba6f7;
font-size: 18px;
padding:5px;
}
.menu-items a:hover {
text-decoration: none;
background-color: #cba6f7;
color: #181825;
font-size: 18px;
}
.menu-selected {
text-decoration: underline;
text-decoration-color: #cdd6f4;
text-decoration-thickness: 3px;
}
.menu-selected:hover {
text-decoration-color: #181825;
}
#menu:checked ~ .menu-items {
display: inline;
}
#menu:checked + .menu-button span:nth-child(1) {
transform: rotate(45deg) translate(5px, 7.5px);
}
#menu:checked + .menu-button span:nth-child(2) {
opacity: 0;
}
#menu:checked + .menu-button span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -7.5px);
}
<input style="display: none;" type="checkbox" id="menu">
<label for="menu" class="menu-button">
<span></span>
<span></span>
<span></span>
</label>


I haven’t tried it, but I’m guessing to implement a manual toggle with
color-scheme, you could then do this:body { color-scheme: light dark; } body:has(#theme-toggle:checked) { color-scheme: dark; }Indeed you can, just like this (see below). I just tried it in a codepen and it seems to work. Although do note that if you put
light darkas the default value, once you toggle the switch off again, it’ll choose whatever mode the user agent wants, which might still be dark mode. So if you want it to be light by default, you’ll need to use a value that enforces that.body { color: light-dark(black, white); background-color: light-dark(white, black); } body:has(#theme-toggle:checked) { color-scheme: dark; }Ah yeah, I guess, users would expect some action to happen when they click that toggle, not just for it to change from automatic-dark to manual-dark.
Perhaps the simplest non-JS and non-persistent solution would then be to have it pick the color-scheme automatically by default, but if the checkbox is checked, then set the colors to the opposite.
So, probably something like this:
body { color-scheme: light dark; color: light-dark(black, white); background-color: light-dark(white, black); } @media (prefers-color-scheme: dark) { body:has(#theme-toggle:checked) { color-scheme: light; /*opposite*/ } } @media (prefers-color-scheme: light) { body:has(#theme-toggle:checked) { color-scheme: dark; /*opposite*/ } }You could probably even theme the checkbox to show a sun or a moon, depending on the current color scheme. 🙃
All true. 👍
Although OP mentioned they can’t use prefers-color-scheme for fingerprinting reasons. And for some reason are against a little bit of JavaScript to help save the value.
A checkbox also can’t offer the three common values: Light, Dark, and Follow system. But oh well, to each their own.
Well, to me, it sounded like they themselves can’t rely on
prefers-color-scheme, which is why the manual toggle is necessary, but it doesn’t hurt to support it for other folks.I guess, that does mean that a solution without persistence is going to be annoying, but yeah, I don’t think that’s solvable without JS. One could ensure that the JS is entirely optional, so that when the user blocks it, you simply get no persistence, but that’s probably about it…
Agree on all points. 👌