Theme Toggle
Animated theme switcher with View Transitions API for smooth light/dark mode.
When to Use
Use Theme Toggle when you need to:
- Allow users to switch between light and dark color modes
- Provide a visually polished theme transition with animation
- Add a persistent theme control in headers, sidebars, or settings panels
- Leverage the View Transitions API for smooth full-page transitions
Default
The default theme toggle with circle animation variant.
Animation Variants
Four transition styles using the View Transitions API: circle, circle-blur, polygon, and instant.
Start Positions
Control the origin point for circle and circle-blur animations. The transition expands from the specified position.
With Label
Display a text label alongside the icon showing the current theme name.
Disabled
Disabled state prevents interaction and applies reduced opacity styling.
UX & Design Guidelines
Visual Hierarchy
The theme toggle uses a ghost button variant to remain visually unobtrusive. Place it in secondary UI areas such as headers, toolbars, or settings panels rather than as a primary action. The Sun and Moon icons provide immediate recognition of the toggle purpose.
Spacing & Layout
The icon-only variant renders as a size-9 (36px) square button, making it consistent with other icon buttons. When using showLabel, the button expands to default size with gap-2 spacing between icon and text. Group with other toolbar controls using consistent gap values.
Responsive Behavior
On smaller screens, prefer the icon-only variant without showLabel to conserve horizontal space. The 36px touch target meets minimum requirements for mobile interaction. Consider hiding the toggle on mobile layouts where space is limited and exposing it in a settings menu instead.
Color & Contrast
The toggle inherits the ghost variant styling, ensuring icon contrast against both light and dark backgrounds. The icon transition between Sun and Moon uses rotation and scale animations for a polished feel. The View Transitions API animation works across the full viewport, so test both themes for contrast compliance.
Accessibility
Keyboard Navigation
- Tab — Move focus to/from the toggle button
- Enter or Space — Activate the toggle to switch themes
Screen Reader Support
- Provides dynamic
aria-labelthat updates with the target theme (e.g., "Switch to light mode") - Includes a
sr-onlyspan with "Toggle theme" text for additional context - When
showLabelis enabled, visible text provides supplementary information - Disabled state is announced via the native
disabledattribute
Focus Management
Inherits the Button component's focus ring with focus-visible:ring-[3px]. Focus is preserved after the theme transition completes. The View Transitions API does not disrupt focus state, so users can continue navigating after toggling.
Motion Preferences
When the user has prefers-reduced-motion enabled, consider using the instant variant to skip view transition animations. Browsers that do not support the View Transitions API automatically fall back to an instant toggle with no animation.
ARIA Attributes
{/* Default — aria-label is set automatically */}
<ThemeToggle />
{/* Renders as: */}
<button aria-label="Switch to light mode">
<Moon className="size-5" />
<span className="sr-only">Toggle theme</span>
</button>
{/* With label — visible text provides context */}
<ThemeToggle showLabel />
{/* Renders as: */}
<button aria-label="Switch to dark mode">
<Sun className="size-5" />
<span>Light</span>
<span className="sr-only">Toggle theme</span>
</button>
{/* Disabled — disabled attribute prevents interaction */}
<ThemeToggle disabled />Related Components
API Reference
The ThemeToggle component accepts the following props in addition to standard button attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "circle" | "circle-blur" | "polygon" | "instant" | "circle" | The animation style used for the theme transition. Uses the View Transitions API. |
| start | "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center" | The origin position for circle and circle-blur animations. |
| showLabel | boolean | false | When true, displays a text label ("Light" or "Dark") next to the icon. |
| disabled | boolean | false | When true, prevents user interaction and applies disabled styles. |
| className | string | — | Additional CSS classes to apply to the toggle button. |