@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.
Files changed (47) hide show
  1. package/agent-tools/.cursor-plugin/plugin.json +1 -1
  2. package/agent-tools/DESIGN.Codecademy.md +239 -191
  3. package/agent-tools/DESIGN.LXStudio.md +236 -184
  4. package/agent-tools/DESIGN.Percipio.md +232 -182
  5. package/agent-tools/DESIGN.md +1 -1
  6. package/agent-tools/commands/gamut-review.md +176 -87
  7. package/agent-tools/guidelines/components/animations.md +74 -0
  8. package/agent-tools/guidelines/components/buttons.md +74 -23
  9. package/agent-tools/guidelines/components/card.md +19 -0
  10. package/agent-tools/guidelines/components/coachmark.md +21 -0
  11. package/agent-tools/guidelines/components/data-table.md +79 -0
  12. package/agent-tools/guidelines/components/forms.md +106 -0
  13. package/agent-tools/guidelines/components/loading-states.md +17 -0
  14. package/agent-tools/guidelines/components/menu.md +58 -0
  15. package/agent-tools/guidelines/components/overview.md +97 -17
  16. package/agent-tools/guidelines/components/radial-progress.md +13 -0
  17. package/agent-tools/guidelines/components/select.md +23 -0
  18. package/agent-tools/guidelines/components/tooltips.md +22 -0
  19. package/agent-tools/guidelines/components/video.md +29 -0
  20. package/agent-tools/guidelines/foundations/color.md +140 -58
  21. package/agent-tools/guidelines/foundations/modes.md +39 -17
  22. package/agent-tools/guidelines/foundations/spacing.md +78 -37
  23. package/agent-tools/guidelines/foundations/typography.md +69 -37
  24. package/agent-tools/guidelines/overview-icons.md +19 -0
  25. package/agent-tools/guidelines/overview-illustrations.md +7 -0
  26. package/agent-tools/guidelines/overview-patterns.md +7 -0
  27. package/agent-tools/guidelines/overview.md +69 -23
  28. package/agent-tools/guidelines/setup.md +59 -18
  29. package/agent-tools/rules/accessibility.mdc +22 -13
  30. package/agent-tools/skills/gamut-accessibility/SKILL.md +97 -112
  31. package/agent-tools/skills/gamut-color-mode/SKILL.md +79 -29
  32. package/agent-tools/skills/gamut-components/SKILL.md +46 -0
  33. package/agent-tools/skills/gamut-forms/SKILL.md +101 -0
  34. package/agent-tools/skills/gamut-style-utilities/SKILL.md +111 -0
  35. package/agent-tools/skills/gamut-system-props/SKILL.md +70 -26
  36. package/agent-tools/skills/gamut-testing/SKILL.md +106 -62
  37. package/agent-tools/skills/gamut-theming/SKILL.md +34 -86
  38. package/agent-tools/skills/gamut-typography/SKILL.md +36 -80
  39. package/bin/commands/plugin/install.mjs +96 -56
  40. package/bin/commands/plugin/list.mjs +11 -43
  41. package/bin/commands/plugin/remove.mjs +30 -38
  42. package/bin/commands/plugin/update.mjs +15 -5
  43. package/bin/gamut.mjs +17 -13
  44. package/bin/lib/design.mjs +71 -0
  45. package/bin/lib/io.mjs +14 -0
  46. package/package.json +6 -6
  47. package/bin/lib/figma.mjs +0 -49
@@ -3,17 +3,17 @@ version: alpha
3
3
  name: Codecademy Design System
4
4
  description: Design tokens for codecademy.com
5
5
  colors:
6
- # palette — reference hex for docs/tools; in product UI use semantic colors via Gamut theme (Emotion) / system props or Figma tokens—never paste these literals into code
6
+ # palette — raw swatches; set once on :root and then always reference by token name, never use hex values directly in code
7
7
  hyper-500: '#3A10E5'
8
8
  hyper-400: '#5533FF'
9
9
  navy-900: '#0A0D1C'
10
10
  navy-800: '#10162F'
11
- navy-700: '#31374C'
12
- navy-600: '#4C5063'
13
- navy-500: '#686C7C'
14
- navy-300: '#BCBEC5'
15
- navy-200: '#E2E3E6'
16
- navy-100: '#F5F6F7'
11
+ navy-700: 'rgba(16, 22, 47, 0.86)'
12
+ navy-600: 'rgba(16, 22, 47, 0.75)'
13
+ navy-500: 'rgba(16, 22, 47, 0.63)'
14
+ navy-300: 'rgba(16, 22, 47, 0.28)'
15
+ navy-200: 'rgba(16, 22, 47, 0.12)'
16
+ navy-100: 'rgba(16, 22, 47, 0.04)'
17
17
  yellow-500: '#FFD300'
18
18
  yellow-400: '#CCA900'
19
19
  yellow-0: '#FFFAE5'
@@ -50,8 +50,6 @@ colors:
50
50
  primary-inverse: '{colors.yellow-500}'
51
51
  secondary: '{colors.navy-800}'
52
52
  secondary-hover: '{colors.navy-700}'
