> ## Documentation Index
> Fetch the complete documentation index at: https://supastack.dev/llms.txt
> Use this file to discover all available pages before exploring further.
> For the full documentation corpus in one file, fetch: https://supastack.dev/llms-full.txt

# Product theme

Customize kit product chrome for your SaaS — without editing the marketing brand theme.

## Setup and configuration

Before any step, read [Before you start](https://supastack.dev/docs/tanstack/before-you-start.md) and [Terminal basics](https://supastack.dev/docs/tanstack/terminal-basics.md). These instructions assume you opened the full supastack monorepo in VS Code; its root package.json marks the repository root. In pnpm commands, a workspace package is an app or shared-code folder; in the product, a workspace is a team's data container and security boundary. corepack pnpm --filter ... selects one package from the monorepo while commands run from its root. If you opened only a synced mobile or desktop folder, it does not contain the Web backend, Docker setup, or root scripts: run the backend from this monorepo or a reachable hosted server, and use the platform folder's own scripts. An environment variable is a named setting stored in a local .env file or your hosting dashboard; restart the relevant app after changing it, and keep server secrets out of NEXT_PUBLIC_ and EXPO_PUBLIC_ values.

1. In VS Code open packages/web-core/src/app-theme.css for signed-in product screens and packages/web-core/src/theme/catalog.ts to add a color pack. Do not edit packages/web-core/src/theme.css for product colors; it controls the marketing brand.

2. From the repository root run corepack pnpm --filter @supastack/web-tanstack dev, then visit http://localhost:3000/settings/appearance.

3. Change a token in your local branch and refresh the preview. Expected: the dashboard controls update while the homepage keeps its selected marketing theme.

Marketing brand and product theme are separate files. Edit packages/web-core/src/app-theme.css for dashboard, auth, settings, billing, chat, and org chrome. Add color packs in packages/web-core/src/theme/catalog.ts. Extend density, radius, font, and layout axes in packages/web-core/src/theme/presets.ts plus matching html[data-*] rules in app-theme.css. Pick the marketing identity in packages/web-core/src/theme/marketing-theme.ts (MARKETING_THEME_MODE: locked | inherit | a pack id) instead of editing packages/web-core/src/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 so theme-boot.ts restores all axes before first paint, and syncs the signed-in user's full appearance bundle across browsers through user_appearance (GET/PUT /api/account/appearance). The same page mounts the kit UI gallery so packs and presets live-update buttons, inputs, cards, badges, and dialogs. 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… */ }
}
```


## Commands to copy

Follow the comments to see which folder a command runs in. If two terminals are labeled, open two terminal windows and keep the first command running while you use the second.

```
corepack pnpm --filter @supastack/web-tanstack dev

# In a second terminal, after changing a theme token:
corepack pnpm test -- packages/web-core/src/theme/contrast.test.ts
```


## 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

If a color change affects the marketing homepage, check that you edited app-theme.css and review packages/web-core/src/theme/marketing-theme.ts. If text becomes hard to read, restore the previous value and run corepack pnpm test -- packages/web-core/src/theme/contrast.test.ts from the root before trying again.

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.


## Next

[UI kit and blocks](https://supastack.dev/docs/tanstack/ui-kit.md)
