← All primitives
TokenSwap
ThemingSet data-brand on any wrapper and every descendant re-skins instantly via CSS variables, with a filmic crossfade. One mechanism, unlimited themes.
Requires the --bx-* token palettes and [data-brand] blocks from globals.css — copy them into your own global stylesheet (they are not bundled in the manifest).
$
View raw manifest →npx shadcn@latest add https://prism.icglabs.co/r/token-swap.jsoncomponents/prism/TokenSwap.tsx
"use client";
import { useState } from "react";
/**
* TokenSwap — the highest-leverage internal tool here. Set `data-brand` on any
* wrapper and every descendant that reads the `--bx-*` CSS variables re-skins
* instantly, with a filmic crossfade. One mechanism = unlimited themes. Ported
* from Snupix's brand explorer. The six palettes live in globals.css.
*/
const BRANDS: { id: string; label: string }[] = [
{ id: "prism", label: "Prism" },
{ id: "golden", label: "Golden" },
{ id: "noir", label: "Noir" },
{ id: "romance", label: "Romance" },
{ id: "studio", label: "Studio" },
{ id: "neon", label: "Neon" },
];
export function TokenSwap() {
const [brand, setBrand] = useState("prism");
return (
<div className="w-full">
<div className="mb-5 flex flex-wrap gap-2">
{BRANDS.map((b) => (
<button
key={b.id}
onClick={() => setBrand(b.id)}
className={`rounded-full border px-3.5 py-1.5 text-sm transition-colors ${
brand === b.id
? "border-ink/40 bg-ink/10 text-ink"
: "border-line text-muted hover:border-line-2 hover:text-ink"
}`}
>
{b.label}
</button>
))}
</div>
{/* Everything below re-skins from the data-brand tokens */}
<div
data-brand={brand}
className="grain relative overflow-hidden rounded-2xl p-6 sm:p-8"
style={{ background: "var(--bx-bg)", border: "1px solid var(--bx-line)" }}
>
<div className="flex flex-col gap-5 sm:flex-row sm:items-stretch">
{/* Mock product card */}
<div
className="flex-1 rounded-xl p-5"
style={{ background: "var(--bx-surface)", border: "1px solid var(--bx-line)" }}
>
<div className="mb-4 h-28 w-full rounded-lg" style={{ background: "var(--bx-grad)" }} />
<div
className="mb-2 text-xs font-medium uppercase tracking-widest"
style={{ color: "var(--bx-acc)" }}
>
New release
</div>
<div className="mb-1 text-xl font-semibold" style={{ color: "var(--bx-ink)" }}>
The same markup, six brands
</div>
<p className="mb-4 text-sm" style={{ color: "var(--bx-sub)" }}>
Not a single class changed between themes — only the token layer.
</p>
<div className="flex items-center gap-2">
<button
className="rounded-lg px-4 py-2 text-sm font-medium"
style={{ background: "var(--bx-acc)", color: "var(--bx-acc-ink)" }}
>
Primary action
</button>
<button
className="rounded-lg px-4 py-2 text-sm font-medium"
style={{ border: "1px solid var(--bx-line)", color: "var(--bx-ink)" }}
>
Secondary
</button>
</div>
</div>
{/* Token swatches */}
<div
className="flex w-full flex-col justify-between rounded-xl p-5 sm:w-56"
style={{ background: "var(--bx-surface)", border: "1px solid var(--bx-line)" }}
>
<div className="text-xs font-medium uppercase tracking-widest" style={{ color: "var(--bx-sub)" }}>
Tokens
</div>
<div className="grid grid-cols-4 gap-2 py-4">
{["--bx-bg", "--bx-surface", "--bx-ink", "--bx-sub", "--bx-line", "--bx-acc", "--bx-acc-ink", "--bx-acc2"].map(
(v) => (
<div
key={v}
title={v}
className="aspect-square rounded-md"
style={{ background: `var(${v})`, border: "1px solid var(--bx-line)" }}
/>
)
)}
</div>
<code className="text-xs" style={{ color: "var(--bx-sub)" }}>
data-brand="{brand}"
</code>
</div>
</div>
</div>
</div>
);
}