53
- interface: '{colors.hyper-500}'
54
- interface-hover: '{colors.hyper-400}'
55
53
  danger: '{colors.red-500}'
56
54
  danger-hover: '{colors.red-600}'
57
55
  feedback-error: '{colors.red-600}'
@@ -79,11 +77,29 @@ typography:
79
77
  fontSize: '2.125rem'
80
78
  fontWeight: '700'
81
79
  lineHeight: '1.2'
82
- hankenGrotesk:
83
- fontFamily: '"Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
84
80
  monospace:
85
81
  fontFamily: 'Monaco, Menlo, "Ubuntu Mono", "Droid Sans Mono", Consolas, monospace'
86
- rounded:
82
+ system:
83
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
84
+ '14':
85
+ fontSize: '0.875rem'
86
+ '16':
87
+ fontSize: '1rem'
88
+ '18':
89
+ fontSize: '1.125rem'
90
+ '20':
91
+ fontSize: '1.25rem'
92
+ '22':
93
+ fontSize: '1.375rem'
94
+ '26':
95
+ fontSize: '1.625rem'
96
+ '34':
97
+ fontSize: '2.125rem'
98
+ '44':
99
+ fontSize: '2.75rem'
100
+ '64':
101
+ fontSize: '4rem'
102
+ borderRadii:
87
103
  none: '0px'
88
104
  sm: '2px'
89
105
  md: '4px'
@@ -106,29 +122,36 @@ components:
106
122
  FillButton:
107
123
  backgroundColor: '{colors.primary}'
108
124
  textColor: '{colors.white}'
109
- rounded: '{rounded.md}'
125
+ borderRadii: '{borderRadii.md}'
110
126
  FillButton-hover:
111
127
  backgroundColor: '{colors.primary-hover}'
112
128
  textColor: '{colors.white}'
113
129
  FillButton-danger:
114
130
  backgroundColor: '{colors.danger}'
115
131
  textColor: '{colors.white}'
116
- rounded: '{rounded.md}'
132
+ borderRadii: '{borderRadii.md}'
117
133
  FillButton-danger-hover:
118
134
  backgroundColor: '{colors.danger-hover}'
119
135
  textColor: '{colors.white}'
120
136
  StrokeButton:
121
137
  backgroundColor: 'transparent'
122
138
  textColor: '{colors.secondary}'
123
- rounded: '{rounded.md}'
139
+ borderRadii: '{borderRadii.md}'
140
+ StrokeButton-hover:
141
+ textColor: '{colors.secondary-hover}'
142
+ IconButton:
143
+ backgroundColor: 'transparent'
144
+ textColor: '{colors.secondary}'
145
+ IconButton-hover:
146
+ textColor: '{colors.secondary-hover}'
124
147
  CTAButton:
125
148
  backgroundColor: '{colors.primary}'
126
149
  textColor: '{colors.white}'
127
- rounded: '{rounded.md}'
150
+ borderRadii: '{borderRadii.md}'
128
151
  CTAButton-inverse:
129
152
  backgroundColor: '{colors.primary-inverse}'
130
153
  textColor: '{colors.secondary}'
131
- rounded: '{rounded.md}'
154
+ borderRadii: '{borderRadii.md}'
132
155
  TextButton:
133
156
  backgroundColor: 'transparent'
134
157
  textColor: '{colors.primary}'
@@ -136,35 +159,35 @@ components:
136
159
  textColor: '{colors.primary-hover}'
137
160
  Card:
138
161
  backgroundColor: '{colors.background}'
139
- rounded: '{rounded.none}'
162
+ borderRadii: '{borderRadii.none}'
140
163
  Card-interactive:
141
- rounded: '{rounded.md}'
164
+ borderRadii: '{borderRadii.md}'
142
165
  Card-elevated:
143
166
  backgroundColor: '{colors.background-primary}'
144
- rounded: '{rounded.lg}'
167
+ borderRadii: '{borderRadii.lg}'
145
168
  Card-beige:
146
169
  backgroundColor: '{colors.beige}'
147
- rounded: '{rounded.lg}'
170
+ borderRadii: '{borderRadii.lg}'
148
171
  Input:
149
172
  backgroundColor: '{colors.background}'
150
173
  textColor: '{colors.text}'
151
- rounded: '{rounded.md}'
174
+ borderRadii: '{borderRadii.md}'
152
175
  Checkbox:
153
- backgroundColor: '{colors.interface}'
154
- rounded: '{rounded.sm}'
176
+ backgroundColor: '{colors.primary}'
177
+ borderRadii: '{borderRadii.sm}'
155
178
  Checkbox-hover:
156
- backgroundColor: '{colors.interface-hover}'
179
+ backgroundColor: '{colors.primary-hover}'
157
180
  Headline:
158
181
  textColor: '{colors.text-accent}'
159
182
  typography: '{typography.title}'
160
183
  Tag-success:
161
184
  backgroundColor: '{colors.feedback-success}'
162
185
  textColor: '{colors.white}'
163
- rounded: '{rounded.sm}'
186
+ borderRadii: '{borderRadii.sm}'
164
187
  Tag-warning:
165
188
  backgroundColor: '{colors.feedback-warning}'
