---
name: scaffold-theme
description: Bootstrap src/index.css with Tailwind v4 + Shoelace token overrides + design tokens from the PRD theme slice (or SmartStack defaults). Idempotent overwrite.
group: D
phase: devFrontend
kind: main
section_label: 'SCAFFOLD-THEME (CSS variables + Shoelace tokens for the generated client app)'
allowed-tools: [Read, Write, Edit, Glob, Grep]
---

# scaffold-theme — Theme bootstrap for the generated client app

## Context

You are scaffolding the **theme entry point** of a SmartStack-generated React + Vite + Tailwind v4 + Shoelace client app. Without this file, every component renders with browser defaults: no token-based palette, no dark-mode contract, no Shoelace `--sl-color-*` overrides — the UI looks unbranded and inconsistent across components.

This skill is invoked by `ba-develop` Phase 3, sub-phase 3.0 (theme + layout), BEFORE any pages are scaffolded — so pages can reference design tokens directly via `var(--color-*)` Tailwind classes (e.g. `bg-[var(--color-accent-500)]`).

## Invocation

```
npx --prefer-offline tsx skills/development/frontend/theme/cli/scaffold-theme/index.ts \
  --spec '{"projectPath":"<abs>","appCode":"<app>","prdSlice":<theme-slice-or-null>}'
```

`--spec` is a JSON object:

| Field | Type | Required | Notes |
|---|---|---|---|
| `projectPath` | string | yes | Absolute path to the web root (holds `src/`). |
| `appCode` | string | yes | Lowercased app code (used for branding label only). |
| `prdSlice` | object \| null | no | Optional theme tokens from the PRD. Falls back to SmartStack defaults. |
| `force` | boolean | no | Overwrite even when content matches. Default `false` (no-op when content unchanged). |
| `useShoelace` | boolean | no | Emit the `@shoelace-style/shoelace` light/dark theme imports in `src/index.css`. Default `false` — set to `true` only when the project lists the package as a dependency. |

## What it produces

- **`src/index.css`** — Tailwind v4 entry + Shoelace `--sl-color-*` overrides + design tokens (`--color-accent-*`, `--bg-app`, `--success-*`, `--warning-*`, `--error-*`, `--info-*`) — all rooted on `:root` and overridden under `.dark` for dark-mode contract.
- **Dataviz block** (read by the dashboard primitives): `--dataviz-1..8` (categorical),
  `--chart-grid/-axis/-tooltip-bg/-tooltip-text` (chrome), `--kpi-value/-label/-trend-up/-trend-down`.
  **The platform theme wins**: every slot is emitted as `var(--platform-token, <derived fallback>)` —
  `--dataviz-N: var(--dataviz-cat-N, …)`, `--kpi-trend-up: var(--dataviz-trend-up, var(--success-text))`,
  `--chart-tooltip-bg: var(--dataviz-surface-elevated, var(--bg-card, …))`, … The
  `@atlashub/smartstack` theme runtime (`ThemeContext`) writes `--dataviz-cat-1..12`,
  `--dataviz-trend-*`, `--dataviz-surface*` on `<html>` from the tenant's **UI configuration**,
  so a theme change in the admin UI flows through to the charts; the accent-derived hexes are
  only the fallback for apps that don't run that runtime. Custom category colours:
  pass `theme.dataviz: string[]` (never hardcode a hex in a widget — audit DEV-UI-036 / R28).

## Idempotency contract

- **Re-run safe.** If the existing `src/index.css` has the **exact same content** as what would be generated, the CLI returns `filesCreated: []` and exits 0 (no-op).
- **Overwrite on drift.** If the file exists but content has drifted (manual edits or stale generation), the file is overwritten unless the user marks it with `/* @customised */` at the top — that marker tells the scaffolder to preserve the file.
- The generated file always carries a `/* AUTO-GENERATED — re-run scaffold-theme to refresh. Add @customised at top to opt out. */` header so re-runs are predictable.

## When NOT to use

- Customising specific tokens for a single page → put overrides in a sibling `.css` next to the page, not in `src/index.css`.
- Adding new design tokens that aren't in the PRD theme slice → extend the PRD theme schema first, then re-run; ad-hoc additions in `src/index.css` are wiped on the next re-run.

## Elevation & motion tokens (plan UI 3.3)

`--shadow-card` (resting surfaces) / `--shadow-overlay` (floating surfaces),
redefined under `.dark`; `--motion-fast`/`--motion-normal`/`--motion-ease`.
Primitives read `shadow-[var(--shadow-…)]`; ui-polish R30 flags raw shadow classes.
