Skip to content
Buttons Fancy Stable

SunSwitch

.md

A theme switch drawn as a pixel sun. Hover it for a lens on its true colours; click it and the new theme spreads from the sun in a dithered, grid-aligned wave.

Available for Svelte 5, React, and Vue 3.

Preview

Installation

pnpm add fancy-ui-svelte
import { SunSwitch } from "fancy-ui-svelte";

Usage

svelte
<script lang="ts">
	import { SunSwitch } from "fancy-ui-svelte";
	import { createThemeState } from "$lib/stores/theme.svelte";

	// The sun is the site's theme switch: the wave reveals the real page in its new theme.
	const theme = createThemeState();
</script>

<header class="w-full max-w-2xl">
	<div class="border-foreground/15 flex items-end justify-between gap-6 border-b">
		<div class="min-w-0 pb-5">
			<p class="text-muted-foreground font-mono text-[11px] tracking-[0.2em] uppercase">
				Issue 14 · Late autumn
			</p>
			<h3 class="mt-2 text-3xl leading-tight font-semibold tracking-tight sm:text-4xl">
				Hours of low light
			</h3>
		</div>
		<SunSwitch
			checked={!theme.isDark}
			onCheckedChange={(day) => theme.setTheme(day ? "light" : "dark")}
			label="Day mode"
			sound
			class="shrink-0"
		/>
	</div>
	<p class="text-muted-foreground mt-4 max-w-prose text-sm leading-relaxed">
		Hover the sun to look at it through the lens. Click it, or focus it and press Space, to change
		the light: the new theme spreads from the sun across the whole page.
	</p>
</header>

Examples

Basic Usage

Contained

The wave stays inside one card with `mode="contained"`; the page theme is untouched.

Custom Trigger

Any button can start the wave with `pixelWave()`, from the pointer, with its own palette.

Interactive Playground

Props

PropTypeDefaultDescription
checked booleantrueDay (true) or night (false). Bindable.
onCheckedChange (checked: boolean) => void-Called with the new value from inside the view transition's update callback. Flip your theme here; the component never changes classes on the document. Flip the theme synchronously: the new snapshot is taken as soon as it returns.
radius number10Sun radius in cells. The canvas is 2 × radius + 1 cells square.
cell number5Grid pitch in CSS pixels, shared by the sun, the wave front and its halo.
dot number4Size of each square dot in CSS pixels (at most `cell`).
palette [string, string, string, string]SUN_PALETTEFour colours from the top of the sun to the bottom. The halo reuses them: densest rim in the last colour, sparse fringe in the first.
ink string"currentColor"Monochrome colour of the night sun and the horizon outside the lens. Resolved from the computed colour of the button, so it follows the theme.
nightAlpha number0.42Opacity of the ink sun at night.
horizon booleantrueDraws a few sparse rows of dots under the sun for the lens to reveal.
lens booleantrueOn hover, a soft circular lens follows the pointer and shows the true colours under it.
lensRadius number7Lens radius in cells; about two thirds of the sun reads best.
mode "document" | "contained""document"`document` runs the wave as a view transition over the whole page. `contained` paints it over `scope` only, with a sheet of the old colour that the wave cuts away.
scope HTMLElement | null-The element the wave covers in contained mode. It needs `position: relative` and `overflow: hidden`.
fill string-Colour of the old-state sheet in contained mode and browsers without view transitions. Defaults to the first opaque background found from `scope` (or the body) upwards.
duration number1300Duration of the wave in milliseconds (ease in-out cubic).
haloCells number9Thickness of the dithered halo that leads the wave front, in cells.
hdr booleantrueOn HDR displays with WebGPU, redraws the sun and the wave halo as hard squares brighter than SDR white. Elsewhere nothing is created.
sound booleanfalsePlays the toggle cue (on for day, off for night) through the sound controller, once the user has enabled sound.
label string"Day mode"Accessible name of the switch. Its checked state is announced as on or off.
disabled booleanfalseBlocks interaction.
class string-Additional CSS classes on the button.