166
189
  textColor: '{colors.text}'
167
- rounded: '{rounded.sm}'
190
+ borderRadii: '{borderRadii.sm}'
168
191
  Alert-error:
169
192
  backgroundColor: '{colors.background-error}'
170
193
  textColor: '{colors.feedback-error}'
@@ -180,75 +203,45 @@ components:
180
203
 
181
204
  This file defines the visual design tokens for codecademy.com, implemented using the Gamut design system (`@codecademy/gamut`, `@codecademy/gamut-styles`). Gamut ships 52 components with Figma ↔ code mappings via Figma Code Connect.
182
205
 
183
- **Figma file**: https://www.figma.com/design/ReGfRNillGABAj5SlITalN/📐-Gamut
184
- **Storybook**: https://gamut.codecademy.com
206
+ Figma file: https://www.figma.com/design/ReGfRNillGABAj5SlITalN/📐-Gamut
207
+ Storybook: https://gamut.codecademy.com
185
208
 
186
209
  ---
187
210
 
188
- ## Visual Theme & Atmosphere
211
+ ## Overview
189
212
 
190
- Codecademy communicates **logic with personality** — structured and trustworthy enough for a learning platform, with creative moments that feel engaging and human. The design voice is: _"we are ruled by logic, but are creative and a bit unexpected as well."_
213
+ Codecademy communicates logic with personality — structured and trustworthy enough for a learning platform, with creative moments that feel engaging and human. The design voice is: _"we are ruled by logic, but are creative and a bit unexpected as well."_
191
214
 
192
- **Density**: Medium. Information-dense layouts use careful whitespace and strong typographic hierarchy to stay readable. Avoid cramped or overly airy layouts.
215
+ Density: Medium. Information-dense layouts use careful whitespace and strong typographic hierarchy to stay readable. Avoid cramped or overly airy layouts.
193
216
 
194
- **Design philosophy**:
217
+ Design philosophy:
195
218
 
196
219
  - Components are color mode–aware by default — never hardcode hex values for adaptive UI
197
220
  - Every component works across all themes without modification
198
221
  - Mobile-first responsive design built on a 12-column grid
199
- - Accessibility is guaranteed by design: semantic color tokens meet contrast requirements per mode automatically
200
-
201
- ---
202
-
203
- ## Themes
204
-
205
- Codecademy products use one of four Gamut themes, all sharing the same core visual identity. Token aliases resolve to the right values per theme automatically — components require no modification.
206
-
207
- | Theme | Use case | Base font | Dark mode |
208
- | ------------- | ------------------------------- | -------------- | -------------- |
209
- | **Core** | Codecademy (default) | Apercu | ✓ light + dark |
210
- | **Admin** | Codecademy admin tools | Apercu | ✓ light + dark |
211
- | **Platform** | Codecademy learning environment | Apercu | ✓ light + dark |
212
- | **LX Studio** | LX Studio application | Hanken Grotesk | light only |
213
-
214
- The active theme is set at the app root via `<GamutProvider>`. When designing, know which theme your screen targets — it affects primary colors, font families, and available color weights.
215
-
216
- **Font licensing**: Apercu is licensed for codecademy.com only. LX Studio uses Hanken Grotesk.
222
+ - Use semantic tokens (`background-primary`, not raw `beige`) so colors adapt across themes and modes
217
223
 
218
- For Percipio projects, use `DESIGN.Percipio.md` from the same package instead.
224
+ ### Themes
219
225
 
220
- ### LX Studio theme overrides
226
+ Codecademy products use three Gamut themes, all sharing the same core visual identity. Token aliases resolve to the right values per theme automatically — components require no modification.
221
227
 
222
- LX Studio extends Core with these differences:
228
+ | Theme | Use case | Base font | Dark mode |
229
+ | -------- | ------------------------------- | --------- | -------------- |
230
+ | Core | Codecademy (default) | Apercu | ✓ light + dark |
231
+ | Admin | Codecademy admin tools | Apercu | ✓ light + dark |
232
+ | Platform | Codecademy learning environment | Apercu | ✓ light + dark |
223
233
 
224
- **Font**: All families `"Hanken Grotesk"` (no Apercu, no Suisse).
234
+ Set the active theme at the app root via `<GamutProvider theme={theme}>` (or `adminTheme` / `platformTheme`). Install this file in app repos: `gamut plugin install cursor --theme core` (copies to `./DESIGN.md`).
225
235
 
226
- **Border radii** (all values shift up one step):
227
-
228
- | Token | Core | LX Studio |
229
- | ----- | ---- | --------- |
230
- | `sm` | 2px | 4px |
231
- | `md` | 4px | 8px |
232
- | `lg` | 8px | 12px |
233
-
234
- **Semantic color overrides (light mode)**:
235
-
236
- | Token | Core value | LX Studio value |
237
- | -------------------- | ------------------- | ------------------------------- |
238
- | `primary` | hyper-500 `#3A10E5` | `#5628FE` (lxStudioPurple) |
239
- | `primary-hover` | hyper-400 `#5533FF` | `#7955FC` (lxStudioPurpleHover) |
240
- | `interface` | hyper-500 | hyper-500 (unchanged) |
241
- | `feedback-success` | green-700 `#008A27` | `#06844F` (lxStudioSuccess) |
242
- | `background-primary` | beige `#FFF0E5` | `#FAFBFC` (lxStudioBgPrimary) |
243
- | `shadow-primary` | navy-800 | navy-200 |
244
- | `border-primary` | navy-800 | navy-400 |
245
- | `border-disabled` | navy-500 | navy-300 |
236
+ For other Skillsoft products: `gamut plugin install cursor --theme percipio` or `--theme lxstudio` (see `DESIGN.Percipio.md`, `DESIGN.LXStudio.md`).
246
237
 
