Web / Customization
Product theme
Customize kit product chrome for your SaaS — without editing the marketing brand theme.
Setup and configuration
Marketing brand and product theme are separate files. Edit apps/web/app/app-theme.css for dashboard, auth, settings, billing, chat, and org chrome. Add color packs in apps/web/src/theme/catalog.ts. Extend density, radius, font, and layout axes in apps/web/src/theme/presets.ts plus matching html[data-*] rules in app-theme.css. Pick the marketing identity in apps/web/src/theme/marketing-theme.ts (MARKETING_THEME_MODE: locked | inherit | a pack id) instead of editing apps/web/app/theme.css — that file's lock via .marketing-theme:not(.app-theme):not([data-theme]):not([data-marketing-theme="inherit"]) keeps the supastack Obsidian homepage brand by default.
At runtime, Settings → Appearance → ThemePicker stores color, density, radius, font, and layout in browser cookie + localStorage (not an account field). The same page mounts the kit UI gallery so packs and presets live-update buttons, inputs, cards, badges, and dialogs. theme-boot.ts restores all axes before paint. Design detail and copy-paste examples live in docs/design/kit-product-theme.md and docs/design/kit-ui-gallery.md.
/* Edit product chrome here — not theme.css */
.app-theme {
--radius-card: 14px;
background: var(--page);
color: var(--text);
}
.app-theme .button-primary {
background: var(--accent);
color: var(--accent-ink);
}
/* catalog.ts — append a ThemeDefinition with every THEME_TOKEN_NAMES entry */
{
id: "coral",
name: "Coral",
colorScheme: "light",
tokens: { "--page": "#fff7f4", "--accent": "#e06b4f" /* …full token set… */ }
}Implementation workflow
Prefer var(--page|--section|--raised|--text|--muted|--border|--accent|…) in app consumers (app-theme.css, globals.css aliases, WorkspaceShell routes). Do not reintroduce teal/pale hex shells or hardcode Obsidian hex into product UI.
When adding a pack, keep contrast.test.ts green. When adding a preset id, update both presets.ts and the html[data-density|data-radius|data-font|data-layout] remaps in app-theme.css so ThemePicker and FOUC boot stay aligned.
Preserve the shared four-tile brand geometry in marketing/brand-geometry.ts for marketing surfaces. Header marks stay transparent; favicon/app icons keep the rounded black backing. Pure marketing pages keep looking Obsidian while MARKETING_THEME_MODE stays "locked" (the kit default); products may pin another pack or inherit the visitor pick.
Verification and troubleshooting
After editing kit theme tokens or packs, verify ThemePicker on /settings/appearance updates the kit UI gallery plus /dashboard, /auth, /billing, /chat, and org shells. Confirm / stays on the Obsidian marketing lock (unless MARKETING_THEME_MODE says otherwise). Run theme unit contracts (catalog, presets, contrast, theme-ownership, kit-ui-gallery) and application-theme Playwright when available. Check keyboard focus, contrast, 320px, and reduced motion.