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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@space-man/react-theme-animation

v2.0.0

Published

React theme switching with smooth view transition animations and multi-theme support

Downloads

1,544

Readme

@space-man/react-theme-animation

https://github.com/user-attachments/assets/2819cf70-c474-478e-b821-6b26457c8d4a

React theme switching with smooth view transition animations, multi-theme support, and synchronized state management.

Spaceman React Theme Animation version 2

Version 2 introduces new features for enhanced color theme management and improved Vite integration.

  • New functions to directly toggle between different color themes, without using the ThemeSelector component
  • Vite theme provider now supports theme toggling and persistence
  • Functions to manage and check active themes
  • Functions to toggle light and dark themes directly
import { useThemeAnimation } from '@space-man/react-theme-animation'

function ColorThemeDemo() {
  const { createColorThemeToggle, isColorThemeActive, toggleDarkTheme, toggleLightTheme } =
    useThemeAnimation({
      colorThemes: ['supabase'],
    })

  return (
    <>
      <button onClick={createColorThemeToggle('supabase')} className="theme-toggle-btn">
        Supabase Theme {isColorThemeActive('supabase') ? '✅' : '❌'}
      </button>
      <button ref={ref} onClick={toggleDarkTheme} className="theme-toggle-btn">
        Dark Theme
      </button>
      <button ref={ref} onClick={toggleLightTheme} className="theme-toggle-btn">
        Light Theme
      </button>
    </>
  )
}

// Vite Theme Provider Example
function App() {
  return (
    <ViteThemeProvider
      themes={['light', 'dark', 'system']}
      colorThemes={['default', 'supabase']}
      defaultTheme="system"
      defaultColorTheme="default"
    >
      <ThemeToggle />
    </ViteThemeProvider>
  )
}

function ThemeToggle() {
  const { createColorThemeToggle, isColorThemeActive } = useViteTheme()

  return (
    <button onClick={createColorThemeToggle('supabase')} className="theme-toggle-btn">
      Supabase Theme {isColorThemeActive('supabase') ? '✅' : '❌'}
    </button>
  )
}

Live Demo

Live Demo Link

Features

  • Smooth Animations: Beautiful view transition animations for theme switching with customizable origins
  • Multi-Theme Support: Support for light, dark, and system themes
  • Color Themes: Additional color theme variants (brand colors, etc.)
  • Powerful Hook: useThemeAnimation hook with full control
  • Ready Components: ThemeSwitcher and ThemeSelector components
  • Provider Pattern: Centralized theme state management with SpacemanThemeProvider
  • State Synchronization: Prevents state drift between multiple components
  • Responsive: Works on all screen sizes including high-resolution displays
  • Performance: Optimized animations with reduced motion support
  • TypeScript: Full TypeScript support with comprehensive types
  • Customizable: Extensive configuration options
  • Backward Compatible: Works with existing implementations

Installation

npm install @space-man/react-theme-animation

Usage Patterns

This library supports multiple usage patterns:

  1. Hook-Only Usage: Direct hook usage for custom implementations
  2. Provider Pattern: Centralized state management (recommended)
  3. ViteThemeProvider: Specialized provider for Vite React SPAs

See the complete Getting Started Guide for detailed examples, advanced configurations, and best practices.

View the Next.js Code Example to see this package in action with a complete implementation of both hook and provider pattern.

View the Vite React SPA Demo for a complete Vite implementation using ViteThemeProvider.


1. Hook-Only Usage For Light and Dark mode animation

Perfect for custom theme toggle buttons and complete control over animated theme logic.

Basic Theme Toggle

import { useThemeAnimation } from '@space-man/react-theme-animation'

function ThemeToggle() {
  const { theme, toggleTheme, ref } = useThemeAnimation()

  return (
    <button ref={ref} onClick={toggleTheme} className="theme-toggle-btn">
      {theme === 'light' ? '🌙' : '🌞'} {theme}
    </button>
  )
}

2. Provider Pattern (Recommended)