247
238
  ---
248
239
 
249
- ## Semantic Color Aliases
240
+ ## Colors
241
+
242
+ Use semantic token names in code and designs. They resolve to the correct raw value for the active theme and color mode automatically. Never hardcode hex values for anything that needs to adapt across modes. Never hardcode core-theme values like `beige` — use `background-primary` and other semantic aliases.
250
243
 
251
- Use these token names when specifying colors in designs. They resolve to the correct raw value for the active theme and color mode automatically. **Never hardcode hex values** for anything that needs to adapt across modes.
244
+ For dark/light regions, use `<ColorMode>` or `<Background>` from `@codecademy/gamut-styles` never swap colors manually with custom CSS.
252
245
 
253
246
  ### Text
254
247
 
@@ -275,17 +268,15 @@ Use these token names when specifying colors in designs. They resolve to the cor
275
268
 
276
269
  ### Interactive
277
270
 
278
- | Token | Light | Dark | Use for |
279
- | ----------------- | -------------------- | -------------------- | --------------------------------------------- |
280
- | `primary` | hyper-500 `#3A10E5` | yellow-500 `#FFD300` | Primary CTA, links, focus rings |
281
- | `primary-hover` | hyper-400 `#5533FF` | yellow-400 `#CCA900` | Hover state of primary interactive |
282
- | `primary-inverse` | yellow-500 `#FFD300` | hyper-500 `#3A10E5` | Primary on a colored background |
283
- | `secondary` | navy-800 `#10162F` | white `#ffffff` | Secondary CTA, ghost buttons |
284
- | `secondary-hover` | navy-800 at 86% | white at 80% | Hover state of secondary interactive |
285
- | `interface` | hyper-500 `#3A10E5` | yellow-500 `#FFD300` | UI affordances (checkboxes, toggles, sliders) |
286
- | `interface-hover` | hyper-400 `#5533FF` | yellow-400 `#CCA900` | Hover on interface elements |
287
- | `danger` | red-500 `#E91C11` | red-300 `#E85D7F` | Destructive actions, error states |
288
- | `danger-hover` | red-600 `#BE1809` | red-400 `#DC5879` | Hover on danger interactive |
271
+ | Token | Light | Dark | Use for |
272
+ | ----------------- | -------------------- | -------------------- | ------------------------------------ |
273
+ | `primary` | hyper-500 `#3A10E5` | yellow-500 `#FFD300` | Primary CTA, links, focus rings |
274
+ | `primary-hover` | hyper-400 `#5533FF` | yellow-400 `#CCA900` | Hover state of primary interactive |
275
+ | `primary-inverse` | yellow-500 `#FFD300` | hyper-500 `#3A10E5` | Primary on a colored background |
276
+ | `secondary` | navy-800 `#10162F` | white `#ffffff` | Secondary CTA, ghost buttons |
277
+ | `secondary-hover` | navy-800 at 86% | white at 80% | Hover state of secondary interactive |
278
+ | `danger` | red-500 `#E91C11` | red-300 `#E85D7F` | Destructive actions, error states |
279
+ | `danger-hover` | red-600 `#BE1809` | red-400 `#DC5879` | Hover on danger interactive |
289
280
 
290
281
  ### Border
291
282
 
@@ -313,11 +304,11 @@ Use these token names when specifying colors in designs. They resolve to the cor
313
304
 
314
305
  ---
315
306
 
316
- ## Raw Color Palette
307
+ ### Raw color palette
317
308
 
318
- All colors available as static tokens regardless of color mode. Use these only when a color should be **fixed** and not adapt to dark mode.
309
+ All colors available as static tokens regardless of color mode. Use these only when a color should be fixed and not adapt to dark mode (e.g. `<Background bg="navy">` on Codecademy-branded surfaces).
319
310
 
320
- ### Core Palette
311
+ #### Core palette
321
312
 
322
313
  | Name | Weights available | Notes |
323
314
  | --------------- | ---------------------------- | --------------------------------------------------------------------------------- |
@@ -335,39 +326,33 @@ All colors available as static tokens regardless of color mode. Use these only w
335
326
  | `black` | — | `#000000` |
336
327
  | `white` (solid) | — | `#ffffff` |
337
328
 
338
- **Named aliases** (shorthand for common weights):
329
+ Named aliases (shorthand for common weights):
339
330
  `beige`, `blue`, `green`, `hyper`, `lightBlue`, `lightGreen`, `navy`, `orange`, `paleBlue`, `paleGreen`, `palePink`, `paleRed`, `paleYellow`, `pink`, `red`, `yellow`, `black`, `white`
