---
name: ui-components
description: >
  Verify that a React page (or the whole generated app) respects the SmartStack
  theme — zero hardcoded colors — and auto-fix the violations. Thin orchestrator
  over the ui-polish engine: it does NOT reimplement any audit/fix logic, it runs
  the deterministic ui-polish CLI and then finishes the residual cases (neutral
  colors with no token) that the CLI cannot map mechanically.
argument-hint: "[page-path] [--check|--fix] [--all-rules]"
model: sonnet
allowed-tools: Read, Grep, Glob, Bash, Edit
---

# ui-components — Theme conformity check & auto-fix

Confirms a page uses the design-system tokens instead of hardcoded colors, and
repairs the violations. This is the user-facing entry point; the actual engine is
the colocated **ui-polish** skill (`development/frontend/ui-polish`). This skill
**delegates** to its CLI and never duplicates its rules.

## When to use

- "Vérifie que cette page respecte le thème / qu'il n'y a aucune couleur hardcodée."
- Before committing a page you wrote or edited by hand.
- After scaffolding, to confirm generated pages match the reference theme.

## What it checks (color/theme rule set)

| Rule | What | Auto-fix |
|------|------|----------|
| R14 | Any **saturated** Tailwind color anywhere in `className` (`text-blue-600`, `bg-emerald-500`, `border-rose-400`, …) | → accent ramp / status tokens; **status dots** (tiny `rounded-full`) → `var(--<status>-dot)` |
| R15 | Any **neutral** color (`bg-white`, `bg-gray-100`, `text-gray-500`, …) | backgrounds → `var(--bg-*)`; text/border **reported only** (no token exists) |
| R16 | Status token bound to the wrong utility — `bg-[var(--<status>-text)]` (illegible) / `text-[var(--<status>-bg)]` (invisible) | → flipped back to its role |
| R17 | Status token used as **decoration** — a color-name key (`blue`, `green`, `yellow`, …) aliased to `var(--<status>-…)` in a color map (e.g. metric/stat cards). Semantic misuse the colour rules can't see (token is valid, role is right) | reported (manual — use `--color-accent-*` / neutral surface, keep status for real status) |
| R18 | Tailwind color utilities in **JS data structures** — object values, arrays, config maps (`{ border: 'border-gray-500' }`) outside className | reported (may false-positive on non-rendering strings) |
| R19 | **Hex/rgb/rgba in `style={{ }}`** props — `style={{ backgroundColor: '#6b7280' }}` bypasses the token system | reported |
| R20 | **Accent overuse** on non-CTA elements — thick accent border (`border-2+`), pale accent fill as section background (`accent-50/100`), full accent fill (`shade ≥ 500`) on non-button | reported (design review) |
| R2 | Arbitrary hex (`bg-[#6366f1]`) | reported |
| R3 / R7 / R13 | Context-specific: primary CTA / error banner / status badge | yes |
| R12 | Dark base must be `#161618` | reported |

> Tokens come from `development/frontend/ui-polish/tokens.json`, extracted from the
> SmartStack.app customisation-ui. The generated theme tokenises **accent + status
> + surfaces** only — neutral text/border are intentionally not tokenised, so R15
> flags them for review rather than guessing a wrong token.

## Steps

1. **Locate the web root** — the folder that contains `src/pages` (the generated
   SmartStack web app). If unsure:
   ```bash
   # from the project root
   ls src/pages 2>/dev/null && echo "." || find . -maxdepth 4 -type d -name pages -path "*/src/*" 2>/dev/null
   ```
   Use the parent of `src` as `<web-root>` (often `.`, or `web/<app>-web`).

2. **Verify (audit)** — report every hardcoded color. Pass `--page` to target a
   single page, omit it to scan the whole app:
   ```bash
   npx tsx skills/development/frontend/ui-polish/cli/ui-polish/index.ts \
     --project-path "<web-root>" \
     --mode audit \
     --page "src/pages/<module>/<Entity>ListPage.tsx" \
     --rules R2,R3,R7,R12,R13,R14,R15,R16,R17,R18,R19,R20
   ```
   Read the `report.findings[]` and summarise the violations to the user.

   **New in v1.3.0:** `--page` now also scans 1st-level local imports
   (`@/components/**`, `@/features/**`), so shared component violations are
   visible. Use `--url /app/module/section` to resolve a URL path to its
   component file(s) via PageRegistry instead of specifying the file path.