The most powerful pattern using SpacemanThemeProvider for centralized theme state management. This prevents state drift and provides synchronized animations.

Basic Provider Setup

import {
  SpacemanThemeProvider,
  ThemeSwitcher,
  ThemeSelector,
} from '@space-man/react-theme-animation'

function App() {
  return (
    <SpacemanThemeProvider>
      <div className="app">
        <header>
          <ThemeSwitcher />
        </header>

        <aside>
          <ThemeSelector colorThemes={['default', 'supabase', 'mono']} />
        </aside>

        <main>
          <YourAppContent />
        </main>
      </div>
    </SpacemanThemeProvider>
  )
}

3. ViteThemeProvider (Vite React SPAs)

Specialized theme provider optimized for Vite React single-page applications. Provides essential theme management without animation features.

Basic Vite Setup

import { ViteThemeProvider, useViteTheme } from '@space-man/react-theme-animation'

function App() {
  return (
    <ViteThemeProvider defaultTheme="system" storageKey="my-app-theme" attribute="class">
      <div className="app">
        <header>
          <ThemeToggle />
        </header>
        <main>
          <ThemeSection />
        </main>
      </div>
    </ViteThemeProvider>
  )
}

function ThemeSection() {
  return (
    <SpacemanThemeProvider>
      <div className="app">
        <header>
          <ThemeSwitcher />
        </header>

        <aside>
          <ThemeSelector colorThemes={['default', 'supabase', 'mono']} />
        </aside>

        <main>
          <YourAppContent />
        </main>
      </div>
    </SpacemanThemeProvider>
  )
}

function ThemeToggle() {
  const { theme, setTheme, resolvedTheme } = useViteTheme()

  return (
    <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
      {resolvedTheme === 'dark' ? '🌞' : '🌙'} {theme}
    </button>
  )
}

ViteThemeProvider Props

| Prop | Type | Default | Description | | --------------------------- | ------------------------------- | -------------- | ------------------------------------------- | | children | ReactNode | - | React children | | attribute | 'class' \| 'data-theme' | 'class' | How to apply theme to DOM | | defaultTheme | 'light' \| 'dark' \| 'system' | 'system' | Default theme | | enableSystem | boolean | true | Enable system theme detection | | disableTransitionOnChange | boolean | false | Disable CSS transitions during theme change | | storageKey | string | 'vite-theme' | localStorage key for persistence |


Defining Application color theme as CSS Variables

The library uses CSS custom properties for theming. Define these in your MAIN CSS file. Note: do not define the other theme variables in a separate CSS file and import it, as the :root variables for light mode will override the light mode variables for the other themes. Keep everything in the same file.

/* Base theme variables */
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
}

/* Dark theme */
.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
}

/* Add other theme variants from design system or tweakcn */

.theme-supabase {
  --background: oklch(0.9911 0 0);
  --foreground: oklch(0.2046 0 0);
}

.theme-supabase.dark {
  --background: oklch(0.1822 0 0);
  --foreground: oklch(0.9288 0.0126 255.5078);
}

.theme-mono {
  --background: oklch(1 0 0);
  --foreground: oklch(0.1448 0 0);
}

.theme-mono.dark {
  --background: oklch(0.1448 0 0);
  --foreground: oklch(1 0 0);
}

API Reference

SpacemanThemeProvider

Context provider for centralized theme state management for the ThemeSelector and ThemeSwitcher components. The SpacemanThemeProvider allows you to manage themes and color themes in your application with smooth animations and synchronized state and provide a state reference for the ThemeSelector and ThemeSwitcher components.

<SpacemanThemeProvider
  defaultTheme="system"
  defaultColorTheme="blue"
  themes={['light', 'dark', 'system']}
  colorThemes={['default', 'blue', 'green', 'purple']}
  animationType={ThemeAnimationType.CIRCLE}
  duration={800}
  onThemeChange={theme => console.log('Theme:', theme)}
  onColorThemeChange={colorTheme => console.log('Color:', colorTheme)}
