npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@brandonaaron/dark-pref

v1.0.1

Published

Dark mode preference manager and optional web components.

Downloads

5

Readme

Dark Pref

Provides 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 <html> 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.

Quick Start

Add this to the <head>:

<!-- tiny render blocking script to avoid flash of content -->
<script src="https://cdn.jsdelivr.net/npm/@brandonaaron/[email protected]/dist/DarkPref.blocking.js"></script>
<!-- rest of the exports could be bundled with your favorite bundler too -->
<script src="https://cdn.jsdelivr.net/npm/@brandonaaron/[email protected]/dist/DarkPrefToggleElement.js" type="module"></script>
<style>
  :root { /* optional css variables */
    --dark-pref-when-light-color: #999; /* defaults to #000 */
    --dark-pref-when-dark-color: #ccc; /* defaults to #fff */
  }
</style>

Then add the included custom element to the <body>:

<dark-pref-toggle>
  <!-- Optional slots "light" and "dark" (they default to a sun and moon svg) -->
  <span slot="light">Switch to dark mode</span>
  <span slot="dark">Switch to light mode</span>
</dark-pref-toggle>

Check the examples folder for other uses like creating your own custom element or not using custom elements at all.

Docs: DarkPref

DarkPref.current

A getter that includes three props: system (boolean), user (DarkPrefUserSetting), and isDark (boolean)`.

DarkPref.reset()

Clear out any stored preference and resync. This always triggers the darkpref:sync event on the document.

DarkPref.sync()

Used to trigger a resync of the current state. This always triggers the darkpref:sync event on the document.

DarkPref.toggle(pref)

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.

Docs: DarkPrefToggleBaseElement

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.

ariaPressedForCurrentState (getter)

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.

ariaLabelForCurrentState (getter)

Provides a value for aria-label attribute.

connectedCallback()

Connects click and darkpref:sync event handlers.

disconnectedCallback()

Disconnects click and darkpref:sync event handlers.

sync()

This needs to be implemented by the extending class. This should check DarkPref.current and implement necessary changes to the DOM.

toggle(pref)

Changes the user preference. The pref argument is optional but should be a boolean or null if passed (DarkPrefUserSetting type).

Docs: DarkPrefToggleElement

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.

CSS Variables

  • --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)

Slots

  • light: Defaults to a sun svg icon
  • dark: Defults to a moon svg icon

Usage with Astro

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 <head> of my main layout:

<script is:inline src="/DarkPref.blocking.js"></script>

Then I created a DarkPrefToggle.astro component with the following:

---
---
<script>
  import { DarkPref } from '@brandonaaron/dark-pref/dist/DarkPref.js'
  import '@brandonaaron/dark-pref/dist/DarkPrefToggleElement.js'
  // The following lines is applicable only if you're using transitions
  document.addEventListener('astro:after-swap', () => { DarkPref.sync() })
</script>
<style>
  :root {
    --dark-pref-when-light-color: theme("colors.zinc.800");
    --dark-pref-when-dark-color: theme("colors.zinc.200");
  }
</style>
<dark-pref-toggle class="w-[24px] h-[24px]"></dark-pref-toggle>

My tailwind.config.js also has the following to ensure it works with html element dark mode class toggling:

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).