@codecademy/gamut
68.6.068.6.1-alpha.bc8b32.0
agent-tools/commands/gamut-review.md+
agent-tools/commands/gamut-review.mdNew file+231
Index: package/agent-tools/commands/gamut-review.md
===================================================================
--- package/agent-tools/commands/gamut-review.md
+++ package/agent-tools/commands/gamut-review.md
@@ -0,0 +1,231 @@
+---
+description: Use this command when auditing existing code for Gamut usage — dependencies, GamutProvider, deep imports, hardcoded hex colors, and test patterns — and you need a consolidated report with pointers to the matching Gamut skills.
+argument-hint: [path]
+allowed-tools: Read Glob Grep
+---
+
+This is an audit of **existing code** at **`$ARGUMENTS`** (default: current working directory). Your job is to find violations and misuse, not to generate new code.
+
+Use `DESIGN.md` at the project root as the authoritative reference for the product's design intent, token names, and component patterns. This file is distributed as `DESIGN.Codecademy.md` or `DESIGN.Percipio.md` from the `@codecademy/gamut` agent-tools package and renamed to `DESIGN.md` by the consuming project. When a finding maps to a skill, note it in the report so the developer knows where to get remediation guidance.
+
+Run all five checks below, then print a single consolidated report using the format at the end of this file.
+
+---
+
+## Check 1 — Dependencies
+
+Read `package.json` (and `package.json` in `$ARGUMENTS` if a path was given). Inspect `dependencies`, `devDependencies`, and `peerDependencies` combined.
+
+| Package | Expectation |
+| -------------------------- | ------------------------------------------------------- |
+| `@codecademy/gamut` | **Required** — core component library |
+| `@codecademy/gamut-styles` | Recommended — design tokens and theme primitives |
+| `@codecademy/variance` | Recommended — style-prop system used by Gamut internals |
+
+---
+
+## Check 2 — Setup
+
+Search source files (`.ts`, `.tsx`, `.js`, `.jsx`) for these symbols. Skip `node_modules`, `dist`, `.next`, `build`, `.turbo`.
+
+| Symbol | Expectation |
+| --------------- | ------------------------------------------------------------------- |
+| `GamutProvider` | **Required** — must appear at least once (app root wrapper) |
+| `ColorMode` | Recommended — enables semantic light/dark theming |
+| `Background` | Recommended — semantic surface color via `@codecademy/gamut-styles` |
+
+For each found symbol report the first file path where it appears.
+
+---
+
+## Check 3 — Import patterns
+
+Grep source files for any of these patterns. Each match is an error.
+
+| Pattern | Reason |
+| ------------------------------- | ----------------------------------------------------------------------- |
+| `@codecademy/gamut/dist/` | Deep dist import — bypasses public API and breaks on internal refactors |
+| `@codecademy/gamut/src/` | Deep src import — not part of the published package |
+| `@codecademy/gamut-styles/src/` | Deep src import — use the package root |
+| `@codecademy/variance/src/` | Deep src import — use the package root |
+
+Report each violation as `file:line`.
+
+---
+
+## Check 4 — Hardcoded colors (semantic-first)
+
+**Rule:** Inline hex literals in application UI code are violations. Remediation is **not** “replace hex with `navy-800`” — prefer **semantic ColorMode tokens** (`text`, `background`, `primary`, …) so light/dark and theme switches stay correct. Reserve **raw palette tokens** for colors that must stay fixed and for **`bg` on `<Background>`** from `@codecademy/gamut-styles` (section surfaces with content).
+
+Align findings with project docs and Storybook:
+
+- [@codecademy/gamut agent-tools `guidelines/foundations/color.md`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/agent-tools/guidelines/foundations/color.md) — decision guide and semantic tables.
+- [Foundations / ColorMode](https://gamut.codecademy.com/?path=/docs-foundations-colormode--page) — aliases per mode; `<Background>` behavior.
+- [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) — semantic colors + `css` / `variant` / `states` from `gamut-styles`.
+- Foundations / Theme stories (Core, Admin, Platform, Percipio, LX Studio) — verify hex ↔ semantic if the product is not Codecademy Core.
+
+**Theme context:** Infer from `GamutProvider` / app config / root `DESIGN.md` (from `DESIGN.Codecademy.md`, `DESIGN.Percipio.md`, or `DESIGN.LXStudio.md`). If unknown, **assume Codecademy Core** semantics below and add a report note to confirm against the correct theme Storybook page.
+
+**Discovery:** Grep source files (`.ts`, `.tsx`, `.js`, `.jsx`, `.css`, `.scss`, `.less`) for inline hex literals (`#RGB` or `#RRGGBB`). Comparison is case-insensitive. Skip `node_modules`, `dist`, `.next`, `build`, `.turbo` (same spirit as other checks).
+
+### Workflow (each hex match)
+
+1. **Context** — Inspect the surrounding line(s): CSS property (`color`, `background`, `border-color`, …), JSX prop (`color`, `bg`, `borderColor`, SVG fill), or asset. Note whether the subtree is a **section with content** (candidate for `<Background>` + palette `bg`) vs component chrome (prefer semantics).
+2. **Identify palette** — Normalize hex (case-insensitive); map to a Gamut palette name using **Appendix A** below. If missing from the appendix, match against `DESIGN.md` / `packages/gamut-styles` palette definitions.
+3. **Recommend semantic first** — Use **Appendix B** (Core **light** literals from `color.md`) plus role:
+ - Body / UI foreground → `text`; strong emphasis → `text-accent`.
+ - Page or card fill → `background` / `background-primary` / state surfaces (`background-success`, `background-warning`, `background-error`).
+ - CTAs, links, hyper accents → `primary` (+ `primary-hover` on hover); toggles / checkboxes → `interface`.
+ - Ghost / secondary buttons → `secondary`.
+ - Destructive → `danger` / `danger-hover`.
+ - Dividers / outlines → `border-primary` / `border-secondary` / `border-tertiary`.
+ - Inline feedback copy → `feedback-error` / `feedback-success` / `feedback-warning`.
+ - **Disambiguation:** `#FFD300` — warning copy → `feedback-warning`; yellow accent on top of primary-colored surfaces → `primary-inverse`.
+ - Same hex can map to multiple semantics (e.g. `#10162F` → `text` vs `border-primary` vs `secondary`): pick from **property + component role**.
+4. **When palette-only is OK** — **`bg` prop on `<Background>`** (`<Background bg="hyper">`, etc.) is the primary place for **fixed surface** palette colors on sections (see `color.md` decision guide + ColorMode docs). After replacing hex there, use a **named palette token**, not hex. **Exceptions** (flag with rationale): charts/data viz, third-party widgets, exported static illustrations — still prefer tokens over hex when feasible.
+
+**Severity:** Hex on adaptive UI (random wrappers, `styled-components`, inline `style`) → **error**. Hex inside documented exceptions → **warning** with note.
+
+**Reporting:** For each match outside token definition files:
+
+`file:line 'HEX' → semantic: <token(s)> | palette: <token> | note: <theme/disambiguation>`
+
+Use `semantic: (n/a)` only when no semantic applies (e.g. pure illustration); still give `palette: …`. If unmappable: `→ no Gamut token`.
+
+Ignore hex inside design token definition files (e.g. `variables/colors.ts`, `_colors.scss`) — source of truth, not violations.
+
+### Appendix B — Core light: hex → suggested semantic (shortcut)
+
+Use with step 3; verify for non-Core themes. Opacity variants in `color.md` are not listed here — keep using the named semantic token.
+
+| Hex (normalized) | Typical semantic direction |
+| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
+| `#10162f` | `text`, `border-primary`, or `secondary` (by role) |
+| `#0a0d1c` | `text-accent` |
+| `#ffffff` | `background` (fills), `secondary` (inverse ghost on dark — rare in light-only grep context) |
+| `#fff0e5` | `background-primary` |
+| `#f5ffe3` | `background-success` |
+| `#fffae5` | `background-warning` |
+| `#fbf1f0` | `background-error` |
+| `#3a10e5` | `primary`, `interface` (controls vs marketing CTA — prefer `primary` for links/buttons) |
+| `#5533ff` | `primary-hover`, `interface-hover` |
+| `#ffd300` | `feedback-warning` or `primary-inverse` (see disambiguation above) |
+| `#cca900` | Often pairs with hover in dark mode; in light UI as literal hex → check palette appendix (`yellow-400`) then assign role |
+| `#e91c11` | `danger` |
+| `#be1809` | `danger-hover`, `feedback-error` |
+| `#008a27` | `feedback-success` |
+
+Hexes with **no row above** still get **Appendix A** palette id + role-based semantic guess (e.g. blue scale → often decorative or legacy marketing; prefer design review unless mapping clearly to `primary`).
+
+### Appendix A — Hex → palette token (identification only)
+
+Case-insensitive. Use to label `palette:` in the report; **do not** stop at this step without Appendix B / role triage.
+
+| Hex | Token |
+| --------- | -------------------------- |
+| `#000000` | `black` |
+| `#ffffff` | `white` |
+| `#10162f` | `navy` / `navy-800` |
+| `#0a0d1c` | `navy-900` |
+| `#fff0e5` | `beige-100` |
+| `#f5fcff` | `blue-0` |
+| `#d3f2ff` | `blue-100` |
+| `#66c4ff` | `blue-300` |
+| `#3388ff` | `blue-400` |
+| `#1557ff` | `blue-500` |
+| `#1d2340` | `blue-800` |
+| `#f5ffe3` | `green-0` |
+| `#eafdc6` | `green-100` |
+| `#aee938` | `green-400` / `lightGreen` |
+| `#008a27` | `green-700` |
+| `#151c07` | `green-900` |
+| `#fffae5` | `yellow-0` |
+| `#cca900` | `yellow-400` |
+| `#ffd300` | `yellow-500` / `yellow` |
+| `#211b00` | `yellow-900` |
+| `#fff5ff` | `pink-0` |
+| `#f966ff` | `pink-400` / `pink` |
+| `#fbf1f0` | `red-0` |
+| `#e85d7f` | `red-300` |
+| `#dc5879` | `red-400` / `paleRed` |
+| `#e91c11` | `red-500` / `red` |
+| `#be1809` | `red-600` |
+| `#280503` | `red-900` |
+| `#ffe8cc` | `orange-100` |
+| `#ff8c00` | `orange-500` / `orange` |
+| `#5533ff` | `hyper-400` |
+| `#3a10e5` | `hyper-500` / `hyper` |
+| `#f5f5f5` | `gray-100` |
+| `#eeeeee` | `gray-200` |
+| `#e0e0e0` | `gray-300` |
+| `#9e9e9e` | `gray-600` |
+| `#616161` | `gray-800` |
+| `#424242` | `gray-900` |
+| `#fffbf8` | `beige-0` |
+| `#8a7300` | `gold-800` / `gold` |
+| `#d14900` | `orange-800` |
+| `#ca00d1` | `pink-800` |
+| `#006d82` | `teal-500` / `teal` |
+| `#b3ccff` | `purple-300` / `purple` |
+
+---
+
+## Check 5 — Test setup
+
+Grep test files (`**/__tests__/**/*.{ts,tsx}`, `**/*.test.{ts,tsx}`, `**/*.spec.{ts,tsx}`) for these patterns. Skip `node_modules`, `dist`.
+
+| Pattern | Verdict | Reason |
+| ----------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `jest.mock\(.*@codecademy/gamut` | **Error** | Manual mocking bypasses theme context and produces false-positive tests; prefer **`setupRtl`** from `@codecademy/gamut-tests` (or a harness + **`setupRtl`**); use raw **`MockGamutProvider`** + **`render`** only for rare one-offs or Storybook mocks |
+| `jest.mock\(.*@codecademy/gamut-styles` | **Error** | Same issue as above — mocking gamut-styles breaks token resolution |
+| `from '@codecademy/gamut-tests'` | Good — report count of files using it | Correct import for `setupRtl` and `MockGamutProvider` |
+| `from 'component-test-setup'` (without gamut-tests) | **Warning** | Should import `setupRtl` from `@codecademy/gamut-tests`, not directly from `component-test-setup` — the gamut-tests wrapper adds `MockGamutProvider` automatically |
+| `new GamutProvider` or `<GamutProvider` in test files | **Warning** | Prefer **`setupRtl`**; use **`MockGamutProvider`** (sets `useCache={false}`, `useGlobals={false}`) in harnesses or stories, not **`GamutProvider`** directly |
+
+Skill reference for remediation: `gamut-testing`
+
+---
+
+## Output format
+
+```
+Gamut Review — <absolute path>
+══════════════════════════════════════════════════
+
+Dependencies
+ ✓ @codecademy/gamut <version>
+ ⚠ @codecademy/gamut-styles not found — recommended
+ ✗ @codecademy/variance not found — recommended
+
+Setup
+ ✓ GamutProvider found (src/App.tsx)
+ ⚠ ColorMode not found — use ColorMode for light/dark theming [→ gamut-color-mode]
+ ⚠ Background not found — use <Background> for semantic surfaces [→ gamut-color-mode]
+
+Import patterns
+ ✓ Deep dist imports none found
+ ✗ Deep src imports 2 occurrences
+ src/Thing.tsx:7
+ src/Other.tsx:12
+
+Hardcoded colors [→ gamut-color-mode]
+ ✗ src/Card.tsx:22 '#10162F' → semantic: text | palette: navy-800 | note: Core light body copy
+ ⚠ src/Hero.tsx:14 '#1557FF' → semantic: primary (if link/CTA) | palette: blue-500 | note: no exact semantic; confirm theme
+ ⚠ src/Nav.tsx:8 '#BADA55' → semantic: (n/a) | palette: — | note: no Gamut token
+
+Test setup [→ gamut-testing]
+ ✓ @codecademy/gamut-tests used in 12 test files
+ ✗ jest.mock(@codecademy/gamut) 2 occurrences — remove; prefer setupRtl (or harness + setupRtl)
+ src/components/Foo/__tests__/Foo.test.tsx:3
+ src/components/Bar/__tests__/Bar.test.tsx:5
+ ⚠ direct component-test-setup import 1 occurrence — import from @codecademy/gamut-tests
+ src/components/Baz/__tests__/Baz.test.tsx:2
+
+══════════════════════════════════════════════════
+<N> error(s), <N> warning(s) found. (or "All checks passed." if none)
+```
+
+Icons: `✓` = pass, `⚠` = warning (recommended, not required), `✗` = error (required).
+`[→ skill-name]` annotations indicate which Gamut skill has remediation guidance for that category.
+
+After printing the report, offer one sentence of prioritized next-step advice based on what was found.