Theme Toggle

Open in v0

Last updated: September 20, 2026

A light/dark/system theme switch with four genuinely different treatments — a carved sun-moon morph, a segmented pill, a sky-to-night switch and a lit orb.

Installation

npx smoothui-cli add theme-toggle

Features

Four variants built from different primitives so they never look like the same grey pill. Binary and three-way modes, three sizes, controlled or uncontrolled — and it never writes to document.documentElement or localStorage. Wire onThemeChange to your own theme provider (e.g. next-themes).

Variants

Each treatment is a different control. Scroll the sections below — the panel shows one at a time.

Sun Moon

A single filled disc carved by a masking circle that slides across it. The disc radius, the mask radius and the mask position all animate together, so the sun grows into a crescent instead of crossfading two icons. Eight rays retract into the disc with a stagger while the whole ray group rotates.

Pill

A segmented control with icons and labels. The active thumb is a layoutId element with real elevation (drop shadow plus a hairline ring), so it physically travels between segments, and each icon shifts to its own hue as the thumb lands under it (amber for light, indigo for dark).

Switch

A real track-and-knob switch. The track cross-fades a sky gradient into a night gradient, has an inset shadow for depth, and fades in tiny stars on the dark end. The knob is a gradient sphere with a specular highlight and a layered shadow; it travels on a spring while the icon inside rotates through the transition.

Orb

A lit sphere: radial-gradient body, a specular highlight that travels across the surface as the theme changes, craters that fade in on the dark side, an inset terminator shadow and a soft coloured bloom cast beneath it.

Modes

Binary

showSystem={false} — light and dark only. Best for a header chip that should never land on “system”.

Three-way

showSystem (the default) — light, dark and system. On sun-moon and orb the control cycles; on pill and switch system is a third segment / stop.

Accessibility

pill and switch expose their options directly as a role="radiogroup" of radio buttons (the switch places invisible full-height radio hit areas over the track). sun-moon and orb are single-shape controls, so they render one <button> that cycles to the next option and announces the change through a polite live region.

Keyboard Interactions

KeyDescription
Space / EnterActivates the focused control — selects a radio, or advances the cycling button
Arrow Left / Arrow UpMoves to the previous option
Arrow Right / Arrow DownMoves to the next option
Home / EndJumps to the first / last option (pill and switch)

ARIA Attributes

AttributeElementPurpose
role="radiogroup"ContainerIdentifies the pill / switch option set
role="radio" / aria-checkedEach optionExposes each theme option's selection state
aria-pressedCycling buttonExposes the on/off state of sun-moon / orb in binary mode
aria-labelButton(s)Names the option, or — for the cycling button — the current theme and the one it will switch to
aria-live="polite"Status textAnnounces the newly selected theme after any change

Screen Reader

  • Every decorative layer (mask, rays, glow, thumb, stars, knob highlight, orb bloom, craters, specular) is aria-hidden and non-focusable.
  • Every interactive element is a real <button>, so pressed/checked state is announced automatically.

Reduced Motion

This component respects the prefers-reduced-motion media query via useReducedMotion from Motion. When reduced motion is preferred, the mask morph, ray retraction, thumb travel, knob spring, icon rotation, star fade, specular travel and bloom colour change all resolve instantly with no transition.

Props

Created by

Improved by

Eduardo Calvo's avatar

Powered by

motion.dev

© 2026 SmoothUI. Built by Eduardo Calvo.