>
  {children}
</SpacemanThemeProvider>

SpacemanThemeProvider Props

| Property | Type | Default | Description | | -------------------- | ---------------------------------- | ----------------------------- | ------------------------------------- | | defaultTheme | Theme | 'system' | Initial theme | | defaultColorTheme | ColorTheme | 'default' | Initial color theme | | themes | Theme[] | ['light', 'dark', 'system'] | Available themes | | colorThemes | ColorTheme[] | ['default'] | Available color themes | | animationType | ThemeAnimationType | ThemeAnimationType.CIRCLE | Animation type | | duration | number | 500 | Animation duration in ms | | blurAmount | number | 2 | Blur amount for blur-circle animation | | onThemeChange | (theme: Theme) => void | - | Global theme change callback | | onColorThemeChange | (colorTheme: ColorTheme) => void | - | Global color theme change callback |

useSpacemanTheme

Hook to access theme state from SpacemanThemeProvider context.

const { theme, colorTheme, switchTheme, setColorTheme, switchThemeFromElement, ref } =
  useSpacemanTheme()

Returns

| Property | Type | Description | | ------------------------ | ------------------------------------------------------- | ------------------------------------------------- | | theme | Theme | Current theme | | colorTheme | ColorTheme | Current color theme | | switchTheme | (theme: Theme) => Promise<void> | Switch to specific theme | | setColorTheme | (colorTheme: ColorTheme) => void | Set color theme | | switchThemeFromElement | (theme: Theme, element: HTMLElement) => Promise<void> | Switch theme with animation from specific element | | ref | RefObject<HTMLElement> | Ref for animation origin |

useThemeAnimation

Standalone hook for theme management and animations.

const { theme, colorTheme, switchTheme, setColorTheme, toggleTheme, ref } = useThemeAnimation({
  themes: ['light', 'dark', 'system'],
  colorThemes: ['default', 'blue', 'green', 'purple'],
  animationType: ThemeAnimationType.SLIDE,
  duration: 800,
  onThemeChange: theme => console.log('Theme changed:', theme),
  onColorThemeChange: colorTheme => console.log('Color changed:', colorTheme),
})

Options

| Property | Type | Default | Description | | -------------------- | ---------------------------------- | ----------------------------- | --------------------------------------------------- | | themes | Theme[] | ['light', 'dark', 'system'] | Available themes | | colorThemes | ColorTheme[] | ['default'] | Available color themes | | theme | Theme | 'system' | Initial theme | | colorTheme | ColorTheme | 'default' | Initial color theme | | animationType | ThemeAnimationType | ThemeAnimationType.CIRCLE | Animation type. Options: CIRCLE, BLUR_CIRCLE, SLIDE | | duration | number | 500 | Animation duration in ms | | blurAmount | number | 2 | Blur amount for blur-circle animation | | onThemeChange | (theme: Theme) => void | - | Theme change callback | | onColorThemeChange | (colorTheme: ColorTheme) => void | - | Color theme change callback | | slideDirection | SlideDirection | left | Animation slide direction |

Return

| Property | Type | Description | | --------------- | ---------------------------------- | ------------------------- | | theme | Theme | Current theme | | colorTheme | ColorTheme | Current color theme | | switchTheme | (theme: Theme) => Promise<void> | Switch to specific theme | | setColorTheme | (colorTheme: ColorTheme) => void | Set color theme | | toggleTheme | () => Promise<void> | Toggle between light/dark | | ref | RefObject<HTMLElement> | Ref for animation origin |

ThemeSwitcher

Pre-built theme switcher component with animated buttons.

<ThemeSwitcher
  themes={['light', 'dark', 'system']}
  currentTheme="light"
  onThemeChange={theme => console.log(theme)}
  animationType={ThemeAnimationType.CIRCLE}
  duration={600}
  className="custom-class"
/>

ThemeSwitcher Props

