@wrksz/themesv2.1.1

Configurator

Pick your options and copy a provider setup that matches them.

Every option maps to a real prop. The preview runs the actual provider, and the code updates as you change things.

Setup

Framework

createThemes binds the provider and hooks to your theme names.

Themes

  • light
  • dark
Default theme

Ignores the stored choice and tracks the OS.

Forced theme

DOM

Attribute
Target
Attribute values

Optional. Leave empty to use the theme name.

Applies only to themes named light or dark.

Storage

Storage
Key

Needs Next.js with cookie or hybrid storage.

Browser

theme-color meta

Extended provider

systemThemeMap resolves the OS preference to your own theme names.

enableSameDocumentSync shares changes between providers with the same key.

Extras

theme …resolved …

<html>

…

Pick a theme above or change the options.

PrimarySecondary

Generated code

app/layout.tsx
import { ThemeProvider } from "@wrksz/themes/next";
import "./globals.css";

export default function RootLayout({
	children,
}: {
	children: React.ReactNode;
}) {
	return (
		<html lang="en" suppressHydrationWarning>
			<body>
				<ThemeProvider disableTransitionOnChange>{children}</ThemeProvider>
			</body>
		</html>
	);
}
components/theme-switch.tsx
"use client";

import { useHydrated, useTheme } from "@wrksz/themes/client";

const options = ["system", "light", "dark"] as const;

export function ThemeSwitch() {
	const { theme, setTheme, forcedTheme } = useTheme<"light" | "dark">();
	const hydrated = useHydrated();

	return (
		<select
			aria-label="Theme"
			disabled={Boolean(forcedTheme)}
			value={hydrated ? (theme ?? options[0]) : options[0]}
			onChange={(event) => setTheme(event.target.value as (typeof options)[number])}
		>
			{options.map((option) => (
				<option key={option} value={option}>
					{option}
				</option>
			))}
		</select>
	);
}
app/globals.css
@import "tailwindcss";

@custom-variant dark (&:where(.dark, .dark *));