340
331
 
341
- ### Platform-only additions
332
+ #### Platform-only additions
342
333
 
343
334
  `lightBeige` (`#FFFBF8`), `gold` (`#8A7300`), `teal` (`#006D82`), `purple` (`#B3CCFF`)
344
335
 
345
- ### LX Studio additions
346
-
347
- `lxStudioPurple` (`#5628FE`), `lxStudioPurpleHover` (`#7955FC`), `lxStudioSuccess` (`#06844F`)
348
-
349
336
  ---
350
337
 
351
338
  ## Typography
352
339
 
353
340
  ### Typefaces
354
341
 
355
- | Token | Core / Admin / Platform | LX Studio | Use for |
356
- | ----------- | -------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------ |
357
- | `base` | Apercu Pro (CSS: `Apercu`) | Hanken Grotesk | All default UI text, headlines, body copy |
358
- | `accent` | Suisse Intl Mono (CSS: `Suisse`); falls back to `Apercu` | Hanken Grotesk | Code, captions, labels, lists, technical context |
359
- | `monospace` | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Code editor contexts |
360
- | `system` | System UI fonts | System UI fonts | Performance-critical surfaces |
361
-
362
- **Apercu is licensed for codecademy.com only.** LX Studio uses Hanken Grotesk for both `base` and `accent`.
342
+ | Token | Font | Use for |
343
+ | ----------- | -------------------------------------------------------- | ------------------------------------------------ |
344
+ | `base` | Apercu Pro (CSS: `Apercu`) | All default UI text, headlines, body copy |
345
+ | `accent` | Suisse Intl Mono (CSS: `Suisse`); falls back to `Apercu` | Code, captions, labels, lists, technical context |
346
+ | `monospace` | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Code editor contexts |
347
+ | `system` | System UI fonts | Performance-critical surfaces |
363
348
 
364
349
  ### Rules
365
350
 
366
- - **Apercu Bold** for headlines, sub-headlines, CTAs, and buttons.
367
- - **Apercu Regular** for body text, UI labels, and menu items.
368
- - **Apercu Italic** to emphasize text within a Regular paragraph — not Bold.
369
- - **Suisse** sparingly: code snippets, enumerated items, quotations, captions. Reads 10–15% large for its point size — size down relative to Apercu (14px Suisse ≈ 16px Apercu visually).
370
- - Text is **left-aligned** by default. Center-align only for short marketing headlines. Never right-align.
351
+ - Apercu Bold for headlines, sub-headlines, CTAs, and buttons.
352
+ - Apercu Regular for body text, UI labels, and menu items.
353
+ - Apercu Italic to emphasize text within a Regular paragraph — not Bold.
354
+ - Suisse sparingly: code snippets, enumerated items, quotations, captions. Reads 10–15% large for its point size — size down relative to Apercu (14px Suisse ≈ 16px Apercu visually).
355
+ - Text is left-aligned by default. Center-align only for short marketing headlines. Never right-align.
371
356
  - Do not adjust letter-spacing.
372
357
 
373
358
  ### Font size scale
@@ -405,9 +390,11 @@ Target 45–85 characters per line; 66 characters is ideal for web body text. Ma
405
390
 
406
391
  ---
407
392
 
408
- ## Spacing Scale
393
+ ## Layout
409
394
 
410
- All spacing is multiples of 4px, placed on an 8px grid.
395
+ ### Spacing scale
396
+
397
+ All spacing is multiples of 4px, placed on an 8px grid. Use only these values for padding, margin, gap, width, and height — snap to the nearest token if a design specifies an off-scale value.
411
398
 
412
399
  | Token | Value |
413
400
  | ----- | ----- |
@@ -423,9 +410,70 @@ All spacing is multiples of 4px, placed on an 8px grid.
423
410
  | `64` | 64px |
424
411
  | `96` | 96px |
425
412
 
