@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
|
@@ -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 —
|
|
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: '
|
|
12
|
-
navy-600: '
|
|
13
|
-
navy-500: '
|
|
14
|
-
navy-300: '
|
|
15
|
-
navy-200: '
|
|
16
|
-
navy-100: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
+
borderRadii: '{borderRadii.md}'
|
|
128
151
|
CTAButton-inverse:
|
|
129
152
|
backgroundColor: '{colors.primary-inverse}'
|
|
130
153
|
textColor: '{colors.secondary}'
|
|
131
|
-
|
|
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
|
-
|
|
162
|
+
borderRadii: '{borderRadii.none}'
|
|
140
163
|
Card-interactive:
|
|
141
|
-
|
|
164
|
+
borderRadii: '{borderRadii.md}'
|
|
142
165
|
Card-elevated:
|
|
143
166
|
backgroundColor: '{colors.background-primary}'
|
|
144
|
-
|
|
167
|
+
borderRadii: '{borderRadii.lg}'
|
|
145
168
|
Card-beige:
|
|
146
169
|
backgroundColor: '{colors.beige}'
|
|
147
|
-
|
|
170
|
+
borderRadii: '{borderRadii.lg}'
|
|
148
171
|
Input:
|
|
149
172
|
backgroundColor: '{colors.background}'
|
|
150
173
|
textColor: '{colors.text}'
|
|
151
|
-
|
|
174
|
+
borderRadii: '{borderRadii.md}'
|
|
152
175
|
Checkbox:
|
|
153
|
-
backgroundColor: '{colors.
|
|
154
|
-
|
|
176
|
+
backgroundColor: '{colors.primary}'
|
|
177
|
+
borderRadii: '{borderRadii.sm}'
|
|
155
178
|
Checkbox-hover:
|
|
156
|
-
backgroundColor: '{colors.
|
|
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
|
-
|
|
186
|
+
borderRadii: '{borderRadii.sm}'
|
|
164
187
|
Tag-warning:
|
|
165
188
|
backgroundColor: '{colors.feedback-warning}'
|
|
166
189
|
textColor: '{colors.text}'
|
|
167
|
-
|
|
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
|
-
|
|
184
|
-
|
|
206
|
+
Figma file: https://www.figma.com/design/ReGfRNillGABAj5SlITalN/📐-Gamut
|
|
207
|
+
Storybook: https://gamut.codecademy.com
|
|
185
208
|
|
|
186
209
|
---
|
|
187
210
|
|
|
188
|
-
##
|
|
211
|
+
## Overview
|
|
189
212
|
|
|
190
|
-
Codecademy communicates
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
224
|
+
### Themes
|
|
219
225
|
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
| `
|
|
286
|
-
| `
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 |
|
|
356
|
-
| ----------- | -------------------------------------------------------- |
|
|
357
|
-
| `base` | Apercu Pro (CSS: `Apercu`) |
|
|
358
|
-
| `accent` | Suisse Intl Mono (CSS: `Suisse`); falls back to `Apercu` |
|
|
359
|
-
| `monospace` | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas |
|
|
360
|
-
| `system` | System UI fonts |
|
|
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
|
-
-
|
|
367
|
-
-
|
|
368
|
-
-
|
|
369
|
-
-
|
|
370
|
-
- Text is
|
|
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
|
-
##
|
|
393
|
+
## Layout
|
|
409
394
|
|
|
410
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
-
|
|
548
|
-
|
|
549
|
-
-
|
|
550
|
-
-
|
|
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
|
-
-
|
|
557
|
-
-
|
|
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
|
-
|
|
574
|
+
#### Component discovery
|
|
562
575
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
-
|
|
575
|
-
-
|
|
576
|
-
-
|
|
577
|
-
-
|
|
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
|
-
-
|
|
582
|
-
-
|
|
583
|
-
-
|
|
584
|
-
-
|
|
585
|
-
-
|
|
586
|
-
-
|
|
587
|
-
-
|
|
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
|
-
-
|
|
592
|
-
-
|
|
593
|
-
-
|
|
594
|
-
-
|
|
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
|
-
-
|
|
599
|
-
-
|
|
600
|
-
-
|
|
601
|
-
-
|
|
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 actions — reserve 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
|
|