Supporting article https://andy-bell.design/wrote/create-a-user-controlled-dark-or-light-mode/
A Pen by Philip Borenstein on CodePen.
<main> | |
<article> | |
<h1>Dark mode should be a user preference—not presumed</h1> | |
<p>Yes, by default, when a user has <code>@media (prefers-color-scheme: dark)</code> set, we should set a dark theme, but we should also provide a switch for if the dark them isn’t working out for them.</p> | |
<p>This also benefits users that don’t have <code>@media (prefers-color-scheme: dark)</code> set because they get a toggle to choose, too.</p> | |
</article> | |
<div class="user-toggle"> | |
<button class="toggle"> | |
toggle | |
</button> | |
<button class="reset">reset</button> | |
</div> | |
</main> |
Supporting article https://andy-bell.design/wrote/create-a-user-controlled-dark-or-light-mode/
A Pen by Philip Borenstein on CodePen.
const STORAGE_KEY = 'user-color-scheme'; | |
const COLOR_MODE_KEY = '--color-mode'; | |
const toggleButton = document.querySelector('.toggle'); | |
const resetButton = document.querySelector('.reset'); | |
const getCSSCustomProp = (propKey) => { | |
let response = getComputedStyle(document.documentElement).getPropertyValue(propKey); | |
if (response.length) { | |
response = response.replace(/\'|"/g, '').trim(); | |
} | |
return response; | |
}; | |
/** | |
* Takes either a passed settings ('light'|'dark') or grabs that from localStorage. | |
* If it can’t find the setting in either, it tries to load the CSS color mode, | |
* controlled by the media query | |
*/ | |
const applySetting = passedSetting => { | |
let currentSetting = passedSetting || localStorage.getItem(STORAGE_KEY); | |
if(currentSetting) { | |
document.documentElement.setAttribute('data-user-color-scheme', currentSetting); | |
} | |
} | |
/** | |
* Gets the current setting > reverses it > stores it | |
*/ | |
const toggleSetting = () => { | |
let currentSetting = localStorage.getItem(STORAGE_KEY); | |
switch(currentSetting) { | |
case null: | |
currentSetting = getCSSCustomProp(COLOR_MODE_KEY) === 'dark' ? 'light' : 'dark'; | |
break; | |
case 'light': | |
currentSetting = 'dark'; | |
break; | |
case 'dark': | |
currentSetting = 'light'; | |
break; | |
} | |
localStorage.setItem(STORAGE_KEY, currentSetting); | |
return currentSetting; | |
} | |
toggleButton.addEventListener('click', evt => { | |
applySetting(toggleSetting()); | |
}); | |
resetButton.addEventListener('click', evt => { | |
localStorage.clear() | |
document.documentElement.removeAttribute('data-user-color-scheme'); | |
}); | |
applySetting(); |
:root { | |
--color-mode: 'light'; | |
--color-dark: #141414; | |
--color-dark-alpha: rgba(0, 0, 0, 0.1); | |
--color-light: #efefef; | |
--color-light-alpha: rgba(255, 255, 255, 0.9); | |
} | |
:root { | |
--background: var(--color-light); | |
--text-color: var(--color-dark); | |
--border-color: var(--color-dark-alpha); | |
} | |
:root { | |
--color-mode: 'light'; | |
} | |
@media (prefers-color-scheme: dark) { | |
:root { | |
--color-mode: 'dark'; | |
} | |
:root:not([data-user-color-scheme]) { | |
--background: var(--color-dark); | |
--text-color: var(--color-light); | |
--border-color: var(--color-light-alpha); | |
} | |
} | |
[data-user-color-scheme="dark"] { | |
--background: var(--color-dark); | |
--text-color: var(--color-light); | |
--border-color: var(--color-light-alpha); | |
} | |
body { | |
background: var(--background); | |
color: var(--text-color); | |
transition: background 500ms ease-in-out, color 200ms ease; | |
} | |
/* Presentational demo styles */ | |
body { | |
font-family: sans-serif; | |
padding: 2rem 1rem; | |
line-height: 1.4; | |
display: grid; | |
place-items: center; | |
} | |
article { | |
max-width: 75ch; | |
margin: 0 auto; | |
} | |
article > * + * { | |
margin-top: 1em; | |
} | |
h1 { | |
font-size: 2.5rem; | |
line-height: 1.1; | |
} | |
p { | |
font-size: 1.2rem; | |
opacity: 0.9; | |
} | |
code { | |
font-weight: 700; | |
font-size: 1.3em; | |
white-space: pre; | |
} |
<link href="https://codepen.io/andybelldesign/pen/Ygmwym.css" rel="stylesheet" /> |