Dark mode preference manager and optional web components.
npm install @brandonaaron/dark-prefProvides a preference manager (DarkPref), an optional web component (DarkPrefToggleElement), an optional base web component (DarkPrefToggleBaseElement), and a tiny render blocking script to avoid a potential initial flash of content.
* No dependencies, ES Modules, TypeScript, Unit Tests
* Persists to localStorage if possible (uses __dark-pref__ key)
* Keeps multiple tabs in sync via localStorage
* Provides information on system preference and user preference
* Prioritizes the system dark mode preference
* Emits a darkpref:sync custom event on the document with current state as the detail
* dark class name toggled on element
See how it works on my personal site (it is in the top right corner). Details below about how I implemented it in Astro.
Add this to the :
``html`
Then add the included custom element to the
:`html
Switch to dark mode
Switch to light mode
`Check the
examples folder for other uses like creating your own custom element or not using custom elements at all.Docs: DarkPref
$3
A getter that includes three props:
system (boolean), user (DarkPrefUserSetting), and isDark (boolean).Clear out any stored preference and resync. This always triggers the darkpref:sync event on the document.
Used to trigger a resync of the current state. This always triggers the darkpref:sync event on the document.
Changes the user preference. The pref argument is optional but should be a boolean or null if passed (DarkPrefUserSetting type).
The DarkPref.current.user value will be null when their dark preference aligns with the system preference.
Provides a custom element to extend for your own custom element. Automatically hookes up a click and darkpref:sync event handlers on connectedCallback. Does not register a tag.
Provides a value for aria-pressed attribute. The button is considered pressed only if the user has a different prefence from the current system preference.
Provides a value for aria-label attribute.
Connects click and darkpref:sync event handlers.
Disconnects click and darkpref:sync event handlers.
This needs to be implemented by the extending class. This should check DarkPref.current and implement necessary changes to the DOM.
Changes the user preference. The pref argument is optional but should be a boolean or null if passed (DarkPrefUserSetting type).
Provides a quick and easy button to toggle dark mode on/off. Provides two slots and two css variables for customization. This extends the DarkPrefToggleBaseElement. Automatically registers the tag name dark-pref-toggle.
There is a dist/custom-elements.json in the npm package.
* --dark-pref-when-dark-color: Controls the color when dark is preferred (defaults to #fff)
* --dark-pref-when-light-color: Controls the color when light is preferred (defaults to #000)
* light: Defaults to a sun svg icon
* dark: Defults to a moon svg icon
I'm using this on my personal site which is built with Astro and TailwindCSS.
First I npm installed it:
```
npm install @brandonaaron/dark-pref
I copied over dist/DarkPref.blocking.js to the public directory and included that via an is:inline script tag in the
of my main layout:`html
`Then I created a
DarkPrefToggle.astro component with the following:`astro
---
---
`My
tailwind.config.js also has the following to ensure it works with html element dark mode class toggling:`js
module.exports = {
//...
darkMode: 'class',
//...
}
`Docker
Run
docker compose up to get a working dev environment. This will start a dev server on port 8888.Run
docker compose run --build dev npm run test to just run the unit tests in isolation.There is a config for a vscode devcontainer too. The devcontainer uses the same Dockerfile/docker-compose config but does not start a dev server. Instead open a terminal within vscode and run
npm start (or npm run test`, etc).