tweakcn theming
why
a theme tool produces a coherent palette in seconds — exactly why its output must not become authority by paste. values that skip role review arrive without an owner; later theming work cannot tell which token serves canvas, surface, or state. routing candidates through owned semantic roles keeps exploration fast with one accountable token system.
rule
use tweakcn to explore and establish an intentional semantic token system, never to paste a theme in without ownership review.
workflow
- choose the visual role each token serves: canvas, surface, text, action, border, destructive state, data state.
- apply the values in the shared theme source.
- verify contrast, focus, dark mode if supported, real component states.
- remove obsolete token aliases once the system is accepted.
verification
render real component states, not a token swatch alone: default, hover/focus, disabled, error, loading, empty, dense data surfaces where they exist. compare against the declared refinement/redesign decision so a theme update cannot silently change unrelated product identity.
do not
- treat generated theme values as product truth.
- use tweakcn output as a substitute for a component or accessibility review.
good and bad
bad
/* theme.css — values copied from tweakcn without mapping */:root {--slate-50: #f8fafc;--slate-900: #0f172a;--primary: #6750a4;--secondary: #625b71;}/* Button.tsx — no consistency rule */<button className="bg-primary text-slate-50">Submit</button>/* StatusBadge.tsx — uses old palette */<span className="bg-slate-900 text-slate-50">Active</span>/* Card.tsx — uses wrong token */<div className="bg-secondary">Content</div>
tweakcn provided values but not meaning. each component guesses a token. a
later update to --primary (actions only) still hits the card; new components
match inconsistently or reach for raw palette values.
good
/* theme.css — values mapped to semantic roles before adoption */:root {/* visual roles */--action: #6750a4; /* buttons, links, interactive elements */--surface: #fffbfe; /* background layers and containers */--text: #0f172a; /* primary and secondary text */--border: #e2e8f0; /* dividers and outlines */--error: #dc2626; /* destructive and error states */}/* Button.tsx — owns its CVA variants, consumes roles */const button = cva("", {variants: {tone: {primary: "bg-action text-surface",secondary: "bg-surface text-action border border-action",},},});<button className={button({ tone: "primary" })}>Submit</button>/* StatusBadge.tsx — consistent semantic usage */<span className="bg-surface text-text border border-border">Active</span>/* Card.tsx — components never use undocumented tokens */<div className="bg-surface text-text">Content</div>
values become owned semantic roles; components consume them through
variants. updating --action propagates to all buttons; identity stays
coherent.