Theme Switch

The Theme Switch component provides an elegant toggle for switching between light and dark themes. It automatically detects system preferences and persists user choices across sessions.

When to use

  • To provide theme switching functionality in your application
  • When supporting both light and dark modes
  • To respect user system preferences
  • For improved accessibility and user experience

Features

  • Automatic system preference detection
  • LocalStorage persistence
  • Smooth animations
  • Multiple size variants
  • Accessible keyboard navigation
  • Works with any framework
  • Flicker prevention solution included
⚠️ Common Issue: Dark Mode Flicker
Without proper setup, pages may briefly flash light mode before switching to dark mode on refresh. See the "Preventing Dark Mode Flicker" section in the Usage tab for the solution.

Anatomy

  • Switch Button: The clickable toggle control
  • Thumb: Moving indicator showing current state
  • Icons: Sun (light) and moon (dark) icons
  • Label: Optional text label

Basic Usage (Uncontrolled)

This toggle manages its own state and applies theme to the document automatically.

Theme

Size Variants

Available in small, medium (default), and large sizes.

Small
Medium
Large

Fixed/Floating Variants

Fixed theme switches stay in the viewport corner:

Note: In real usage, the fixed theme switch will be positioned relative to the viewport, not this container. Add pm7-theme-switch--fixed-icon class to create a floating theme toggle.

View Full Demo →

Header Integration Example

How the theme toggle looks in a typical application header.

My Application

Keyboard Navigation Test

Try using Tab to focus and Enter/Space to toggle these switches:

Use Tab to navigate between toggles, then press Enter or Space to activate them.

Disabled State

Disabled

No Hover Effect

No hover effect

Installation

npm install @pm7/core

CSS Classes

Class Description
Base Classes
pm7-theme-switch Base class for theme switch container
pm7-theme-switch-button The toggle button (auto-created)
pm7-theme-switch-thumb The moving thumb indicator (auto-created)
pm7-theme-switch-icon Icon container (auto-created)
Size Modifiers
pm7-theme-switch--sm Small size variant
pm7-theme-switch--lg Large size variant
State Modifiers
pm7-theme-switch--disabled Disabled state
pm7-theme-switch--no-hover Removes hover effects
Layout Modifiers
pm7-theme-switch--label-start Places label before the switch
Position Modifiers
pm7-theme-switch--fixed Fixed position (bottom right) with label
pm7-theme-switch--fixed-icon Fixed position circular button (icon only)

Data Attributes

Attribute Type Default Description
data-pm7-theme-switch boolean - Marks element for auto-initialization
data-theme string 'light' Current theme state (auto-managed)
data-default-theme string null Override default theme ('light' or 'dark'). Maps to defaultTheme option.
data-storage-key string 'pm7-theme' LocalStorage key for persistence. Maps to storageKey option.
data-apply-to-root boolean true Apply 'dark' class to document root. Maps to applyToRoot option.

Basic Usage

<!-- Basic theme switch -->
<div class="pm7-theme-switch" data-pm7-theme-switch>
  <span>Theme</span>
</div>

<!-- Without label -->
<div class="pm7-theme-switch" data-pm7-theme-switch></div>

<!-- Small size (use CSS class, not data attribute) -->
<div class="pm7-theme-switch pm7-theme-switch--sm" data-pm7-theme-switch>
  <span>Dark mode</span>
</div>

Auto-initialization

Theme Switch components with the data-pm7-theme-switch attribute are automatically initialized when the DOM loads. This means you don't need to write any JavaScript for basic usage.

<!-- This will auto-initialize on page load -->
<div class="pm7-theme-switch" data-pm7-theme-switch></div>

<!-- Prevent auto-initialization if you want manual control -->
<div class="pm7-theme-switch my-custom-switch"></div>
<script>
  // Manually initialize later
  const element = document.querySelector('.my-custom-switch');
  new PM7ThemeSwitch(element, { /* options */ });
</script>

React Example

function App() {
  return (
    <div className="pm7-theme-switch" data-pm7-theme-switch>
      <span>Theme</span>
    </div>
  );
}

Vue Example

<template>
  <div class="pm7-theme-switch" data-pm7-theme-switch>
    <span>Theme</span>
  </div>
</template>

JavaScript API

import { PM7ThemeSwitch } from '@pm7/core';

// Manual initialization with all options
const element = document.querySelector('.my-theme-switch');
const themeSwitch = new PM7ThemeSwitch(element, {
  defaultTheme: 'dark',     // 'light', 'dark', or null for auto-detect
  storageKey: 'pm7-theme',  // LocalStorage key for persistence
  applyToRoot: true,        // Apply 'dark' class to document root
  onChange: (theme) => {    // Called on user interaction only
    console.log('Theme changed to:', theme);
  }
});

// Methods
themeSwitch.setTheme('dark');  // Set theme programmatically
themeSwitch.getTheme();         // Get current theme
themeSwitch.toggle();           // Toggle theme

// Static method for auto-initialization
PM7ThemeSwitch.autoInit();     // Initialize all [data-pm7-theme-switch] elements

Custom Storage Key

<!-- Use a custom localStorage key -->
<div class="pm7-theme-switch" 
     data-pm7-theme-switch
     data-storage-key="my-app-theme">
</div>

Without Root Class Application

<!-- Don't apply 'dark' class to document root -->
<div class="pm7-theme-switch" 
     data-pm7-theme-switch
     data-apply-to-root="false">
</div>

Preventing Dark Mode Flicker

When implementing dark mode, users may experience a "flicker" where the page briefly shows in light mode before switching to dark mode on refresh. To prevent this, add the following script in your HTML <head> before any stylesheets:

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your App</title>
  
  <!-- Dark mode flicker prevention - MUST come before stylesheets -->
  <script>
    // Prevent dark mode flicker - must run before page renders
    (function() {
      const savedTheme = localStorage.getItem('pm7-theme');
      if (savedTheme === 'dark' || (!savedTheme && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
        document.documentElement.classList.add('dark');
      }
    })();
  </script>
  
  <!-- Your stylesheets come AFTER the script -->
  <link rel="stylesheet" href="@pm7/core/styles/index.css">
  <link rel="stylesheet" href="your-styles.css">
</head>

Why this works: The script runs synchronously before the browser starts rendering, applying the dark class immediately if needed. This eliminates the visual flicker that occurs when dark mode is applied after the page loads.

Important Notes

  • The onChange callback is only triggered by user interaction, not during initial theme setup
  • Size variants must be applied using CSS classes (e.g., pm7-theme-switch--sm), not data attributes
  • Components are automatically initialized on page load if they have the data-pm7-theme-switch attribute
  • The component automatically detects and respects system theme preferences
  • Theme preference is persisted in localStorage and survives page reloads
  • Prevent flicker: Add the flicker prevention script (see above) to your HTML <head> to avoid light mode flash on page refresh

Accessibility

The Theme Switch component is fully accessible:

  • Keyboard navigable with Tab key
  • Toggleable with Space or Enter keys
  • ARIA attributes for screen readers
  • Proper role and state announcements
  • Respects prefers-reduced-motion

Copy this link to share the Theme Switch documentation with AI assistants:

https://raw.githubusercontent.com/patrickmast/pm7-ui/main/packages/core/src/components/theme-switch/README.md

🤖 AI-Optimized Documentation

The documentation link above provides AI assistants with complete context about the Theme Switch component, including all props, methods, examples, and best practices.

Tip: When asking AI assistants to help implement the Theme Switch, include this documentation link for accurate, up-to-date information about the component API.