react-theme-switch-animation
v1.1.0
Published
Beautiful, smooth animations for theme switching in React applications. Features Circle, Blur Circle, QR Scan, Polygon, Polygon Gradient, and Custom GIF animations with TypeScript support.
Maintainers
Readme
🎥 Demo
✨ Six Animation Types Available
- 🔵 Circle: Smooth expanding circle transition
- 🌀 Blur Circle: Circle with elegant blur effect on the edges
- 📱 QR Scan: Scanning line sweeps left to right (like QR code reader)
- 🔷 Polygon: Diagonal wipe that sweeps across the screen
- 📐 Polygon Gradient: Diagonal wipe with a soft gradient edge
- 🎬 Custom GIF: Reveal the new theme through any GIF mask of your choice
📝 Notes
- The hook is only available in the browser environment. So if you use NextJS App router or any other framework that uses Server Components, you should use this hook in a Client Component by adding the directive
use client - Currently works only if the project is using TailwindCSS
- Ensure your project has the necessary TailwindCSS configuration for dark mode
🚀 Features
- 🎨 Multiple Animation Types: Circle, Blur Circle, QR Scan, Polygon, Polygon Gradient, and Custom GIF animations
- ⚡ High Performance: Optimized for high-resolution displays with smooth 60fps animations
- 🎯 View Transition API: Leverages modern browser APIs for seamless transitions
- 📱 Responsive Design: Works perfectly across all device sizes and screen resolutions
- ♿ Accessibility First: Respects
prefers-reduced-motionand provides fallback experiences - 🔧 TypeScript Support: Full TypeScript support for enhanced development experience
- 💾 State Persistence: Uses
localStorageto persist theme state across sessions - 🎛️ Highly Customizable: Configure duration, easing, blur amount, and more
- 🪝 React Hooks: Clean, modern React Hooks API
📦 Installation
Install the package using npm or YARN:
npm install react-theme-switch-animationor
yarn add react-theme-switch-animation📚 Usage
Here’s how to use the useModeAnimation hook in your React component:
'use client'
import React from 'react'
import { useModeAnimation } from 'react-theme-switch-animation'
const MyComponent = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation()
return (
<button ref={ref} onClick={toggleSwitchTheme}>
Toggle Dark Mode (Currently {isDarkMode ? 'Dark' : 'Light'} Mode)
</button>
)
}
export default MyComponent📚 API
useModeAnimation accepts an optional props object with the following properties:
| Property | Type | Default | Description |
| ------------------ | ------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| duration | number | 750 | Duration of the animation in milliseconds (defaults to 1500 for POLYGON_GRADIENT and 2000 for GIF). |
| easing | string | "ease-in-out" | CSS easing for the animation (defaults to an expo curve for POLYGON, POLYGON_GRADIENT, and GIF). |
| pseudoElement | string | "::view-transition-new(root)" | Pseudo-element used for the animation. |
| globalClassName | string | "dark" | Class name to apply to the root element. |
| animationType | ThemeAnimationType | ThemeAnimationType.CIRCLE | Type of animation effect (CIRCLE, BLUR_CIRCLE, QR_SCAN, POLYGON, POLYGON_GRADIENT, GIF) |
| blurAmount | number | 2 | Blur intensity for blur circle animation. |
| gifUrl | string | undefined | URL of the GIF used as the reveal mask (required for the GIF animation type). |
| styleId | string | "theme-switch-style" | ID for the injected style element (blur circle, polygon gradient, and gif animations). |
| isDarkMode | boolean | false | Initial dark mode state. |
| onDarkModeChange | (isDark: boolean) => void | undefined | Callback function to handle dark mode change. |
Animation Types
The hook supports six types of animations:
ThemeAnimationType.CIRCLE: A clean circle expansion animation (default)ThemeAnimationType.BLUR_CIRCLE: A circle expansion with blur effect on the edgesThemeAnimationType.QR_SCAN: A scanning line that sweeps from left to rightThemeAnimationType.POLYGON: A diagonal wipe — toward dark it sweeps from the top-left corner, back to light from the bottom-rightThemeAnimationType.POLYGON_GRADIENT: A diagonal wipe with a soft gradient edge expanding from the top-left cornerThemeAnimationType.GIF: Reveals the new theme through a custom GIF mask that scales up to fill the screen (requiresgifUrl)
Examples for Each Animation Type
Circle Animation (Default)
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const CircleAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.CIRCLE,
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme
</button>
)
}Blur Circle Animation
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const BlurCircleAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.BLUR_CIRCLE,
blurAmount: 4, // Optional: adjust blur intensity
duration: 1000, // Optional: adjust animation duration
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme (Blur)
</button>
)
}QR Scan Animation
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const QRScanAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.QR_SCAN,
duration: 500, // Faster scan animation
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme (QR Scan)
</button>
)
}Polygon Animation
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const PolygonAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.POLYGON,
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme (Polygon)
</button>
)
}Polygon Gradient Animation
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const PolygonGradientAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.POLYGON_GRADIENT,
duration: 1500, // Optional: defaults to 1500ms for this animation
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme (Polygon Gradient)
</button>
)
}Custom GIF Animation
'use client'
import React from 'react'
import { ThemeAnimationType, useModeAnimation } from 'react-theme-switch-animation'
const GifAnimation = () => {
const { ref, toggleSwitchTheme, isDarkMode } = useModeAnimation({
animationType: ThemeAnimationType.GIF,
gifUrl: 'https://media.tenor.com/cyORI7kwShQAAAAi/shigure-ui-dance.gif', // Any GIF URL
duration: 2000, // Optional: defaults to 2000ms for this animation
})
return (
<button ref={ref} onClick={toggleSwitchTheme}>
{isDarkMode ? '🌙' : '☀️'} Toggle Theme (GIF)
</button>
)
}Tip: GIFs with transparent backgrounds work best as masks — the theme is revealed through the opaque parts of the GIF. If
gifUrlis omitted, the hook falls back to the circle animation.
Returns an object containing:
ref: React ref for attaching to the component that will trigger the dark mode toggle.toggleSwitchTheme: Function to toggle dark mode.isDarkMode: Current state of dark mode (truefor dark,falsefor light).
⚡ Performance & Optimization
This library is built with performance in mind:
- High-Resolution Display Support: Automatically detects and optimizes for displays ≥3000px width/height
- Adaptive Animation Duration: Reduces duration by 20% on high-res displays for smoother experience
- GPU Acceleration: Uses hardware-accelerated CSS properties (
transform,clip-path,mask) - Memory Efficient: Automatic cleanup of style elements and event listeners
- Accessibility: Respects
prefers-reduced-motionfor users who prefer minimal animations
🌐 Browser Support
- Modern Browsers: Chrome 111+, Firefox 103+, Safari 16.4+
- View Transition API: Falls back gracefully on unsupported browsers
- Progressive Enhancement: Basic theme switching works everywhere, animations enhance the experience
📝 Requirements
- React 16.8 or later (for Hooks support)
- TypeScript for compiling the package during installation
- TailwindCSS for styling (ensure dark mode is configured)
🙏 Acknowledgements
The polygon, polygon gradient, and GIF animations are inspired by rudrodip/theme-toggle-effect.
🤝 Contributing
Contributions are welcome! Please open an issue or submit a pull request with your suggested changes.
📜 License
MIT
