Theme Toggler
Animated theme toggle using the View Transitions API with configurable clip-path shapes and origin.
import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler /> </div> )}Installation
Section titled “Installation”pnpm dlx shadcn@latest add @magicui-solid/animated-theme-togglerbunx shadcn@latest add @magicui-solid/animated-theme-toggleryarn dlx shadcn@latest add @magicui-solid/animated-theme-togglernpx shadcn@latest add @magicui-solid/animated-theme-togglerImport
Section titled “Import”import { AnimatedThemeToggler } from '@/components/magicui/animated-theme-toggler'<AnimatedThemeToggler />Use variant to change the clip-path shape of the view transition (circle, square, triangle, diamond, rectangle, hexagon, or star):
<AnimatedThemeToggler variant="star" />Expand from the viewport center instead of the button with fromCenter:
<AnimatedThemeToggler variant="hexagon" duration={600} fromCenter />Examples
Section titled “Examples”The preview at the top of this page uses the default circle reveal. Below, each variant has its own isolated preview so you can test one shape at a time.
Square
Section titled “Square”import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerSquareDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="square" /> </div> )}Diamond
Section titled “Diamond”import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerDiamondDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="diamond" /> </div> )}Rectangle
Section titled “Rectangle”import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerRectangleDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="rectangle" /> </div> )}Hexagon
Section titled “Hexagon”import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerHexagonDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="hexagon" /> </div> )}Triangle
Section titled “Triangle”import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerTriangleDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="triangle" /> </div> )}import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerStarDemo() { return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler variant="star" /> </div> )}Controlled
Section titled “Controlled”Pass theme and onThemeChange to keep the parent theme state in sync on toggle. When controlled, the component skips localStorage and lets the parent own persistence.
import { createSignal, onCleanup, onMount } from 'solid-js'
import { AnimatedThemeToggler } from '@/registry/magicui/animated-theme-toggler'
export function AnimatedThemeTogglerControlledDemo() { const [theme, setTheme] = createSignal<'light' | 'dark'>('dark')
onMount(() => { const root = document.documentElement const update = () => { setTheme(root.dataset.theme === 'dark' ? 'dark' : 'light') } update()
const observer = new MutationObserver(update) observer.observe(root, { attributes: true, attributeFilter: ['data-theme'] })
onCleanup(() => observer.disconnect()) })
return ( <div class="flex justify-center p-6"> <AnimatedThemeToggler theme={theme()} onThemeChange={(t) => { document.documentElement.dataset.theme = t localStorage.setItem('starlight-theme', t) setTheme(t) }} /> </div> )}The component sets --magicui-theme-toggle-vt-duration and a data-magicui-theme-vt="active" flag on <html> only while a toggle is in flight, so the clip-path animation stays in sync with the view-transition group without affecting any other view transitions in your app.
Add the following CSS to your global stylesheet:
html[data-magicui-theme-vt="active"]::view-transition-group(root) { animation-duration: var(--magicui-theme-toggle-vt-duration);}| Prop | Type | Default | Description |
|---|---|---|---|
class |
string |
- |
Additional classes for the button |
duration |
number |
400 |
Duration of the theme transition animation in milliseconds |
variant |
TransitionVariant |
"circle" |
Shape used for the view-transition clip-path reveal |
fromCenter |
boolean |
false |
If true, the clip expands from the viewport center instead of the button |
theme |
"light" / "dark" |
- |
Controlled theme value. When set, the parent owns persistence. |
onThemeChange |
(theme: "light" / "dark") => void |
- |
Called on toggle. Pair with theme for controlled usage. |
Export TransitionVariant from the same module when you need typed props or menus.
Credits
Section titled “Credits”- Credit to Nazam Kalsi, chishiyac, dikshantgulekar20-oss
Original:
magicui.design/docs/components/animated-theme-toggler