413
+ Use multiples of 8px for block-element spacing; 4px only for inline or typographic relationships.
414
+
415
+ ### System props
416
+
417
+ Never use inline `style` attributes. Use system props from `@codecademy/gamut-styles` with shorthand names:
418
+
419
+ | Long form | Shorthand |
420
+ | --------------- | --------- |
421
+ | `margin` | `m` |
422
+ | `marginTop` | `mt` |
423
+ | `marginBottom` | `mb` |
424
+ | `marginLeft` | `ml` |
425
+ | `marginRight` | `mr` |
426
+ | `marginX` | `mx` |
427
+ | `marginY` | `my` |
428
+ | `padding` | `p` |
429
+ | `paddingTop` | `pt` |
430
+ | `paddingBottom` | `pb` |
431
+ | `paddingLeft` | `pl` |
432
+ | `paddingRight` | `pr` |
433
+ | `paddingX` | `px` |
434
+ | `paddingY` | `py` |
435
+
436
+ Use `mb={16}`, not `marginBottom={16}`. Color and border props: `bg`, `color` / `textColor`, `borderColor`, `borderRadius` — values must be Gamut tokens, never raw hex.
437
+
438
+ ### Responsive behavior
439
+
440
+ Mobile-first. Apply styles from the named breakpoint and up.
441
+
442
+ | Token | Min-width | Screen dimensions | Max content | Fold height |
443
+ | -------- | --------- | ----------------- | ----------- | ----------- |
444
+ | _(base)_ | 0 | 320×480 | 288px | 440px |
445
+ | `xs` | 480px | 480×900 | 448px | 440px |
446
+ | `sm` | 768px | 768×1024 | 704px | 680px |
447
+ | `md` | 1024px | 1024×768 | 896px | 680px |
448
+ | `lg` | 1200px | 1200×900 | 1072px | 680px |
449
+ | `xl` | 1440px | 1440×900 | 1248px | 680px |
450
+
451
+ Container query variants (`c_xs` through `c_xl`) mirror these values but trigger on component container size, not viewport.
452
+
453
+ Grid: 12-column grid at all breakpoints.
454
+
455
+ | Usage | Recommended values |
456
+ | --------------------- | ------------------------------------------------ |
457
+ | Horizontal margins | 64px (lg+), 48px (md), 32px (sm/xs), 16px (base) |
458
+ | Column gaps (gutters) | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
459
+ | Row gaps | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
460
+
461
+ Minimum interactive touch target: 44×44px on mobile breakpoints.
462
+
463
+ - Begin design work at 1440px (XL), then adapt down.
464
+ - Wider multi-column layouts collapse to fewer columns — do not stretch or squish.
465
+ - Avoid dense or small components in the base (mobile) breakpoint.
466
+
467
+ ### Global layout tokens
468
+
469
+ | Token | Value | Use |
470
+ | -------------- | -------------------------------------- | ------------------------------- |
471
+ | `headerHeight` | 4rem (64px) base, 5rem (80px) at `md`+ | Global page header height |
472
+ | `headerZ` | 15 | Z-index for global page headers |
473
+
426
474
  ---
427
475
 
428
- ## Depth & Elevation
476
+ ## Elevation & Depth
429
477
 
430
478
  Gamut uses border-based and shadow-based depth cues rather than a rigid z-elevation tier system.
431
479
 
@@ -457,12 +505,14 @@ Interactive cards (`isInteractive` prop) gain a shadow on hover and `borderRadiu
457
505
 
458
506
  ---
459
507
 
460
- ## Border Radius Scale
508
+ ## Shapes
509
+
510
+ Border radius tokens from `borderRadii` in `@codecademy/gamut-styles`. No custom radius values.
461
511
 
462
512
  | Token | Value | Use |
463
513
  | ------ | ----- | ------------------------------------------ |
464
514
  | `none` | 0px | Square / non-interactive elements |
465
- | `sm` | 2px | Subtle rounding, tags |
515
+ | `sm` | 2px | Subtle rounding, tags, checkboxes |
466
516
  | `md` | 4px | Default buttons, inputs, interactive cards |
467
517
  | `lg` | 8px | Cards, panels |
468
518
  | `xl` | 16px | Large cards, modals |
@@ -470,47 +520,9 @@ Interactive cards (`isInteractive` prop) gain a shadow on hover and `borderRadiu
470
520
 
471
521
  ---
472
522
 
473
- ## Responsive Behavior
474
-
475
- Mobile-first. Apply styles from the named breakpoint and up.
476
-
477
- ### Breakpoints & screen sizes
478
-
479
- | Token | Min-width | Screen dimensions | Max content | Fold height |
480
- | -------- | --------- | ----------------- | ----------- | ----------- |
481
- | _(base)_ | 0 | 320×480 | 288px | 440px |
482
- | `xs` | 480px | 480×900 | 448px | 440px |
483
- | `sm` | 768px | 768×1024 | 704px | 680px |
484
- | `md` | 1024px | 1024×768 | 896px | 680px |
485
- | `lg` | 1200px | 1200×900 | 1072px | 680px |
486
- | `xl` | 1440px | 1440×900 | 1248px | 680px |
487
-
488
- Container query variants (`c_xs` through `c_xl`) mirror these values but trigger on component container size, not viewport.
489
-
490
- ### Grid
491
-
492
- 12-column grid at all breakpoints. The designer specifies how many columns a section spans per breakpoint.
493
-
494
- | Usage | Recommended values |
495
- | --------------------- | ------------------------------------------------ |
496
- | Horizontal margins | 64px (lg+), 48px (md), 32px (sm/xs), 16px (base) |
497
- | Column gaps (gutters) | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
498
- | Row gaps | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
499
-
500
- ### Touch targets
523
+ ## Components
501
524
 
502
- Minimum interactive touch target: **44×44px** on mobile breakpoints.
503
-
504
- ### Collapsing strategies
505
-
506
- - Begin design work at 1440px (XL), then adapt to smaller sizes.
507
- - Wider multi-column layouts collapse to fewer columns — do not simply stretch or squish.
508
- - Elements not in an explicit lockup (e.g., catalog cards) should align on one axis (usually left) rather than fill column widths.
509
- - Avoid dense or small components in the base (mobile) breakpoint.
510
-
511
- ---
512
-
513
- ## Component Library
525
+ ### Component catalog
514
526
 
