惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
Microsoft Security Blog
Microsoft Security Blog
Jina AI
Jina AI
博客园 - 叶小钗
J
Java Code Geeks
博客园 - 聂微东
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
美团技术团队
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
罗磊的独立博客
T
The Blog of Author Tim Ferriss
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
The Cloudflare Blog
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
小众软件
小众软件

endtimes.dev

Happy new year | endtimes.dev why lowercase letters save data Practical betterments | endtimes.dev Short posts and some site updates Input Output | endtimes.dev Actually, dark mode can save the planet Why your website should be under 14kB in size HTML and CSS only multiple color scheme picker Why your website should work without Javascript. you can use css to remove the double-tap zoom feature on iOs Missing Dice | endtimes.dev Can Dark Mode Save Battery Life and Human Civilzation? Your DNS has a significant impact on site speed. Add spaces to the dock on MacOS You can leave out <html>, <head>, and <body> tags. Listen for changes between dark and light mode with javascript Use a keyboard shortcut to quickly toggle light and dark mode on MacOs. Why Your Website Should Use Dithered Images You can restore the startup chimes on MacOS Emoji Clipboard | endtimes.dev You can use Emojis as Favicons .dev and HSTS preload | endtimes.dev Prod — Block Distractions. Achieve your goals. Pattern generator | endtimes.dev Green Quarantine | endtimes.dev Qwitter Bot | endtimes.dev
An HTML and CSS only dark-mode toggle button.
2021-03-14 · via endtimes.dev

A quick guide to building an html and css only dark-mode toggle.

Using just css and html we'll build a button that:

  • changes between light-mode and dark-mode
  • defaults to the user's preferred color scheme
  • changes the label to reflect the user's preferred color scheme.

Here's a CodePen with the final product (plus some styling)

Why no javascript? #

I've been building a website with tools for board game players. Simple things like dice and random playing cards.

One of my goals is for every tool to work without javascript. The site also has a dark-mode and light-mode. (and some other color schemes too).

I needed a way to toggle dark-mode without javascript — while still defaulting to the visitor preferred-color-scheme.

Here's my solution, simplified for this tutorial:

How it works: #

Most dark-mode toggle buttons work by changing an attribute on the <body> tag, and then targeting that attribute in the css. Like so:

<body class="dark-mode">
<!-- Site Content -->
</body>

<style>

body {
background:white
}

body.dark-mode {
background:black
}

</style>

<script>
function toggleDarkMode() {
// some logic to change the class on the body tag
}
</script>

This is very simple, but requires javascript to add and remove the dark-mode class.

Luckily we can still make changes to our styles without javascript. We can use CSS to target non-javascript user interactions.

Here we're going to use a checkbox, and the :checked pseudo-selector:

<body>
<input id="color-mode" type="checkbox" name="color-mode">
<label for="color-mode">Dark Mode</label>

<!-- Site Content -->
</body>

We need to make sure the input is the first thing in our <body> so we can target everything after it in our CSS.


body {
background:white
}

#dark-mode:checked ~ * {
background:black
}

But there's a problem with this!

There's no way in CSS to target the parent of an element. So we can't change the color of the <body>.

So we'll use a work around. We'll place a <div> after our checkbox that does the job of the <body>. Then we style the <div> to fill the screen.

Now we can use the checkbox input to style our <div>:

<body>
<input id="color-mode" type="checkbox" name="color-mode">
<label for="color-mode">Dark Mode</label>

<div class="color-scheme-wrapper">
<!-- Site Content -->
</div>
</body>

<style>

.color-scheme-wrapper {
min-height:100vh;
background:white;
color:black;
}

#color-mode:checked ~ .color-scheme-wrapper {
background:black;
color:white;
}

</style>

This works! But there's still a few things we need to fix:

  • We need to make it default to the user's preferred color scheme.
  • We should use css variables because it will make life easier.
  • We need to change the label to reflect the user's preferences.

First let's add the css variables. #

CSS variables allow us to define colors that change based on the checkbox. We'll use just two colors one for the background and one for text:

:root {
--bg:#F4F0EB;
--text:#141414;
}

#dark-mode:checked ~ .color-scheme-wrapper {
--bg:#333;
--text:#fff;
}

.color-scheme-wrapper {
background:var(--bg);
color:var(--text);
}

Now, when we check the checkbox the variables change, and those changes are reflected in the rest of or css.

Defaulting to our visitors' preferred color scheme. #

Now let's make it so it defaults to user's preferences. To target user preferences we can use a @media query.

Based on the result of the prefers-color-scheme media query we'll swap our light-mode and dark-mode themes.

So if a user's device has dark-mode enabled it starts off dark:

:root {
--bg:white;
--text:black;
}

@media (prefers-color-scheme: dark) {
:root {
--bg:black;
--text:white;
}
}

#color-mode:checked ~ .color-scheme-wrapper {
--bg:black;
--text:white;
}

@media (prefers-color-scheme: dark) {
#color-mode:checked ~ .color-scheme-wrapper {
--bg:white;
--text:black;
}
}

.color-scheme-wrapper {
min-height:100vh;
background:var(--bg);
color:var(--text);
}

Changing the label based on user preferences. #

Now that we've swapped dark-mode and light-mode we need to make sure the label for our checkbox reflects this.

It would be confusing if the label said dark-mode was on when the screen was bright white.

There's a quick fix for this too. First we add two sets of text in our <label> one for each user preference:

<input id="color-mode" type="checkbox" name="color-mode">
<label for="color-mode">
<span class="dark-mode-hide">Dark Mode</span>
<span class="light-mode-hide">Light Mode</span>
</label>

Then we hide one of the labels depending on the mode.

This set of media queries allows us to target both light-mode, dark -mode, and browsers that don't support prefers-color-scheme:


.light-mode-hide {
display:none;
}

@media (prefers-color-scheme: dark) {
.dark-mode-hide {
display:none;
}
}

@media (prefers-color-scheme: dark) {
.light-mode-hide {
display:initial;
}
}

That's it. Let me know what you think!

If you can think of a clever way of having the color scheme remain the same after you've navigated to a different page. Let me know.

Also - There's a good argument for using input[type=radio] instead of input[type=checkbox]. But the concept is easier illustrated with a checkbox.

Links:

Here's a link to the codePen example with some extra styling: codepen.io

Here's a link to the five color version on: missingdice.com

published
14 Mar 2021
modified
14 Mar 2021
author
Nathaniel
tags
posts post html css a11y tutorial dark mode