Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Themes

ploxum ships two themes in the first cut, with full light + dark support on the non-brand theme. Themes use a two-layer token model so any tweakcn-generated CSS drops in as a single new file with no translation step.

Included themes

ThemeModesIdentity
cinematicdark-onlyWarm amber on near-black. The locked ploxum brand. NASA mission control × Bloomberg Terminal × Linear polish.
amber-minimallight + darkShadcn-style amber on white / near-black. Light: #ffffff / #262626. Dark: #171717 / #e5e5e5.

The Cinematic theme is locked dark-only: the mode toggle is hidden when it's active. All other themes may opt into a light variant by defining a .dark override block.

Two-layer token model

Every theme defines tokens at two layers inside a single [data-theme="<id>"] selector:

Outer shadcn alias layer

Every theme defines the standard shadcn semantic names — --background, --foreground, --card, --card-foreground, --popover, --popover-foreground, --primary, --primary-foreground, --secondary, --secondary-foreground, --muted, --muted-foreground, --accent, --accent-foreground, --destructive, --destructive-foreground, --border, --input, --ring, --chart-1..5, plus the full sidebar group.

This lets you drop any tweakcn export into packages/tokens/src/themes/<name>.css and have it Just Work alongside the ploxum components.

Inner ploxum derivation layer

Each theme also re-expresses the richer ploxum hierarchy — --ploxum-color-text-tertiary, four amber tones (-default, -bright, -dim, -tint), four surface tiers (-canvas, -surface, -elevated, -overlay), and four status palettes — in terms of the shadcn names. Every shipping component keeps reading --ploxum-color-* and doesn't need to migrate.

Provider API

PloxumProvider accepts two theme-related props alongside the existing density prop:

<PloxumProvider
  theme="amber-minimal"   // "cinematic" | "amber-minimal"; default "cinematic"
  mode="system"           // "light" | "dark" | "system"; default "system"
  density="comfortable"
>
  {children}
</PloxumProvider>

mode is coerced to the theme's single mode when that's the only option (e.g. always "dark" when theme="cinematic"). Selection persists in localStorage under ploxum.theme and ploxum.mode. The provider sets data-theme on <html> and toggles the .dark class to match the resolved mode.

useTheme

Read or set the current theme + mode from any descendant:

import { useTheme } from "@roger-emerson/ploxum-primitives";
 
function Toolbar() {
  const { theme, mode, resolvedMode, setTheme, setMode, themes } = useTheme();
  return (
    <span>
      Current: {theme} · {mode === "system" ? `system → ${resolvedMode}` : mode}
    </span>
  );
}

resolvedMode always resolves "system" to either "light" or "dark" via prefers-color-scheme.

Picker primitives

Three composables ship from @roger-emerson/ploxum-primitives:

<ThemeSwitcher variant="compact" />  {/* dropdown — icon-only chip */}
<ThemeSwitcher variant="full" />     {/* dropdown — labelled with theme name */}
<ModeToggle />                        {/* sun/moon — returns null on single-mode themes */}
<ThemePalette value={theme} onChange={setTheme} />  {/* inline card rail */}

Drop them anywhere inside a <PloxumProvider>. The compact <ThemeSwitcher> + <ModeToggle> pair fits naturally in a TopBar utility cluster. <ThemePalette> is the rail used in the live demo's themes catalog.

Adding a tweakcn theme

Drop a new theme into the workspace in four steps:

  1. Paste the tweakcn CSS into packages/tokens/src/themes/<name>.css. Wrap the light defaults in [data-theme="<name>"] and the dark override in [data-theme="<name>"].dark.
  2. Append the ploxum derivation block to each selector (the template lives in packages/tokens/src/themes/amber-minimal.css).
  3. Add the entry to packages/tokens/src/themes/index.mjs (with id, label, description, modes, defaultMode, swatches) and index.d.ts.
  4. Run pnpm tokens.

The theme is now selectable in <ThemeSwitcher> and renders across every component without a single component change.

Cinematic-only effects

Visual effects that are only correct under the cinematic palette — the bokeh stage wash on the venue canvas, certain ambient gradients — wrap in the .cinematic-only class. A single CSS rule gates them:

:where(:not([data-theme="cinematic"])) .cinematic-only {
  display: none;
}

This lives in apps/demo/src/styles/globals.css and ships as a convention for ploxum consumers. The venue map drops its warm wash under Amber Minimal but keeps zones, gates, stadium rings, and the stage marker — same component, different visual register.

See it in action

The live demo's themes catalog shows the active palette swatches on the left and a live-rendered preview on the right. Switching themes there updates the entire demo instantly with no layout shift.