515
527
  Components are organized into three tiers:
516
528
 
@@ -530,6 +542,8 @@ ContentContainer, GridContainer, Layout, LayoutGrid
530
542
 
531
543
  #### Buttons
532
544
 
545
+ There is no generic `Button` component. Use the variant that matches intent:
546
+
533
547
  | Variant | Component | Use for |
534
548
  | ----------------- | -------------- | ----------------------------------- |
535
549
  | Primary action | `FillButton` | Solid fill, high-emphasis CTA |
@@ -538,32 +552,58 @@ ContentContainer, GridContainer, Layout, LayoutGrid
538
552
  | Tertiary / inline | `TextButton` | Low-emphasis, inline text actions |
539
553
  | Icon-only | `IconButton` | Compact actions with icon only |
540
554
 
541
- All button variants support sizes: `small`, `normal` (default), `large`. They accept an `icon` prop (leading or trailing) and a `disabled` prop. Passing `href` renders the button as an `<a>` tag.
542
-
543
- **States**: default hover (`primary-hover` / `secondary-hover`) active disabled (`text-disabled` + `background-disabled`).
555
+ - `IconButton` requires `tip` for screen reader accessibility.
556
+ - Never set `mode` on buttons — they inherit color context from parent wrappers.
557
+ - Sizes: `small`, `normal` (default), `large`. Support `icon`, `disabled`, and `href` (renders as `<a>`).
558
+ - States: default → hover (`primary-hover` / `secondary-hover`) → active → disabled (`text-disabled` + `background-disabled`).
544
559
 
545
560
  #### Cards
546
561
 
547
- Cards support:
548
-
549
- - **Background variants**: `default` (ColorMode-responsive), `white`, `yellow`, `beige` (light contexts), `navy`, `hyper` (dark contexts)
550
- - **Shadow variants**: `none` (default), `outline`, `patternLeft`, `patternRight`
551
- - **Interaction**: wrap in `<Anchor>` and add `isInteractive` for hover shadow + `borderRadius: md`
552
- - **Border radius**: defaults to `none` (non-interactive); override with the `borderRadius` prop as needed
562
+ - Valid `variant` values: `default`, `white`, `yellow`, `beige`, `navy`, `hyper` — never invent compound names (invalid values crash `parseToHsl()`).
563
+ - Defaults: `shadow="none"`, `isInteractive={false}`.
564
+ - Set `isInteractive` only when the card is a link or has click/hover interaction (e.g. wrapped in `<Anchor>`); interactive cards get hover shadow and `borderRadius: md`.
565
+ - Shadow variants: `none` (default), `outline`, `patternLeft`, `patternRight`.
553
566
 
554
567
  #### Color-aware components
555
568
 
556
- - **`<ColorMode mode="light|dark|system">`**wraps a subtree in an explicit color mode.
557
- - **`<Background bg="<color>">`**applies a background color and automatically switches the color mode inside to maintain accessible contrast. Prefer this over setting a raw `bg` prop on any content-bearing surface.
569
+ - `<ColorMode mode="light|dark|system">`explicit mode when you know which mode a region should use.
570
+ - `<Background bg="<color>">`dynamic background; Gamut picks contrast-safe inner mode. Prefer over raw `bg` on content-bearing surfaces.
558
571
 
559
- ---
572
+ ### Gamut implementation guardrails
560
573
 
561
- ## Global Elements
574
+ #### Component discovery
562
575
 
563
- | Token | Value | Use |
564
- | -------------- | -------------------------------------- | ------------------------------- |
565
- | `headerHeight` | 4rem (64px) base, 5rem (80px) at `md`+ | Global page header height |
566
- | `headerZ` | 15 | Z-index for global page headers |
576
+ 1. Inspect `@codecademy/gamut` exports before building custom UI for a pattern.
577
+ 2. Prefer Gamut components (`Menu`, `DataTable`, `Tabs`, `Video`, etc.) over raw HTML.
578
+ 3. Read component TypeScript definitions before using `variant` / color props.
579
+ 4. If no Gamut component exists: `{/* No Gamut component available for [pattern] using custom markup */}`
580
+
581
+ #### Forms
582
+
583
+ - Submit/save flows (validation, bundled fields, dirty tracking): use `GridForm` or `ConnectedForm`.
584
+ - Live filters / standalone controls (no submit step): use atoms (`Input`, `Select`, `Checkbox`, `Radio`, `TextArea`, `FormGroup`) directly.
585
+ - Always provide `defaultValues`; use `validation="onChange"` when the submit button should stay disabled until valid.
586
+ - Set `hideLabel: true` on checkbox, radio, or toggle fields without a meaningful `label`.
587
+
588
+ #### DataTable / DataList
589
+
590
+ - `sortable: true` on a column requires `query`, `onQueryChange`, and client-sorted `rows` — Gamut does not sort data internally.
591
+
592
+ #### Menu
593
+
594
+ - Always set `variant` explicitly: `fixed` + `as="nav"` for persistent navigation (sidebars, primary nav); `popover` for overflow/action menus.
595
+ - Do not let flex-stretching ancestors expand `Menu` to fill vertical space — wrap in intrinsic-height containers.
596
+
597
+ #### Accessibility
598
+
599
+ - Meet WCAG contrast and 44×44px minimum touch targets on mobile.
600
+ - Use `FocusTrap` inside modals, dialogs, drawers, and other focus-confined regions.
601
+
602
+ #### Assets
603
+
604
+ - Icons: `@codecademy/gamut-icons` — verify icons exist; do not trust stale Figma layer names.
605
+ - Illustrations: `@codecademy/gamut-illustrations`
606
+ - Patterns: `@codecademy/gamut-patterns`
567
607
 