| Property | Type | Default | Description | | --------------- | ------------------------ | ----------------------------- | ----------------------------------- | | themes | Theme[] | ['light', 'dark', 'system'] | Available themes | | currentTheme | Theme | - | Controlled current theme (optional) | | onThemeChange | (theme: Theme) => void | - | Theme change callback (optional) | | animationType | ThemeAnimationType | ThemeAnimationType.CIRCLE | Animation type | | duration | number | 500 | Animation duration in ms | | className | string | - | Additional CSS classes |

Note: When used with SpacemanThemeProvider, currentTheme and onThemeChange are automatically handled by the context.

ThemeSelector

Dropdown selector for color themes.

<ThemeSelector
  colorThemes={['default', 'blue', 'green', 'purple']}
  currentColorTheme="blue"
  onColorThemeChange={colorTheme => console.log(colorTheme)}
  animationType={ThemeAnimationType.BLUR_CIRCLE}
  duration={400}
/>

ThemeSelector Props

| Property | Type | Default | Description | | -------------------- | ---------------------------------- | ----------------------------- | ----------------------------------------- | | themes | Theme[] | ['light', 'dark', 'system'] | Available themes (for standalone hook) | | colorThemes | ColorTheme[] | ['default'] | Available color themes | | currentColorTheme | ColorTheme | - | Controlled current color theme (optional) | | onColorThemeChange | (colorTheme: ColorTheme) => void | - | Color theme change callback (optional) | | animationType | ThemeAnimationType | ThemeAnimationType.CIRCLE | Animation type | | duration | number | 500 | Animation duration in ms |

Note: When used with SpacemanThemeProvider, currentColorTheme and onColorThemeChange are automatically handled by the context.

ThemeAnimationType

Animation types for theme transitions.

enum ThemeAnimationType {
  CIRCLE = 'circle',
  BLUR_CIRCLE = 'blur-circle',
}

Types

type Theme = 'light' | 'dark' | 'system'
type ColorTheme = string // e.g., 'default', 'blue', 'green', etc.

interface SpacemanThemeProviderProps {
  children: React.ReactNode
  defaultTheme?: Theme
  defaultColorTheme?: ColorTheme
  themes?: Theme[]
  colorThemes?: ColorTheme[]
  animationType?: ThemeAnimationType
  duration?: number
  blurAmount?: number
  onThemeChange?: (theme: Theme) => void
  onColorThemeChange?: (colorTheme: ColorTheme) => void
}

interface UseThemeAnimationProps {
  themes?: Theme[]
  colorThemes?: ColorTheme[]
  theme?: Theme
  colorTheme?: ColorTheme
  animationType?: ThemeAnimationType
  duration?: number
  blurAmount?: number
  onThemeChange?: (theme: Theme) => void
  onColorThemeChange?: (colorTheme: ColorTheme) => void
}

interface UseThemeAnimationReturn {
  theme: Theme
  colorTheme: ColorTheme
  switchTheme: (theme: Theme) => Promise<void>
  setColorTheme: (colorTheme: ColorTheme) => void
  toggleTheme: () => Promise<void>
  ref: RefObject<HTMLElement>
}

interface ThemeSwitcherProps {
  themes?: Theme[]
  currentTheme?: Theme
  onThemeChange?: (theme: Theme) => void
  animationType?: ThemeAnimationType
  duration?: number
  className?: string
}

interface ThemeSelectorProps {
  themes?: Theme[]
  colorThemes?: ColorTheme[]
  currentColorTheme?: ColorTheme
  onColorThemeChange?: (colorTheme: ColorTheme) => void
  animationType?: ThemeAnimationType
  duration?: number
}


Browser Support

  • View Transitions API: Chrome 111+, Edge 111+
  • Fallback: All modern browsers with CSS transitions
  • Reduced Motion: Respects prefers-reduced-motion setting
  • Framework Support: React 16.8+ (hooks required)

Performance

  • Animations use the View Transitions API when available
  • Optimized for 60fps animations
  • Respects prefers-reduced-motion setting

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

MIT