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

theme-mode

v1.1.0

Published

A lightweight utility for switching CSS theme modes

Downloads

792

Readme

theme-mode

Try it out on the Master CSS documentation site.

Features

Vanilla, Next, React, Vue, and Master CSS are available:

  • ⚡️ Ultra-lightweight ~1.2KB
  • 🌈 Switch between light, dark, and system
  • 💖 Sync with system theme preferences
  • 💾 Store the user's preference in localStorage
  • 💫 Access theme preferences and modes through context
  • 🧩 Built-in "use client" directive

Why should I use this?

The prefers-color-scheme cannot force override to the specified color mode. Once you want to switch themes, you cannot use @media (prefers-color-scheme: dark).

https://stackoverflow.com/questions/56300132/how-to-override-css-prefers-color-scheme-setting

How does this work?

This package automatically switches themes using class="" and color-scheme:; that's it.

<html class="dark" style="color-scheme: dark">
    <body>
        <h1 class="bg:black@dark bg:white@light">Hello World</h1>
    </body>
</html>

To view the source code examples:

  • React: https://github.com/master-co/theme-mode/tree/main/examples/react
  • Vue: https://github.com/master-co/theme-mode/tree/main/examples/vue
  • Svelte: https://github.com/master-co/theme-mode/tree/main/examples/svelte

Getting Started

Install the package depending on your framework.

Vanilla

npm install theme-mode
import ThemeMode from 'theme-mode'

const themeMode = new ThemeMode().init()

// Set `preference` anywhere to switch theme modes.
themeMode.preference = 'dark'

React

npm install @master/theme-mode.react
import ThemeModeProvider from '@master/theme-mode.react'

export default function App({ children }) {
    return (
        <ThemeModeProvider preference='system'>
            {children}
        </ThemeModeProvider>
    )
}

Vue

npm install @master/theme-mode.vue
<script setup lang="ts">
import ThemeModeProvider from '@master/theme-mode.vue'
</script>

<template>
    <ThemeModeProvider preference="system">
        <slot></slot>
    </ThemeModeProvider>
</template>

Svelte

npm install @master/theme-mode.svelte
<script lang="ts">
    import ThemeModeProvider from '@master/theme-mode.svelte';
</script>

<ThemeModeProvider preference="system">
    ...
</ThemeModeProvider>

Basic usage

Default to light or dark mode

You can set the default theme mode when the user has not set a theme preference, such as common light or dark mode.

<ThemeModeProvider preference="dark">...</ThemeModeProvider>

Rendered as:

<html class="dark" style="color-scheme: dark">…</html>

Default based on the system preference

Automatically switches modes based on the user's system preference.

<ThemeModeProvider preference="system">...</ThemeModeProvider>

Rendered as:

<html class="light" style="color-scheme: light">…</html>
<!-- or -->
<html class="dark" style="color-scheme: dark">…</html>

Note: CSS only supports light and dark modes for system preferences.

Sync the user's preference to localStorage

By default options.store is set to 'theme-preference', which uses this key to set local storage when the preference is changed.

In this way, the theme preference set last time will be applied when the user visits or refreshes the website again.

To disable local storage, set it to false.

<ThemeModeProvider store={false}>...</ThemeModeProvider>

Apply styles based on theme modes

You can now create selector-driven CSS themes using tools like Master CSS.

<html class="light" style="color-scheme: light">
    <body>
        <div class="block@dark" hidden>Dark</div>
        <div class="block@light" hidden>Light</div>
        <div class="block@christmas" hidden>Christmas</div>
    </body>
</html>

Create a theme-switching select

React

import { useThemeMode } from '@master/theme-mode.react'

export default function ThemeModeSelect() {
    const themeMode = useThemeMode()
    return (
        <button>
            {themeMode.value === 'dark' ? '🌜' : '☀️'} {themeMode.preference}
            <select className="abs full inset:0 opacity:0"
                value={themeMode.preference}
                onChange={(event) => themeMode.preference = event.target.value}>
                <option value="light">☀️ Light</option>
                <option value="dark">🌜 Dark</option>
                <option value="system">System</option>
            </select>
        </button>
    )
}