568
608
  ---
569
609
 
@@ -571,34 +611,42 @@ Cards support:
571
611
 
572
612
  ### Colors
573
613
 
574
- - **Do** use semantic color aliases (`primary`, `text`, `background`, etc.) for any UI that must adapt to color mode or theme.
575
- - **Do** use `<Background bg="...">` when setting a section background — it adjusts the inner color mode for contrast automatically.
576
- - **Don't** hardcode hex values for anything adaptive.
577
- - **Don't** use navy or white semi-transparent swatches where they may overlap unpredictably.
614
+ - Do use semantic color aliases (`primary`, `text`, `background`, etc.) for any UI that must adapt to color mode or theme.
615
+ - Do use `<Background bg="...">` when setting a section background — it adjusts the inner color mode for contrast automatically.
616
+ - Don't hardcode hex values for anything adaptive.
617
+ - Don't use navy or white semi-transparent swatches where they may overlap unpredictably.
578
618
 
579
619
  ### Typography
580
620
 
581
- - **Do** use `title` weight (700) for headlines, CTAs, and buttons.
582
- - **Do** keep body text at 150–175% line height for readability.
583
- - **Do** use Suisse sparingly — as an accent for code, captions, and lists only.
584
- - **Don't** use Apercu Bold to emphasize text _within_ a paragraph — use Italic instead.
585
- - **Don't** adjust letter-spacing.
586
- - **Don't** right-align text in normal circumstances.
587
- - **Don't** center-align body paragraphs with long line lengths.
621
+ - Do use `title` weight (700) for headlines, CTAs, and buttons.
622
+ - Do keep body text at 150–175% line height for readability.
623
+ - Do use Suisse sparingly — as an accent for code, captions, and lists only.
624
+ - Don't use Apercu Bold to emphasize text _within_ a paragraph — use Italic instead.
625
+ - Don't adjust letter-spacing.
626
+ - Don't right-align text in normal circumstances.
627
+ - Don't center-align body paragraphs with long line lengths.
588
628
 
589
629
  ### Layout & Spacing
590
630
 
591
- - **Do** use multiples of 8px for block-element spacing (4px only for inline / typographic relationships).
592
- - **Do** begin design work at 1440px (XL), then adapt down to each breakpoint.
593
- - **Do** align elements to the 12-column grid.
594
- - **Don't** stretch elements to fill wider space — maintain proper line lengths and component widths.
631
+ - Do use multiples of 8px for block-element spacing (4px only for inline / typographic relationships).
632
+ - Do begin design work at 1440px (XL), then adapt down to each breakpoint.
633
+ - Do align elements to the 12-column grid.
634
+ - Don't stretch elements to fill wider space — maintain proper line lengths and component widths.
595
635
 
596
636
  ### Components
597
637
 
598
- - **Do** use `FillButton` for primary actions and `StrokeButton` for secondary actions.
599
- - **Do** add `isInteractive` to any `Card` that is wrapped in an `<Anchor>`.
600
- - **Don't** use `CTAButton` for standard UI actions reserve it for marketing/high-visibility promotions.
601
- - **Don't** use `<Background>` without an actual color value — it's not a neutral wrapper.
638
+ - Do use `FillButton` for primary actions and `StrokeButton` for secondary actions.
639
+ - Do add `isInteractive` only to `Card` components that are links or otherwise interactive.
640
+ - Don't import a generic `Button` it does not exist in Gamut.
641
+ - Don't use `CTAButton` for standard UI actionsreserve it for marketing/high-visibility promotions.
642
+ - Don't use `<Background>` without an actual color value — it's not a neutral wrapper.
643
+ - Don't use bare form atoms for functional forms — use `GridForm` or `ConnectedForm`.
644
+
645
+ ### Pre-ship validation
646
+
647
+ Before considering UI output final, run `/gamut-review` from the app repository root (the directory that contains `DESIGN.md`). Install the plugin first if needed: Cursor — `gamut plugin install cursor --theme core`; Claude Code — `gamut plugin install claude --theme core` (use `admin` or `platform` for those Codecademy themes).
648
+
649
+ The command performs automated checks (dependencies, `GamutProvider`, imports, hex colors, tests, component guardrails) and prints a manual pre-ship checklist keyed to this product's theme. Fix all errors before shipping. Full procedure: [`commands/gamut-review.md`](commands/gamut-review.md) in `@codecademy/gamut` agent-tools (installed as a slash command with the Gamut plugin).
602
650
 
603
651
  ---
604
652