theme()
Define CSS custom properties at :root level for design tokens.
Signature
Section titled “Signature”function theme(tokens: ThemeTokens, options?: ThemeOptions): void
// Since alpha.9 — exported from "@whisq/core"interface ThemeOptions { /** Suppress the dev-mode "theme() called twice" warning. */ silent?: boolean;}Parameters
Section titled “Parameters”| Param | Type | Description |
|---|---|---|
tokens | ThemeTokens | Object of token groups and values |
options.silent | boolean (optional, since alpha.9) | Suppress the dev-mode duplicate-call warning. Use for legitimate second calls (theme-switching). Default: false. |
Examples
Section titled “Examples”import { theme, sheet } from "@whisq/core";
theme({ color: { primary: "#4386FB", text: "#111827", bg: "#ffffff", }, space: { sm: "0.5rem", md: "1rem", lg: "1.5rem" }, radius: { md: "8px", lg: "12px" },});
// Generated CSS: --color-primary: #4386FB; --space-md: 1rem; etc.
// Use in sheet():sheet({ card: { background: "var(--color-bg)", padding: "var(--space-lg)", borderRadius: "var(--radius-lg)", },});Call-site conventions
Section titled “Call-site conventions”-
Call once, at module scope in your
src/styles.tsfile. The tokens become CSS custom properties on:rootand are available everywhere. Importstyles.tstransitively fromApp.tsso the call runs on first import. -
Duplicate calls = last-call-wins. A second
theme()call replaces the first<style id="whisq-style-whisq-theme">block entirely; the new tokens take effect immediately. This is intentional — it enables theme-switching (e.g.,theme(lightTokens)→theme(darkTokens)in a toggle handler) without leaking old tokens. -
Duplicate-call dev warning (since alpha.9). Accidentally importing two
styles.tsfiles silently wiping the first theme’s variables was the alpha.8 feedback’s top styling footgun. Alpha.9+ emits aconsole.warnin dev when a secondtheme()call would replace an existing block; use{ silent: true }to acknowledge intentional second calls (theme-switching). Production doesn’t emit the warning regardless. See Duplicate-call warning below. -
SSR-safe (since alpha.8). On the server (
typeof document === "undefined"), the call is a no-op — no<style>tag is written. Client-side hydration applies the theme on mount. Previously threwReferenceError: document is not defined.CSS variables won’t appear in server-rendered HTML by themselves; attach theme tokens in a
<style>block in your SSR template until a dedicated server renderer ships.
Duplicate-call warning (since alpha.9)
Section titled “Duplicate-call warning (since alpha.9)”theme() is last-call-wins by design — the second call replaces the first <style> block. Useful for theme-switching; a source of silent bugs when the duplicate is accidental (e.g., two styles.ts files imported transitively by different parts of the app).
Alpha.9 adds a console.warn in dev when a second call would replace an existing block. Two modes:
// Accidental duplicate — dev warns.theme({ color: { primary: "#4386FB" } });
// another-styles.ts (imported from another branch of the graph)theme({ color: { primary: "#ffffff" } }); // Dev: console.warn names the conflict.
// Intentional theme switch — opt out of the warning.const toggleDark = () => { theme(darkTokens, { silent: true }); // No warning.};Detection is DOM-based (getElementById("whisq-style-whisq-theme")). Production builds strip the entire guard via the existing NODE_ENV !== "production" pattern, so there’s no runtime cost in shipped code.
The silent: true option only suppresses the warning — it doesn’t change injection semantics. theme() remains last-call-wins regardless.
See also: /api/sheet/, Styling guide, Common Mistakes → theme variables flipped unexpectedly.
Docs current to v0.1.0-alpha.9 . All releases →