Mode Watcher

repository·main·Indexed 20 days ago

https://github.com/svecosystem/mode-watcher

A utility library for SvelteKit applications to manage light and dark modes. It provides automatic OS preference detection, persistence via localStorage, and theme color management. Key features include the ModeWatcher component for root layout integration, functions like toggleMode, setMode, and resetMode, and readable stores for tracking resolved mode, user preferences, and system settings without causing a flash of unstyled content (FOUC).

Tokens
11K
Snippets
49
Records
58
Agent score
68%

What's inside mode-watcher

  1. Overview of Mode Watcher

    main
    Mode Watcher is a utility library for Svelte applications designed to manage light and dark modes with minimal configuration. It is built to be compatible with Server-Side Rendering (SSR), Client-Side Rendering (CSR), and Static Site Generation (SSG) without causing a flash of unstyled content (FOUC).
  2. Understand userPrefersMode and systemPrefersMode stores

    main

    Mode Watcher provides two stores for tracking different preference layers:

    • userPrefersMode: A writable store representing the user's chosen mode preference. It accepts "light", "dark", or "system".
    • systemPrefersMode: A readable store representing the operating system's mode preference. It returns "light", "dark", or undefined (on the server). It tracks OS changes unless track={false} is passed to the ModeWatcher component.
  3. Difference between `mode` and `userPrefersMode`

    main

    When managing themes, it is important to distinguish between the resolved mode and the user's explicit preference:

    1. mode: The actual active theme (always "light" or "dark"). This is what you use to determine how to style components or logic.
    2. userPrefersMode: The user's specific selection, which can be "light", "dark", or "system".

    If you need to know if a user has opted to follow the system settings, use userPrefersMode instead of mode.

  4. Key features of Mode Watcher

    main

    Mode Watcher provides several automated theme management features:

    • Real-time detection: Tracks prefers-color-scheme changes automatically.
    • Persistence: Uses localStorage to save user preferences and sync themes across different tabs and windows.
    • System Integration: Updates the color-scheme CSS property (for scrollbars and form controls) and the theme-color meta tag (for the browser interface).
    • Smooth Transitions: Automatically disables CSS transitions during theme changes to prevent flickering.
    • Flexible Defaults: Allows setting a default theme and provides options to toggle between light/dark modes or respect system preferences.
  5. Use the theme state to track granular visual styles

    main

    The theme state is a readable state that holds the currently active theme as a string. Unlike mode (which is restricted to "light" or "dark"), theme allows you to define and track any custom string (e.g., "dracula", "retro", or "corporate"). This is useful for supporting multiple granular visual styles beyond simple light/dark modes. You can access the current value via theme.current and update it using setTheme.

    <script lang="ts">
    	import { setTheme, theme } from "mode-watcher";
    
    	function cycleTheme() {
    		if (theme.current === "dracula") {
    			setTheme("retro");
    		} else {
    			setTheme("dracula");
    		}
    	}
    </script>
    
    <button onclick={cycleTheme}>{theme.current}</button>
  6. Understand the difference between Mode and Theme

    main

    In Mode Watcher, Mode and Theme are distinct layers of customization that work together:

    Mode

    Represents the user's preference for the interface appearance. It controls the technical implementation of light/dark styles by:

    • Applying the light or dark class to the root <html> element.
    • Setting the color-scheme (light or dark) for browser rendering.

    Possible mode values:

    • "light"
    • "dark"
    • "system" (follows the operating system's preference)

    Theme

    Represents your application's design system (colors, typography, spacing, etc.). A single theme can contain both light and dark variants. Instead of creating separate themes for different modes (e.g., dracula-light and dracula-dark), you should provide one theme (e.g., dracula) that includes both variants. Mode Watcher will automatically select the correct variant based on the active mode.

  7. Use `createInitialModeExpression` to prevent theme flashing in SvelteKit

    main

    In SvelteKit applications, you can use createInitialModeExpression to generate a secure, inline JavaScript snippet that sets the initial color mode (light, dark, or system) before the page hydrates. This prevents the 'flash of unstyled content' (FOUC) where the theme jumps from light to dark (or vice versa) during page load.

    This utility is specifically designed for environments with strict Content Security Policies (CSP) that require a nonce for inline scripts.

    import { createInitialModeExpression } from "mode-watcher";
    
    // Returns a string containing the inline script logic
    const snippet = createInitialModeExpression();
  8. Setup the ModeWatcher component

    main

    To use mode-watcher, add the ModeWatcher component to your root +layout.svelte file. This component automatically detects user preferences and applies the "dark" class and color-scheme style attribute to the html element.

    Configuration Props

    • track (boolean): If set to false, the component will stop tracking operating system preference changes.
    • defaultMode (string): Sets a default mode (e.g., "dark") instead of automatic detection.
    • themeColors (object): Configures the theme-color meta tag. Accepts an object with dark and light keys.

    Example configuration:

    <script lang="ts">
    	import { ModeWatcher } from "mode-watcher";
    	let { children } = $props();
    </script>
    
    <ModeWatcher 
      track={true} 
      defaultMode="light" 
      themeColors={{ dark: "black", light: "white" }}
    />
    {@render children()}
  9. Install and use the ModeWatcher component

    main

    To automatically apply mode and theme preferences in a SvelteKit application, add the ModeWatcher component to your root +layout.svelte file.

    ModeWatcher performs the following actions:

    • Detects user mode preferences (light, dark, or system).
    • Applies the appropriate class (defaulting to dark) to the <html> element.
    • Sets the color-scheme attribute on the <html> element.
    • Optionally applies a theme via the data-theme attribute.
    <script lang="ts">
    	import { ModeWatcher } from "mode-watcher";
    	let { children } = $props();
    </script>
    
    <ModeWatcher />
    {@render children()}
  10. Build and preview a Svelte project

    main

    To prepare your application for production, run the build script. Once the build is complete, you can use the preview script to run a local server that serves the production build for testing.

    # Create a production version of your app
    npm run build
    
    # Preview the production build
    npm run preview