Theme

Give every surface one light, dark, or system preference and store it in Chrome storage instead of page-local state.

Initialize on mount

The generated surface root initializes the stored preference and applies the resolved theme before the interface renders.

import { initializeTheme } from "@/shared/theme";

await initializeTheme();

Read the current theme

import {
  getThemePreference,
  getSystemTheme,
  resolveThemePreference,
} from "@/shared/theme";

// What the user chose: "light" | "dark" | "system"
const pref = await getThemePreference();

// What the OS is set to: "light" | "dark"
const system = getSystemTheme();

// What's actually applied — resolves "system" to the OS value
const resolved = resolveThemePreference();

Change the theme

import { setThemePreference, applyThemePreference } from "@/shared/theme";

// Persist and apply: "light" | "dark" | "system"
await setThemePreference("dark");

// Apply without persisting (useful for previews)
applyThemePreference("dark");

Subscribe to changes

Subscribe when product code must react beyond CSS. The provider already updates surface styles when the stored preference or operating-system theme changes.

import { subscribeToThemeChange } from "@/shared/theme";

const unsubscribe = subscribeToThemeChange((resolved) => {
  console.log("Theme changed to:", resolved); // "light" | "dark"
});

// Call unsubscribe() when the component unmounts

System mode

System mode follows prefers-color-scheme and updates while the surface is open. Build colors from the shadcn tokens in globals.css so both themes remain coherent.