Sun Moon Morph
A free React theme toggle where the sun morphs into a crescent moon.
- Automated axe check
- Reduced-motion tested
- SSR build verified
- Motion
- 1 runtime package
Sun Moon Morph is a free React theme toggle for Next.js, styled with Tailwind, that switches between light and dark mode with a spring-physics morph: eight sun rays retract with a 20ms stagger while a mask circle slides in to carve the sun's core into a crescent moon. It is a real button with aria-pressed and an accessible name that announces the action, so it works as a drop-in dark mode switch.
The component is fully controlled: you pass the current theme and receive the next one in onToggle, so persistence, system preference detection and next-themes wiring stay in your app. Only SVG transforms and opacity ever animate, the button box never changes size, and reduced-motion users get an instant icon swap instead of the morph.
Pick your side of the sky
Currently in dark mode.
This panel carries its own scoped theme. The toggle is a controlled button, so your app decides where the theme lives: local state, next-themes or anything else.
Customise
Component settings update the preview and generated code together.
Appearance
Behaviour
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/theme-sun-moonUsage
"use client";
import { useState } from "react";
import { ThemeSunMoon } from "@/components/ui/theme-sun-moon";
export default function Page() {
const [theme, setTheme] = useState<"light" | "dark">("dark");
return <ThemeSunMoon theme={theme} onToggle={setTheme} />;
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| theme | "light" | "dark" | required | Controlled current theme. |
| onToggle | (next: "light" | "dark") => void | required | Called with the next theme when the button is activated. |
| size | number | 40 | Button size in px. The box is fixed; only the icon animates inside it. |
| className | string | undefined | Additional classes merged onto the button. |
Customisation
The toggle consumes the semantic tokens directly: the disc uses bg-background, the icon uses text-foreground via currentColor, and the focus ring uses the ring token. Restyle it by merging utilities through className, for example a brand-coloured icon on a borderless button, and scale it with the size prop.
<ThemeSunMoon
theme={theme}
onToggle={setTheme}
size={48}
className="border-none text-primary"
/>Frequently asked questions
- Is Sun Moon Morph free for commercial use?
- Yes. Sun Moon Morph is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Is Sun Moon Morph accessible and performant?
- Yes. It renders a real button with aria-pressed reflecting the dark state and an accessible name that announces the action, updated per state, with a visible focus ring. The SVG is decorative and hidden from assistive technology. The morph animates only SVG transforms and opacity inside a fixed button box, so there is no layout shift, and users who prefer reduced motion get an instant opacity swap with no morph or stagger.
- How do I customise the Sun Moon Morph toggle?
- Use the size prop to scale the button and pass Tailwind utilities through className to change the disc, border or icon colour. The icon inherits currentColor, so a class such as text-primary retints the whole sun and moon while the semantic tokens keep both themes readable.
- Does Sun Moon Morph work with next-themes or my own theme state?
- Yes. The component is fully controlled through the theme and onToggle props and stores nothing itself. Wire it to useState for a scoped section, or pass the values from next-themes or any context provider to switch your whole app, including persistence and system preference detection handled by your store.