3. **Fix (apply)** — only if the user asked to correct (or `--fix`). Regeneration
   is **disabled** (`--regenerate-threshold 0`) so a page is never destructively
   rebuilt — only the color utilities are rewritten in place:
   ```bash
   npx tsx skills/development/frontend/ui-polish/cli/ui-polish/index.ts \
     --project-path "<web-root>" \
     --mode apply --regenerate-threshold 0 \
     --page "src/pages/<module>/<Entity>ListPage.tsx" \
     --rules R2,R3,R7,R12,R13,R14,R15,R16,R17
   ```
   Note: R18/R19/R20 are `autoFixable: false` — they appear in audit output but
   `apply` mode only rewrites R2–R17 mechanically. R18/R19/R20 findings require
   manual review and editing.

4. **Review the apply diff** — `git diff` the files the CLI rewrote. The engine
   fixes colors mechanically, but one case is **semantic** and only you can do it,
   plus two need a spot-check:

   - **(a) Categorical badges collapsed onto one accent — the engine CANNOT fix
     this.** R14 maps every brand family (`blue`, `indigo`, `violet`, `purple`,
     `fuchsia`, `pink`) to the **single** accent ramp, because it has no way to
     know which badges represent **distinct categories** that must stay visually
     different. So N category badges that were distinct (`bg-blue-100`,
     `bg-violet-100`, …) all become the same accent. Re-assign a **distinct token
     per category** (from `success` / `warning` / `error` / `info` / `accent`) and
     use the **soft badge pattern** — `bg-[var(--<token>-bg)]
     text-[var(--<token>-text)] border border-[var(--<token>-border)]` (or the
     `.badge-<status>` helper) — never a full accent fill.
   - **(b) Spot-check the status dots (R14).** Tiny `rounded-full` indicators
     should now be `var(--<status>-dot)` (saturated, visible), not
     `var(--<status>-bg)` (pale). The heuristic only fires on `rounded-full` + a
     small size util (`h-2`/`w-2`/`size-2`…); a dot built differently (e.g.
     `rounded` + `p-1`) is missed — fix it by hand to the `-dot` token.
   - **(c) Spot-check R16 token-role flips.** `bg-[var(--*-text)]` → pale surface
     and `text-[var(--*-bg)]` → saturated text are now auto-fixed. Where only the
     `bg` was flipped on a badge/banner, complete the soft triplet (`text-` +
     `border-`).

   Apply these manual edits with `Edit` — this and step 5 are the only places this
   skill touches files directly; the bulk is done by the CLI.

5. **Finish the neutral residuals & re-audit** — re-run step 2. Saturated colors,
   R16 role-misuse and badges should now be `0` errors. Remaining **warnings** are
   neutral `text-*`/`border-*` and raw hex the engine cannot map (no token). For
   each, decide with the user:
   - replace with an existing token if one fits, OR
   - add the missing neutral token via `scaffold-theme` (then it becomes fixable), OR
   - accept it as an **idiomatic neutral** and leave it, noting why — e.g.
     `text-white` on an accent button, `bg-black/50` on a modal overlay.

6. **Validate** — typecheck the app (`npm run typecheck` / `tsc --noEmit` at the
   web root) and confirm the re-audit reports **0 errors**. Present the final
   `git diff` to the user.

## Guardrails

- **Never** edit `tokens.json` or the ui-polish CLI from here — change rules in the
  `development/frontend/ui-polish` skill instead.
- **Never** pass a `--regenerate-threshold` > 0 in this skill — full-page
  regeneration belongs to `ba-develop`, not to a conformity check.
- Default to the color/theme rule subset. Add `--all-rules` (omit `--rules`) only
  when the user wants the full anatomy audit (PageTemplate, slots, i18n, …).
- The CLI is the single source of truth for both detection and mechanical fixes;
  this skill only orchestrates and finishes what the CLI flags as unfixable.
