@codecademy/gamut 68.6.1-alpha.e6c390.0 → 68.6.1-alpha.f6b2ce.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-tools/.cursor-plugin/plugin.json +1 -1
- package/agent-tools/DESIGN.Codecademy.md +239 -191
- package/agent-tools/DESIGN.LXStudio.md +236 -184
- package/agent-tools/DESIGN.Percipio.md +232 -182
- package/agent-tools/DESIGN.md +1 -1
- package/agent-tools/commands/gamut-review.md +176 -87
- package/agent-tools/guidelines/components/animations.md +74 -0
- package/agent-tools/guidelines/components/buttons.md +74 -23
- package/agent-tools/guidelines/components/card.md +19 -0
- package/agent-tools/guidelines/components/coachmark.md +21 -0
- package/agent-tools/guidelines/components/data-table.md +79 -0
- package/agent-tools/guidelines/components/forms.md +106 -0
- package/agent-tools/guidelines/components/loading-states.md +17 -0
- package/agent-tools/guidelines/components/menu.md +58 -0
- package/agent-tools/guidelines/components/overview.md +97 -17
- package/agent-tools/guidelines/components/radial-progress.md +13 -0
- package/agent-tools/guidelines/components/select.md +23 -0
- package/agent-tools/guidelines/components/tooltips.md +22 -0
- package/agent-tools/guidelines/components/video.md +29 -0
- package/agent-tools/guidelines/foundations/color.md +140 -58
- package/agent-tools/guidelines/foundations/modes.md +39 -17
- package/agent-tools/guidelines/foundations/spacing.md +78 -37
- package/agent-tools/guidelines/foundations/typography.md +69 -37
- package/agent-tools/guidelines/overview-icons.md +19 -0
- package/agent-tools/guidelines/overview-illustrations.md +7 -0
- package/agent-tools/guidelines/overview-patterns.md +7 -0
- package/agent-tools/guidelines/overview.md +69 -23
- package/agent-tools/guidelines/setup.md +59 -18
- package/agent-tools/rules/accessibility.mdc +22 -13
- package/agent-tools/skills/gamut-accessibility/SKILL.md +97 -112
- package/agent-tools/skills/gamut-color-mode/SKILL.md +79 -29
- package/agent-tools/skills/gamut-components/SKILL.md +46 -0
- package/agent-tools/skills/gamut-forms/SKILL.md +101 -0
- package/agent-tools/skills/gamut-style-utilities/SKILL.md +111 -0
- package/agent-tools/skills/gamut-system-props/SKILL.md +70 -26
- package/agent-tools/skills/gamut-testing/SKILL.md +106 -62
- package/agent-tools/skills/gamut-theming/SKILL.md +34 -86
- package/agent-tools/skills/gamut-typography/SKILL.md +36 -80
- package/bin/commands/plugin/install.mjs +96 -56
- package/bin/commands/plugin/list.mjs +11 -43
- package/bin/commands/plugin/remove.mjs +30 -38
- package/bin/commands/plugin/update.mjs +15 -5
- package/bin/gamut.mjs +17 -13
- package/bin/lib/design.mjs +71 -0
- package/bin/lib/io.mjs +14 -0
- package/package.json +6 -6
- package/bin/lib/figma.mjs +0 -49
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Audit Gamut usage before shipping — DESIGN.md, dependencies, GamutProvider, imports, hex colors, tests, and pre-ship design guardrails — with a consolidated report and skill pointers.
|
|
3
3
|
argument-hint: [path]
|
|
4
4
|
allowed-tools: Read Glob Grep
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
This is an audit of
|
|
7
|
+
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.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
When `DESIGN.md` is present at the audit root, use it as the authoritative reference for product design intent, token names, and component patterns. It is copied from `DESIGN.Codecademy.md`, `DESIGN.Percipio.md`, or `DESIGN.LXStudio.md` in `@codecademy/gamut` agent-tools (via `gamut plugin install --theme <name>`). When a finding maps to a skill, note it in the report so the developer knows where to get remediation guidance.
|
|
10
10
|
|
|
11
|
-
Run
|
|
11
|
+
Run Check 0 first, then Checks 1–6, then print a single consolidated report using the format at the end of this file.
|
|
12
|
+
|
|
13
|
+
Before reporting: Read [`guidelines/validation-checklist.md`](../guidelines/validation-checklist.md) and any [`guidelines/components/`](../guidelines/components/) guides relevant to findings (e.g. `data-table.md`, `menu.md`, `forms.md`).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## How to run
|
|
18
|
+
|
|
19
|
+
| Step | Guidance |
|
|
20
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| Install plugin | Cursor: `gamut plugin install` (or `gamut plugin install cursor`) from `@codecademy/gamut`. Claude Code: `gamut plugin install claude`. Use `--theme core`, `percipio`, or `lxstudio` to copy `DESIGN.md` to the app repo root. |
|
|
22
|
+
| When | Before marking Gamut UI work final, before large PRs, when onboarding a codebase |
|
|
23
|
+
| Where | App repo root (directory containing `DESIGN.md`), or pass a subpath: `/gamut-review packages/web` |
|
|
24
|
+
| In editor | Slash command `/gamut-review` (Cursor and Claude Code after plugin install; Claude may need `/reload-plugins` once) |
|
|
25
|
+
| Severity | Errors (`✗`) are blocking — fix before ship. Warnings (`⚠`) are recommended follow-ups. |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Check 0 — DESIGN.md present
|
|
30
|
+
|
|
31
|
+
Resolve the audit root: `$ARGUMENTS` if provided, otherwise the current working directory. Look for `DESIGN.md` at that root (not inside `node_modules` or package subfolders unless the audit path is explicitly that folder).
|
|
32
|
+
|
|
33
|
+
| Result | Action |
|
|
34
|
+
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| Found | Report `✓ DESIGN.md present (<path>)`. Proceed with Checks 1–6 using this file for product/theme context. Infer product (Codecademy, Percipio, LX Studio) from `DESIGN.md` content for Check 6 theme-specific manual items. |
|
|
36
|
+
| Missing | Report `✗ DESIGN.md not found` as a blocking finding. Include remediation: from the repo root run `gamut plugin install cursor --theme core` (or `percipio`, `lxstudio`, `admin`, `platform`; also `claude`), or manually copy the matching `DESIGN.*.md` from `@codecademy/gamut` agent-tools and rename to `DESIGN.md`. Still run Checks 1–3 and 5. For Check 4, list hex violations with `palette:` / `semantic:` only where Appendix A/B apply without product YAML — prefix the Hardcoded colors section with `⚠ low confidence — no DESIGN.md` and do not assume Codecademy Core semantics; do not use Appendix B shortcuts as authoritative. |
|
|
12
37
|
|
|
13
38
|
---
|
|
14
39
|
|
|
@@ -16,11 +41,11 @@ Run all five checks below, then print a single consolidated report using the for
|
|
|
16
41
|
|
|
17
42
|
Read `package.json` (and `package.json` in `$ARGUMENTS` if a path was given). Inspect `dependencies`, `devDependencies`, and `peerDependencies` combined.
|
|
18
43
|
|
|
19
|
-
| Package
|
|
20
|
-
|
|
21
|
-
| `@codecademy/gamut`
|
|
22
|
-
| `@codecademy/gamut-styles` | Recommended — design tokens and theme primitives
|
|
23
|
-
| `@codecademy/variance`
|
|
44
|
+
| Package | Expectation |
|
|
45
|
+
| -------------------------- | ------------------------------------------------------- |
|
|
46
|
+
| `@codecademy/gamut` | Required — core component library |
|
|
47
|
+
| `@codecademy/gamut-styles` | Recommended — design tokens and theme primitives |
|
|
48
|
+
| `@codecademy/variance` | Recommended — style-prop system used by Gamut internals |
|
|
24
49
|
|
|
25
50
|
---
|
|
26
51
|
|
|
@@ -28,11 +53,11 @@ Read `package.json` (and `package.json` in `$ARGUMENTS` if a path was given). In
|
|
|
28
53
|
|
|
29
54
|
Search source files (`.ts`, `.tsx`, `.js`, `.jsx`) for these symbols. Skip `node_modules`, `dist`, `.next`, `build`, `.turbo`.
|
|
30
55
|
|
|
31
|
-
| Symbol
|
|
32
|
-
|
|
33
|
-
| `GamutProvider` |
|
|
34
|
-
| `ColorMode`
|
|
35
|
-
| `Background`
|
|
56
|
+
| Symbol | Expectation |
|
|
57
|
+
| --------------- | ------------------------------------------------------------------- |
|
|
58
|
+
| `GamutProvider` | Required — must appear at least once (app root wrapper) |
|
|
59
|
+
| `ColorMode` | Recommended — enables semantic light/dark theming |
|
|
60
|
+
| `Background` | Recommended — semantic surface color via `@codecademy/gamut-styles` |
|
|
36
61
|
|
|
37
62
|
For each found symbol report the first file path where it appears.
|
|
38
63
|
|
|
@@ -42,89 +67,147 @@ For each found symbol report the first file path where it appears.
|
|
|
42
67
|
|
|
43
68
|
Grep source files for any of these patterns. Each match is an error.
|
|
44
69
|
|
|
45
|
-
| Pattern
|
|
46
|
-
|
|
47
|
-
| `@codecademy/gamut/dist/`
|
|
48
|
-
| `@codecademy/gamut/src/`
|
|
49
|
-
| `@codecademy/gamut-styles/src/` | Deep src import — use the package root
|
|
50
|
-
| `@codecademy/variance/src/`
|
|
70
|
+
| Pattern | Reason |
|
|
71
|
+
| ------------------------------- | ----------------------------------------------------------------------- |
|
|
72
|
+
| `@codecademy/gamut/dist/` | Deep dist import — bypasses public API and breaks on internal refactors |
|
|
73
|
+
| `@codecademy/gamut/src/` | Deep src import — not part of the published package |
|
|
74
|
+
| `@codecademy/gamut-styles/src/` | Deep src import — use the package root |
|
|
75
|
+
| `@codecademy/variance/src/` | Deep src import — use the package root |
|
|
51
76
|
|
|
52
77
|
Report each violation as `file:line`.
|
|
53
78
|
|
|
54
79
|
---
|
|
55
80
|
|
|
56
|
-
## Check 4 — Hardcoded colors
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
## Check 4 — Hardcoded colors (semantic-first)
|
|
82
|
+
|
|
83
|
+
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).
|
|
84
|
+
|
|
85
|
+
Align findings with project docs and Storybook:
|
|
86
|
+
|
|
87
|
+
- [@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.
|
|
88
|
+
- [Foundations / ColorMode](https://gamut.codecademy.com/?path=/docs-foundations-colormode--page) — aliases per mode; `<Background>` behavior.
|
|
89
|
+
- [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) — semantic colors + `css` / `variant` / `states` from `gamut-styles`.
|
|
90
|
+
- Foundations / Theme stories (Core, Admin, Platform, Percipio, LX Studio) — verify hex ↔ semantic if the product is not Codecademy Core.
|
|
91
|
+
|
|
92
|
+
Theme context: If Check 0 passed, infer product/theme from root `DESIGN.md` and `GamutProvider` / app config. If Check 0 failed, follow the low-confidence rules in Check 0 — do not assume Codecademy Core semantics. If `DESIGN.md` exists but theme is still unclear, add a report note to confirm against the correct theme Storybook page.
|
|
93
|
+
|
|
94
|
+
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).
|
|
95
|
+
|
|
96
|
+
### Workflow (each hex match)
|
|
97
|
+
|
|
98
|
+
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).
|
|
99
|
+
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.
|
|
100
|
+
3. Recommend semantic first — Use Appendix B (Core light literals from `color.md`) plus role:
|
|
101
|
+
- Body / UI foreground → `text`; strong emphasis → `text-accent`.
|
|
102
|
+
- Page or card fill → `background` / `background-primary` / state surfaces (`background-success`, `background-warning`, `background-error`).
|
|
103
|
+
- CTAs, links, hyper accents → `primary` (+ `primary-hover` on hover); toggles / checkboxes → `interface`.
|
|
104
|
+
- Ghost / secondary buttons → `secondary`.
|
|
105
|
+
- Destructive → `danger` / `danger-hover`.
|
|
106
|
+
- Dividers / outlines → `border-primary` / `border-secondary` / `border-tertiary`.
|
|
107
|
+
- Inline feedback copy → `feedback-error` / `feedback-success` / `feedback-warning`.
|
|
108
|
+
- Disambiguation: `#FFD300` — warning copy → `feedback-warning`; yellow accent on top of primary-colored surfaces → `primary-inverse`.
|
|
109
|
+
- Same hex can map to multiple semantics (e.g. `#10162F` → `text` vs `border-primary` vs `secondary`): pick from property + component role.
|
|
110
|
+
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.
|
|
111
|
+
|
|
112
|
+
Severity: Hex on adaptive UI (random wrappers, `styled-components`, inline `style`) → error. Hex inside documented exceptions → warning with note.
|
|
113
|
+
|
|
114
|
+
Reporting: For each match outside token definition files:
|
|
115
|
+
|
|
116
|
+
`file:line 'HEX' → semantic: <token(s)> | palette: <token> | note: <theme/disambiguation>`
|
|
117
|
+
|
|
118
|
+
Use `semantic: (n/a)` only when no semantic applies (e.g. pure illustration); still give `palette: …`. If unmappable: `→ no Gamut token`.
|
|
119
|
+
|
|
120
|
+
Ignore hex inside design token definition files (e.g. `variables/colors.ts`, `_colors.scss`) — source of truth, not violations.
|
|
121
|
+
|
|
122
|
+
### Appendix B — Core light: hex → suggested semantic (shortcut)
|
|
123
|
+
|
|
124
|
+
Use with step 3; verify for non-Core themes. Opacity variants in `color.md` are not listed here — keep using the named semantic token.
|
|
125
|
+
|
|
126
|
+
| Hex (normalized) | Typical semantic direction |
|
|
127
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
128
|
+
| `#10162f` | `text`, `border-primary`, or `secondary` (by role) |
|
|
129
|
+
| `#0a0d1c` | `text-accent` |
|
|
130
|
+
| `#ffffff` | `background` (fills), `secondary` (inverse ghost on dark — rare in light-only grep context) |
|
|
131
|
+
| `#fff0e5` | `background-primary` |
|
|
132
|
+
| `#f5ffe3` | `background-success` |
|
|
133
|
+
| `#fffae5` | `background-warning` |
|
|
134
|
+
| `#fbf1f0` | `background-error` |
|
|
135
|
+
| `#3a10e5` | `primary`, `interface` (controls vs marketing CTA — prefer `primary` for links/buttons) |
|
|
136
|
+
| `#5533ff` | `primary-hover`, `interface-hover` |
|
|
137
|
+
| `#ffd300` | `feedback-warning` or `primary-inverse` (see disambiguation above) |
|
|
138
|
+
| `#cca900` | Often pairs with hover in dark mode; in light UI as literal hex → check palette appendix (`yellow-400`) then assign role |
|
|
139
|
+
| `#e91c11` | `danger` |
|
|
140
|
+
| `#be1809` | `danger-hover`, `feedback-error` |
|
|
141
|
+
| `#008a27` | `feedback-success` |
|
|
142
|
+
|
|
143
|
+
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`).
|
|
144
|
+
|
|
145
|
+
### Appendix A — Hex → palette token (identification only)
|
|
146
|
+
|
|
147
|
+
Case-insensitive. Use to label `palette:` in the report; do not stop at this step without Appendix B / role triage.
|
|
148
|
+
|
|
149
|
+
| Hex | Token |
|
|
150
|
+
| --------- | -------------------------- |
|
|
151
|
+
| `#000000` | `black` |
|
|
152
|
+
| `#ffffff` | `white` |
|
|
153
|
+
| `#10162f` | `navy` / `navy-800` |
|
|
154
|
+
| `#0a0d1c` | `navy-900` |
|
|
155
|
+
| `#fff0e5` | `beige-100` |
|
|
156
|
+
| `#f5fcff` | `blue-0` |
|
|
157
|
+
| `#d3f2ff` | `blue-100` |
|
|
158
|
+
| `#66c4ff` | `blue-300` |
|
|
159
|
+
| `#3388ff` | `blue-400` |
|
|
160
|
+
| `#1557ff` | `blue-500` |
|
|
161
|
+
| `#1d2340` | `blue-800` |
|
|
162
|
+
| `#f5ffe3` | `green-0` |
|
|
163
|
+
| `#eafdc6` | `green-100` |
|
|
75
164
|
| `#aee938` | `green-400` / `lightGreen` |
|
|
76
|
-
| `#008a27` | `green-700`
|
|
77
|
-
| `#151c07` | `green-900`
|
|
78
|
-
| `#fffae5` | `yellow-0`
|
|
79
|
-
| `#cca900` | `yellow-400`
|
|
80
|
-
| `#ffd300` | `yellow-500` / `yellow`
|
|
81
|
-
| `#211b00` | `yellow-900`
|
|
82
|
-
| `#fff5ff` | `pink-0`
|
|
83
|
-
| `#f966ff` | `pink-400` / `pink`
|
|
84
|
-
| `#fbf1f0` | `red-0`
|
|
85
|
-
| `#e85d7f` | `red-300`
|
|
86
|
-
| `#dc5879` | `red-400` / `paleRed`
|
|
87
|
-
| `#e91c11` | `red-500` / `red`
|
|
88
|
-
| `#be1809` | `red-600`
|
|
89
|
-
| `#280503` | `red-900`
|
|
90
|
-
| `#ffe8cc` | `orange-100`
|
|
91
|
-
| `#ff8c00` | `orange-500` / `orange`
|
|
92
|
-
| `#5533ff` | `hyper-400`
|
|
93
|
-
| `#3a10e5` | `hyper-500` / `hyper`
|
|
94
|
-
| `#f5f5f5` | `gray-100`
|
|
95
|
-
| `#eeeeee` | `gray-200`
|
|
96
|
-
| `#e0e0e0` | `gray-300`
|
|
97
|
-
| `#9e9e9e` | `gray-600`
|
|
98
|
-
| `#616161` | `gray-800`
|
|
99
|
-
| `#424242` | `gray-900`
|
|
100
|
-
| `#fffbf8` | `beige-0`
|
|
101
|
-
| `#8a7300` | `gold-800` / `gold`
|
|
102
|
-
| `#d14900` | `orange-800`
|
|
103
|
-
| `#ca00d1` | `pink-800`
|
|
104
|
-
| `#006d82` | `teal-500` / `teal`
|
|
105
|
-
| `#b3ccff` | `purple-300` / `purple`
|
|
106
|
-
|
|
107
|
-
Ignore hex values inside design token definition files themselves (e.g. `variables/colors.ts`, `_colors.scss`) — those are the source of truth, not violations.
|
|
108
|
-
|
|
109
|
-
For each match outside token files report: `file:line 'HEX' → token` (or `→ no Gamut token` if unknown).
|
|
165
|
+
| `#008a27` | `green-700` |
|
|
166
|
+
| `#151c07` | `green-900` |
|
|
167
|
+
| `#fffae5` | `yellow-0` |
|
|
168
|
+
| `#cca900` | `yellow-400` |
|
|
169
|
+
| `#ffd300` | `yellow-500` / `yellow` |
|
|
170
|
+
| `#211b00` | `yellow-900` |
|
|
171
|
+
| `#fff5ff` | `pink-0` |
|
|
172
|
+
| `#f966ff` | `pink-400` / `pink` |
|
|
173
|
+
| `#fbf1f0` | `red-0` |
|
|
174
|
+
| `#e85d7f` | `red-300` |
|
|
175
|
+
| `#dc5879` | `red-400` / `paleRed` |
|
|
176
|
+
| `#e91c11` | `red-500` / `red` |
|
|
177
|
+
| `#be1809` | `red-600` |
|
|
178
|
+
| `#280503` | `red-900` |
|
|
179
|
+
| `#ffe8cc` | `orange-100` |
|
|
180
|
+
| `#ff8c00` | `orange-500` / `orange` |
|
|
181
|
+
| `#5533ff` | `hyper-400` |
|
|
182
|
+
| `#3a10e5` | `hyper-500` / `hyper` |
|
|
183
|
+
| `#f5f5f5` | `gray-100` |
|
|
184
|
+
| `#eeeeee` | `gray-200` |
|
|
185
|
+
| `#e0e0e0` | `gray-300` |
|
|
186
|
+
| `#9e9e9e` | `gray-600` |
|
|
187
|
+
| `#616161` | `gray-800` |
|
|
188
|
+
| `#424242` | `gray-900` |
|
|
189
|
+
| `#fffbf8` | `beige-0` |
|
|
190
|
+
| `#8a7300` | `gold-800` / `gold` |
|
|
191
|
+
| `#d14900` | `orange-800` |
|
|
192
|
+
| `#ca00d1` | `pink-800` |
|
|
193
|
+
| `#006d82` | `teal-500` / `teal` |
|
|
194
|
+
| `#b3ccff` | `purple-300` / `purple` |
|
|
110
195
|
|
|
111
196
|
---
|
|
112
197
|
|
|
113
198
|
## Check 5 — Test setup
|
|
114
199
|
|
|
115
|
-
Grep test files (
|
|
200
|
+
Grep test files (`/__tests__//*.{ts,tsx}`, `**/*.test.{ts,tsx}`, `**/*.spec.{ts,tsx}`) for these patterns. Skip `node_modules`, `dist`.
|
|
116
201
|
|
|
117
|
-
| Pattern
|
|
118
|
-
|
|
119
|
-
| `jest.mock\(.*@codecademy/gamut`
|
|
120
|
-
| `jest.mock\(.*@codecademy/gamut-styles`
|
|
121
|
-
| `from '@codecademy/gamut-tests'`
|
|
122
|
-
| `from 'component-test-setup'` (without gamut-tests)
|
|
123
|
-
| `new GamutProvider` or `<GamutProvider` in test files |
|
|
202
|
+
| Pattern | Verdict | Reason |
|
|
203
|
+
| ----------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `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 |
|
|
205
|
+
| `jest.mock\(.*@codecademy/gamut-styles` | Error | Same issue as above — mocking gamut-styles breaks token resolution |
|
|
206
|
+
| `from '@codecademy/gamut-tests'` | Good — report count of files using it | Correct import for `setupRtl` and `MockGamutProvider` |
|
|
207
|
+
| `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 |
|
|
208
|
+
| `new GamutProvider` or `<GamutProvider` in test files | Warning | Prefer `setupRtl`; use `MockGamutProvider` (sets `useCache={false}`, `useGlobals={false}`) in harnesses or stories, not `GamutProvider` directly |
|
|
124
209
|
|
|
125
|
-
Skill reference for remediation: `gamut-testing`
|
|
126
|
-
|
|
127
|
-
---
|
|
210
|
+
## Skill reference for remediation: `gamut-testing`
|
|
128
211
|
|
|
129
212
|
## Output format
|
|
130
213
|
|
|
@@ -132,6 +215,10 @@ Skill reference for remediation: `gamut-testing`
|
|
|
132
215
|
Gamut Review — <absolute path>
|
|
133
216
|
══════════════════════════════════════════════════
|
|
134
217
|
|
|
218
|
+
DESIGN.md
|
|
219
|
+
✓ present <path>/DESIGN.md
|
|
220
|
+
✗ missing run: gamut plugin install cursor --theme <core|percipio|lxstudio|…> [blocking for color audit]
|
|
221
|
+
|
|
135
222
|
Dependencies
|
|
136
223
|
✓ @codecademy/gamut <version>
|
|
137
224
|
⚠ @codecademy/gamut-styles not found — recommended
|
|
@@ -149,17 +236,19 @@ Import patterns
|
|
|
149
236
|
src/Other.tsx:12
|
|
150
237
|
|
|
151
238
|
Hardcoded colors [→ gamut-color-mode]
|
|
152
|
-
|
|
153
|
-
⚠ src/
|
|
239
|
+
✗ src/Card.tsx:22 '#10162F' → semantic: text | palette: navy-800 | note: Core light body copy
|
|
240
|
+
⚠ src/Hero.tsx:14 '#1557FF' → semantic: primary (if link/CTA) | palette: blue-500 | note: no exact semantic; confirm theme
|
|
241
|
+
⚠ src/Nav.tsx:8 '#BADA55' → semantic: (n/a) | palette: — | note: no Gamut token
|
|
154
242
|
|
|
155
243
|
Test setup [→ gamut-testing]
|
|
156
244
|
✓ @codecademy/gamut-tests used in 12 test files
|
|
157
|
-
✗ jest.mock(@codecademy/gamut) 2 occurrences — remove
|
|
245
|
+
✗ jest.mock(@codecademy/gamut) 2 occurrences — remove; prefer setupRtl (or harness + setupRtl)
|
|
158
246
|
src/components/Foo/__tests__/Foo.test.tsx:3
|
|
159
247
|
src/components/Bar/__tests__/Bar.test.tsx:5
|
|
160
248
|
⚠ direct component-test-setup import 1 occurrence — import from @codecademy/gamut-tests
|
|
161
249
|
src/components/Baz/__tests__/Baz.test.tsx:2
|
|
162
250
|
|
|
251
|
+
|
|
163
252
|
══════════════════════════════════════════════════
|
|
164
253
|
<N> error(s), <N> warning(s) found. (or "All checks passed." if none)
|
|
165
254
|
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Animations
|
|
2
|
+
|
|
3
|
+
`Animation` is a namespace of controlled containers from `@codecademy/gamut` — one primitive per pattern. Use these instead of raw CSS keyframes, inline `transition`, or third-party motion libraries when a Gamut primitive covers the case.
|
|
4
|
+
|
|
5
|
+
Storybook: [Atoms / Animation](https://gamut.codecademy.com/?path=/docs-atoms-animation--docs)
|
|
6
|
+
|
|
7
|
+
## Cross-cutting rule: animations contain content, not click targets
|
|
8
|
+
|
|
9
|
+
Never put `onClick` on the animation container. The action element (`FillButton`, `StrokeButton`, etc.) wraps the animation; the primitive wraps the visual content that animates.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// Wrong
|
|
13
|
+
<Rotation onClick={handleClick}>
|
|
14
|
+
<MiniChevronDownIcon />
|
|
15
|
+
</Rotation>
|
|
16
|
+
|
|
17
|
+
// Right
|
|
18
|
+
<StrokeButton onClick={handleClick}>
|
|
19
|
+
<Rotation>
|
|
20
|
+
<MiniChevronDownIcon />
|
|
21
|
+
</Rotation>
|
|
22
|
+
</StrokeButton>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Primitives
|
|
26
|
+
|
|
27
|
+
### `Rotation`
|
|
28
|
+
|
|
29
|
+
Rotates children — common for expand/collapse chevrons.
|
|
30
|
+
|
|
31
|
+
Props: `rotated`, `degrees` (default `180`), `height`, `width`, `aria-hidden`, `children`
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<StrokeButton onClick={toggleExpanded}>
|
|
35
|
+
<Rotation rotated={isExpanded}>
|
|
36
|
+
<MiniChevronDownIcon />
|
|
37
|
+
</Rotation>
|
|
38
|
+
</StrokeButton>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `ExpandInCollapseOut`
|
|
42
|
+
|
|
43
|
+
Expand/collapse via Framer Motion. Required: wrap in `<AnimatePresence>` and conditionally render.
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { AnimatePresence } from 'framer-motion';
|
|
47
|
+
|
|
48
|
+
<AnimatePresence>
|
|
49
|
+
{isExpanded && <ExpandInCollapseOut>{/* content */}</ExpandInCollapseOut>}
|
|
50
|
+
</AnimatePresence>;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `FadeInSlideOut`
|
|
54
|
+
|
|
55
|
+
Fade in / slide out via Framer Motion. Required: `<AnimatePresence>` + conditional mount — toggling CSS does not trigger the animation.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<AnimatePresence>
|
|
59
|
+
{isVisible && (
|
|
60
|
+
<FadeInSlideOut>
|
|
61
|
+
<Box border={1} p={8}>
|
|
62
|
+
{/* content */}
|
|
63
|
+
</Box>
|
|
64
|
+
</FadeInSlideOut>
|
|
65
|
+
)}
|
|
66
|
+
</AnimatePresence>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
1. Prefer Gamut animation primitives over raw keyframes or ad-hoc motion libraries.
|
|
72
|
+
2. Never put click handlers on animation containers.
|
|
73
|
+
3. Enter/exit: `AnimatePresence` + conditional render — not CSS show/hide.
|
|
74
|
+
4. Import `AnimatePresence` from `framer-motion` (peer dependency via Gamut).
|
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
# Buttons
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
There is no generic `Button` export from `@codecademy/gamut`. Never import `Button` — use the specific variant that matches the design.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
Agent reference: which button component and which `variant` to use. Colors are wired inside each atom — consumers do not pass `color`, `bg`, hex, or semantic token names on stock buttons.
|
|
6
|
+
|
|
7
|
+
Related docs: [overview.md](../overview.md) (reading order) · [foundations/color.md](../foundations/color.md) and `gamut-color-mode` skill — semantic tokens for custom styled controls only, not stock button atoms · [foundations/modes.md](../foundations/modes.md) — ColorMode / `<Background>` when placing buttons on colored surfaces
|
|
8
|
+
|
|
9
|
+
Storybook (UX source of truth):
|
|
10
|
+
|
|
11
|
+
- [Atoms / Buttons / Button](https://gamut.codecademy.com/?path=/docs-atoms-buttons-button--docs) — variants, light/dark examples
|
|
12
|
+
- [FillButton](https://gamut.codecademy.com/?path=/docs-atoms-buttons-fillbutton--docs) · [StrokeButton](https://gamut.codecademy.com/?path=/docs-atoms-buttons-strokebutton--docs) · [TextButton](https://gamut.codecademy.com/?path=/docs-atoms-buttons-textbutton--docs) · [IconButton](https://gamut.codecademy.com/?path=/docs-atoms-buttons-iconbutton--docs) · [CTAButton](https://gamut.codecademy.com/?path=/docs-atoms-buttons-ctabutton--docs)
|
|
13
|
+
- [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) — semantic tokens for custom `css` / `variant` / `states`, not prebuilt atoms
|
|
14
|
+
- [UX Writing / Component guidelines / Buttons](https://gamut.codecademy.com/?path=/docs-ux-writing-component-guidelines-buttons--docs) — label copy
|
|
15
|
+
|
|
16
|
+
## Component selection
|
|
17
|
+
|
|
18
|
+
| Component | Use for | Default `variant` |
|
|
19
|
+
| -------------- | --------------------------------------------- | ------------------------------------------------ |
|
|
20
|
+
| `FillButton` | Primary / high-emphasis actions | `primary` |
|
|
21
|
+
| `StrokeButton` | Secondary / outlined actions | `primary` (often pass `secondary` per Storybook) |
|
|
22
|
+
| `TextButton` | Low-emphasis, inline actions | `primary` |
|
|
23
|
+
| `IconButton` | Icon-only; requires `tip` and accessible name | `secondary` |
|
|
24
|
+
| `CTAButton` | Marketing / high-visibility CTA only | `primary` (only option) |
|
|
25
|
+
|
|
26
|
+
## `variant` prop
|
|
27
|
+
|
|
28
|
+
Shared by `FillButton`, `StrokeButton`, `TextButton`, and `IconButton`. `CTAButton` only supports `primary`.
|
|
29
|
+
|
|
30
|
+
| `variant` | Typical use |
|
|
31
|
+
| ----------- | ------------------------------ |
|
|
32
|
+
| `primary` | Submit, main CTA |
|
|
33
|
+
| `secondary` | Close, cancel, low priority |
|
|
34
|
+
| `danger` | Destructive actions |
|
|
35
|
+
| `interface` | Controls styled like UI chrome |
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<FillButton variant="primary">Submit</FillButton>
|
|
39
|
+
<StrokeButton variant="secondary">Cancel</StrokeButton>
|
|
40
|
+
<IconButton icon={SearchIcon} tip="Search" variant="secondary" />
|
|
41
|
+
<CTAButton>Try Pro for free</CTAButton>
|
|
42
|
+
```
|
|
13
43
|
|
|
14
44
|
## Sizes
|
|
15
45
|
|
|
@@ -17,28 +47,49 @@
|
|
|
17
47
|
|
|
18
48
|
## Key props
|
|
19
49
|
|
|
20
|
-
| Prop
|
|
21
|
-
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
50
|
+
| Prop | Type | Effect |
|
|
51
|
+
| -------------- | ----------------------------------------------------- | ------------------------------------------------ |
|
|
52
|
+
| `variant` | `"primary" \| "secondary" \| "danger" \| "interface"` | Color emphasis (see table above) |
|
|
53
|
+
| `size` | `"small" \| "normal" \| "large"` | Padding and font size |
|
|
54
|
+
| `icon` | Icon component | Leading or trailing icon (Fill, Stroke, Text) |
|
|
55
|
+
| `iconPosition` | `"left" \| "right"` | Defaults to left |
|
|
56
|
+
| `disabled` | boolean | Disabled state styling |
|
|
57
|
+
| `href` | string | Renders as `<a>` tag |
|
|
58
|
+
| `tip` | string | Required on `IconButton` (tooltip + hover label) |
|
|
27
59
|
|
|
28
60
|
## States
|
|
29
61
|
|
|
30
|
-
|
|
62
|
+
Hover, active, and disabled colors are handled by the component. Do not override state colors with `color` / `bg` props.
|
|
31
63
|
|
|
32
|
-
##
|
|
64
|
+
## Accessibility — `disabled` vs `aria-disabled`
|
|
33
65
|
|
|
66
|
+
| Situation | Use | Why |
|
|
67
|
+
| --------------------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| Button should not be activatable (default) | `disabled` prop | Renders native `disabled` on `<button>`; removed from tab order; correct for most forms and actions |
|
|
69
|
+
| Disabled button with a tooltip that must stay readable on focus | `aria-disabled` only — do not also pass `disabled` | Native `disabled` blocks keyboard focus, so the tooltip cannot be reached. Gamut disabled styles also match `[aria-disabled='true']`. See [ToolTip — With a disabled Button](https://gamut.codecademy.com/?path=/docs-molecules-tips-tooltip--docs) |
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// Default — not interactive
|
|
73
|
+
<FillButton disabled>Submit</FillButton>
|
|
74
|
+
|
|
75
|
+
// Disabled but focusable (e.g. wrapped in ToolTip explaining why)
|
|
76
|
+
<ToolTip id="why-disabled" info="Complete the lesson first">
|
|
77
|
+
<FillButton aria-describedby="why-disabled" aria-disabled>
|
|
78
|
+
Submit
|
|
79
|
+
</FillButton>
|
|
80
|
+
</ToolTip>
|
|
34
81
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
82
|
+
|
|
83
|
+
- `href` + `disabled`: `ButtonBase` (internal) drops `href` and renders a `<button disabled>` — link-style buttons cannot stay anchors while disabled.
|
|
84
|
+
- `IconButton`: provide an accessible name via `tip` (used as `aria-label` when `aria-label` is omitted). See ToolTip / IconButton Storybook pages.
|
|
85
|
+
- `ButtonBase` is not exported from `@codecademy/gamut` (only the `ButtonBaseElements` type is). Prefer stock atoms; custom button styling belongs in Gamut itself or via `css` / `variant` from `gamut-styles`, not by importing `ButtonBase`.
|
|
39
86
|
|
|
40
87
|
## Rules
|
|
41
88
|
|
|
42
|
-
-
|
|
89
|
+
- Never set the `mode` prop on buttons — they inherit color context from parent `ColorMode` / `<Background>` wrappers.
|
|
90
|
+
- Match variant to design intent: filled → `FillButton`, outlined → `StrokeButton`, text-only → `TextButton`, icon-only → `IconButton` (with `tip`).
|
|
91
|
+
- Use `FillButton` for primary actions and `StrokeButton` for secondary — do not use both at equal weight on the same screen.
|
|
43
92
|
- Reserve `CTAButton` for marketing / high-visibility promotions; do not use it for standard UI actions.
|
|
93
|
+
- Avoid placing buttons in the wrong color-mode context (e.g. light-mode buttons on a navy band without `<Background>`). See [modes.md](../foundations/modes.md).
|
|
44
94
|
- Every interactive `Card` wrapped in `<Anchor>` should have `isInteractive` — not a button inside.
|
|
95
|
+
- Do not set `color`, `bg`, or hex on stock button components. For custom styled controls, follow [color.md](../foundations/color.md) and `gamut-styles` utilities — do not import internal `ButtonBase`.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Card
|
|
2
|
+
|
|
3
|
+
`Card` accepts specific `variant` values that map to internal color tokens via `polished`'s parser. Invalid variants (e.g. `"navy-on-white"` instead of `"white"`) cause a runtime crash in `parseToHsl()`.
|
|
4
|
+
|
|
5
|
+
Storybook: [Atoms / Card](https://gamut.codecademy.com/?path=/docs-atoms-card--docs)
|
|
6
|
+
|
|
7
|
+
## Valid `Card` variants
|
|
8
|
+
|
|
9
|
+
`"default"`, `"white"`, `"yellow"`, `"hyper"`, `"navy"`
|
|
10
|
+
|
|
11
|
+
Never guess or invent compound variant names. Inspect TypeScript definitions in `@codecademy/gamut` if unsure.
|
|
12
|
+
|
|
13
|
+
## Defaults and props
|
|
14
|
+
|
|
15
|
+
1. Default `shadow` to `"none"` unless the design explicitly calls for elevation.
|
|
16
|
+
2. Pass `isInteractive` when the card is wrapped in `<Anchor>` or acts as a clickable surface — enables hover shadow and `borderRadius: md`.
|
|
17
|
+
3. Default `borderRadius` is `none` — override with the `borderRadius` prop when the design specifies rounding.
|
|
18
|
+
|
|
19
|
+
Confirm semantic surface colors against root `DESIGN.md` and the active theme Storybook page — variants are palette-backed, not arbitrary hex.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Coachmark
|
|
2
|
+
|
|
3
|
+
Storybook: [Molecules / Coachmark](https://gamut.codecademy.com/?path=/docs-molecules-coachmark--docs)
|
|
4
|
+
|
|
5
|
+
## Popovers: `outline` and `CheckerDense` by default
|
|
6
|
+
|
|
7
|
+
Pass `outline: true` and `pattern: CheckerDense` (from `@codecademy/gamut-patterns`) via `popoverProps` unless explicitly overridden.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Coachmark } from '@codecademy/gamut';
|
|
11
|
+
import { CheckerDense } from '@codecademy/gamut-patterns';
|
|
12
|
+
|
|
13
|
+
<Coachmark
|
|
14
|
+
popoverProps={{
|
|
15
|
+
outline: true,
|
|
16
|
+
pattern: CheckerDense,
|
|
17
|
+
}}
|
|
18
|
+
>
|
|
19
|
+
{/* ... */}
|
|
20
|
+
</Coachmark>;
|
|
21
|
+
```
|