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" /> |