Vue

<script setup lang="ts">
import { inject } from 'vue'
const themeMode = inject<any>('theme-mode')
</script>

<template>
    <button class="px:5x r:2x font:18 h:48 bg:slate-10@light bg:gray-80@dark fg:strong rel">
        {{ themeMode.value === 'dark' ? '🌜' : '☀️' }} {{ themeMode.preference }}
        <select class="abs full inset:0 opacity:0" v-model="themeMode.preference">
            <option value="light">☀️ Light</option>
            <option value="dark">🌜 Dark</option>
            <option value="system">System</option>
        </select>
    </button>
</template>

Svelte

<script lang="ts">
    import { getThemeMode } from "@master/theme-mode.svelte";
    const themeMode = getThemeMode();
</script>

<span id="value">{$themeMode.value}</span>
<span id="preference">{$themeMode.preference}</span>
<select class="abs full inset:0 opacity:0" bind:value={$themeMode.preference}>
    <option value="light">☀️ Light</option>
    <option value="dark">🌜 Dark</option>
    <option value="system">System</option>
</select>

Avoid FOUC

If you've pre-rendered your CSS styles to the page to improve the page loading and first-render experience, it's crucial to initialize the theme mode in advance.

By default, three modules of minified advanced initial scripts for different default themes are exported:

  • theme-mode/pre-init: https://github.com/master-co/theme-mode/tree/main/packages/core/src/pre-init.iife.min.ts
  • theme-mode/pre-init-light: https://github.com/master-co/theme-mode/tree/main/packages/core/src/pre-init-light.iife.min.ts
  • theme-mode/pre-init-dark: https://github.com/master-co/theme-mode/tree/main/packages/core/src/pre-init-dark.iife.min.ts

You have to use the build tool to inject these original scripts into HTML <head>, taking Next.js as an example:

import PRE_INIT_THEME_MODE_SCRIPT from '!!raw-loader!theme-mode/pre-init';

export default async function RootLayout({ children }: {
    children: JSX.Element
}) {
    return (
        <html suppressHydrationWarning>
            <head>
                <script dangerouslySetInnerHTML={{ __html: PRE_INIT_THEME_MODE_SCRIPT }}></script>
                ...
            </head>
            ...
        </html>
    )
}

Or copy them directly:

const preference = localStorage.getItem('theme-preference') || 'system';
const value = preference === 'system'
    ? matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light'
    : preference;

document.documentElement.classList.add(value);
if (['dark', 'light'].includes(value)) document.documentElement.style.colorScheme = value;

Those JS resources cannot be loaded from external because this is a critical script for the first painting of the page.

Options

.preference

Specify the default theme preference.

  • Default: undefined
  • Value: 'dark' | 'light' | 'system' | string

.store

Enable local storage and specify the key for localStorage.

  • Default: 'theme-preference'
  • Value: 'theme-preference' | string | false

Properties

themeMode.preference

Set or get the current theme preference.

  • Default: undefined
  • Value: 'dark' | 'light' | 'system' | string

themeMode.value

Set or get the current theme mode.

  • Default: undefined
  • Value: 'dark' | 'light' | string

themeMode.storage

Get the currently stored theme preference.

  • Default: undefined
  • Value: 'dark' | 'light' | string

themeMode.systemPreference

Get the theme mode of the current system

  • Default: undefined
  • Value: 'dark' | 'light' | string

Methods

themeMode.init()

Initialize the default theme mode. This is usually performed after the DOM has been initialized.

themeMode.destroy()

Destroy the theme mode, including removing media query listeners.

Community

The Master community can be found here:

Our 《 Code of Conduct 》 applies to all Master community channels.

Contributing

Please see our CONTRIBUTING for workflow.