@codecademy/gamut 68.6.1-alpha.edab62.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 (46) hide show
  1. package/agent-tools/.cursor-plugin/plugin.json +1 -1
  2. package/agent-tools/DESIGN.Codecademy.md +466 -413
  3. package/agent-tools/DESIGN.LXStudio.md +350 -282
  4. package/agent-tools/DESIGN.Percipio.md +357 -279
  5. package/agent-tools/commands/gamut-review.md +176 -87
  6. package/agent-tools/guidelines/components/animations.md +74 -0
  7. package/agent-tools/guidelines/components/buttons.md +74 -23
  8. package/agent-tools/guidelines/components/card.md +19 -0
  9. package/agent-tools/guidelines/components/coachmark.md +21 -0
  10. package/agent-tools/guidelines/components/data-table.md +79 -0
  11. package/agent-tools/guidelines/components/forms.md +106 -0
  12. package/agent-tools/guidelines/components/loading-states.md +17 -0
  13. package/agent-tools/guidelines/components/menu.md +58 -0
  14. package/agent-tools/guidelines/components/overview.md +97 -17
  15. package/agent-tools/guidelines/components/radial-progress.md +13 -0
  16. package/agent-tools/guidelines/components/select.md +23 -0
  17. package/agent-tools/guidelines/components/tooltips.md +22 -0
  18. package/agent-tools/guidelines/components/video.md +29 -0
  19. package/agent-tools/guidelines/foundations/color.md +140 -58
  20. package/agent-tools/guidelines/foundations/modes.md +41 -17
  21. package/agent-tools/guidelines/foundations/spacing.md +78 -37
  22. package/agent-tools/guidelines/foundations/typography.md +69 -37
  23. package/agent-tools/guidelines/overview-icons.md +19 -0
  24. package/agent-tools/guidelines/overview-illustrations.md +7 -0
  25. package/agent-tools/guidelines/overview-patterns.md +7 -0
  26. package/agent-tools/guidelines/overview.md +71 -22
  27. package/agent-tools/guidelines/setup.md +59 -18
  28. package/agent-tools/rules/accessibility.mdc +22 -13
  29. package/agent-tools/skills/gamut-accessibility/SKILL.md +97 -112
  30. package/agent-tools/skills/gamut-color-mode/SKILL.md +91 -41
  31. package/agent-tools/skills/gamut-components/SKILL.md +46 -0
  32. package/agent-tools/skills/gamut-forms/SKILL.md +101 -0
  33. package/agent-tools/skills/gamut-style-utilities/SKILL.md +111 -0
  34. package/agent-tools/skills/gamut-system-props/SKILL.md +81 -29
  35. package/agent-tools/skills/gamut-testing/SKILL.md +106 -62
  36. package/agent-tools/skills/gamut-theming/SKILL.md +36 -86
  37. package/agent-tools/skills/gamut-typography/SKILL.md +36 -80
  38. package/bin/commands/plugin/install.mjs +96 -56
  39. package/bin/commands/plugin/list.mjs +11 -43
  40. package/bin/commands/plugin/remove.mjs +30 -38
  41. package/bin/commands/plugin/update.mjs +15 -5
  42. package/bin/gamut.mjs +17 -13
  43. package/bin/lib/design.mjs +71 -0
  44. package/bin/lib/io.mjs +14 -0
  45. package/package.json +6 -6
  46. package/bin/lib/figma.mjs +0 -49
@@ -4,242 +4,303 @@ name: Percipio Design System
4
4
  description: Design tokens for the Skillsoft Percipio platform.
5
5
  colors:
6
6
  # palette — raw swatches; set once on :root and then always reference by token name, never use hex values directly in code
7
- percipioTextPrimary: "#222325"
8
- percipioTextSecondary: "#595A5C"
9
- percipioTextDisabled: "#AFB6C2"
10
- percipioActionPrimary: "#0073C4"
11
- percipioActionPrimaryHover: "#141C36"
12
- percipioActionSecondary: "#6A6E75"
13
- percipioActionSecondaryHover: "#7F8288"
14
- percipioActionDangerHover: "#A52020"
15
- percipioDanger: "#B83C3C"
16
- percipioFeedbackSuccess: "#1B8057"
17
- percipioFeedbackWarning: "#EF5B0D"
18
- percipioBgPrimary: "#FAFBFC"
19
- percipioBgSuccess: "#EEF7F3"
20
- percipioBgWarning: "#FFF7E0"
21
- percipioBgError: "#FFF1F5"
22
- navy-800: "#10162F"
23
- navy-400: "#8F919D"
24
- navy-300: "#BCBEC5"
25
- navy-200: "#E2E3E6"
26
- navy-100: "#F5F6F7"
27
- white: "#ffffff"
7
+ percipioTextPrimary: '#222325'
8
+ percipioTextAccent: '#222325'
9
+ percipioTextSecondary: 'rgba(34, 35, 37, 0.75)'
10
+ percipioTextDisabled: '#AFB6C2'
11
+ sapphire: '#1C50BB'
12
+ percipioActionPrimaryHover: '#141C36'
13
+ percipioActionSecondary: '#6A6E75'
14
+ percipioActionSecondaryHover: 'rgba(106, 110, 117, 0.86)'
15
+ percipioActionDangerHover: '#A52020'
16
+ percipioDanger: '#B83C3C'
17
+ percipioFeedbackSuccess: '#1B8057'
18
+ percipioFeedbackWarning: '#EF5B0D'
19
+ percipioBgPrimary: '#FAFBFC'
20
+ percipioBgSuccess: '#EEF7F3'
21
+ percipioBgWarning: '#FFF7E0'
22
+ percipioBgError: '#FFF1F5'
23
+ navy-800: '#10162F'
24
+ navy-400: 'rgba(16, 22, 47, 0.47)'
25
+ navy-300: 'rgba(16, 22, 47, 0.28)'
26
+ navy-200: 'rgba(16, 22, 47, 0.12)'
27
+ navy-100: 'rgba(16, 22, 47, 0.04)'
28
+ white: '#ffffff'
28
29
  # semantic aliases — use these in code, not palette swatches or hex values
29
- text: "{colors.percipioTextPrimary}"
30
- text-accent: "{colors.percipioTextPrimary}"
31
- text-secondary: "{colors.percipioTextSecondary}"
32
- text-disabled: "{colors.percipioTextDisabled}"
33
- background: "{colors.white}"
34
- background-primary: "{colors.percipioBgPrimary}"
35
- background-selected: "{colors.navy-100}"
36
- background-hover: "{colors.navy-200}"
37
- background-disabled: "{colors.navy-200}"
38
- background-success: "{colors.percipioBgSuccess}"
39
- background-warning: "{colors.percipioBgWarning}"
40
- background-error: "{colors.percipioBgError}"
41
- primary: "{colors.percipioActionPrimary}"
42
- primary-hover: "{colors.percipioActionPrimaryHover}"
43
- primary-inverse: "{colors.white}"
44
- secondary: "{colors.percipioActionSecondary}"
45
- secondary-hover: "{colors.percipioActionSecondaryHover}"
46
- interface: "{colors.percipioActionPrimary}"
47
- interface-hover: "{colors.percipioActionPrimaryHover}"
48
- danger: "{colors.percipioDanger}"
49
- danger-hover: "{colors.percipioActionDangerHover}"
50
- feedback-error: "{colors.percipioDanger}"
51
- feedback-success: "{colors.percipioFeedbackSuccess}"
52
- feedback-warning: "{colors.percipioFeedbackWarning}"
53
- border-primary: "{colors.navy-400}"
54
- border-secondary: "{colors.navy-200}"
55
- border-tertiary: "{colors.navy-800}"
56
- border-disabled: "{colors.navy-300}"
57
- shadow-primary: "{colors.navy-200}"
58
- shadow-secondary: "{colors.navy-400}"
30
+ text: '{colors.percipioTextPrimary}'
31
+ text-accent: '{colors.percipioTextAccent}'
32
+ text-secondary: '{colors.percipioTextSecondary}'
33
+ text-disabled: '{colors.percipioTextDisabled}'
34
+ background: '{colors.white}'
35
+ background-primary: '{colors.percipioBgPrimary}'
36
+ background-selected: '{colors.navy-100}'
37
+ background-hover: '{colors.navy-200}'
38
+ background-disabled: '{colors.navy-200}'
39
+ background-success: '{colors.percipioBgSuccess}'
40
+ background-warning: '{colors.percipioBgWarning}'
41
+ background-error: '{colors.percipioBgError}'
42
+ primary: '{colors.sapphire}'
43
+ primary-hover: '{colors.percipioActionPrimaryHover}'
44
+ primary-inverse: '{colors.white}'
45
+ secondary: '{colors.percipioActionSecondary}'
46
+ secondary-hover: '{colors.percipioActionSecondaryHover}'
47
+ danger: '{colors.percipioDanger}'
48
+ danger-hover: '{colors.percipioActionDangerHover}'
49
+ feedback-error: '{colors.percipioDanger}'
50
+ feedback-success: '{colors.percipioFeedbackSuccess}'
51
+ feedback-warning: '{colors.percipioFeedbackWarning}'
52
+ border-primary: '{colors.navy-400}'
53
+ border-secondary: '{colors.navy-200}'
54
+ border-tertiary: '{colors.navy-800}'
55
+ border-disabled: '{colors.navy-300}'
56
+ shadow-primary: '{colors.navy-200}'
57
+ shadow-secondary: '{colors.navy-400}'
59
58
  typography:
60
59
  base:
61
- fontFamily: '"Roboto", sans-serif'
62
- fontSize: "1rem"
63
- fontWeight: "400"
64
- lineHeight: "1.5"
60
+ fontFamily: '"Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
61
+ fontSize: '1rem'
62
+ fontWeight: '400'
63
+ lineHeight: '1.5'
65
64
  accent:
66
- fontFamily: '"Roboto", sans-serif'
67
- fontSize: "0.875rem"
68
- fontWeight: "400"
69
- lineHeight: "1.5"
65
+ fontFamily: '"Skillsoft Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
66
+ fontSize: '0.875rem'
67
+ fontWeight: '400'
68
+ lineHeight: '1.5'
70
69
  title:
71
- fontFamily: '"Roboto", sans-serif'
72
- fontSize: "2.125rem"
73
- fontWeight: "500"
74
- lineHeight: "1.2"
70
+ fontFamily: '"Skillsoft Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
71
+ fontSize: '2.125rem'
72
+ fontWeight: '500'
73
+ lineHeight: '1.2'
75
74
  monospace:
76
75
  fontFamily: '"Roboto Mono", monospace'
76
+ system:
77
+ fontFamily: '"Roboto", sans-serif'
78
+ '14':
79
+ fontSize: '0.875rem'
80
+ '16':
81
+ fontSize: '1rem'
82
+ '18':
83
+ fontSize: '1.125rem'
84
+ '20':
85
+ fontSize: '1.25rem'
86
+ '22':
87
+ fontSize: '1.375rem'
88
+ '26':
89
+ fontSize: '1.625rem'
90
+ '34':
91
+ fontSize: '2.125rem'
92
+ '44':
93
+ fontSize: '2.75rem'
94
+ '64':
95
+ fontSize: '4rem'
96
+ borderRadii:
97
+ none: '0px'
98
+ sm: '2px'
99
+ md: '4px'
100
+ lg: '8px'
101
+ xl: '16px'
102
+ full: '999px'
103
+ spacing:
104
+ '0': '0'
105
+ '4': '0.25rem'
106
+ '8': '0.5rem'
107
+ '12': '0.75rem'
108
+ '16': '1rem'
109
+ '24': '1.5rem'
110
+ '32': '2rem'
111
+ '40': '2.5rem'
112
+ '48': '3rem'
113
+ '64': '4rem'
114
+ '96': '6rem'
77
115
  components:
78
116
  FillButton:
79
- backgroundColor: "{colors.primary}"
80
- textColor: "{colors.primary-inverse}"
81
- rounded: "{rounded.md}"
117
+ backgroundColor: '{colors.primary}'
118
+ textColor: '{colors.primary-inverse}'
119
+ borderRadii: '{borderRadii.md}'
82
120
  FillButton-hover:
83
- backgroundColor: "{colors.primary-hover}"
84
- textColor: "{colors.primary-inverse}"
121
+ backgroundColor: '{colors.primary-hover}'
122
+ textColor: '{colors.primary-inverse}'
85
123
  FillButton-danger:
86
- backgroundColor: "{colors.danger}"
87
- textColor: "{colors.white}"
88
- rounded: "{rounded.md}"
124
+ backgroundColor: '{colors.danger}'
125
+ textColor: '{colors.white}'
126
+ borderRadii: '{borderRadii.md}'
89
127
  FillButton-danger-hover:
90
- backgroundColor: "{colors.danger-hover}"
91
- textColor: "{colors.white}"
128
+ backgroundColor: '{colors.danger-hover}'
129
+ textColor: '{colors.white}'
92
130
  StrokeButton:
93
- backgroundColor: "transparent"
94
- textColor: "{colors.secondary}"
95
- rounded: "{rounded.md}"
131
+ backgroundColor: 'transparent'
132
+ textColor: '{colors.secondary}'
133
+ borderRadii: '{borderRadii.md}'
134
+ StrokeButton-hover:
135
+ textColor: '{colors.secondary-hover}'
136
+ TextButton:
137
+ backgroundColor: 'transparent'
138
+ textColor: '{colors.primary}'
139
+ TextButton-hover:
140
+ textColor: '{colors.primary-hover}'
141
+ IconButton:
142
+ backgroundColor: 'transparent'
143
+ textColor: '{colors.secondary}'
144
+ IconButton-hover:
145
+ textColor: '{colors.secondary-hover}'
96
146
  Input:
97
- backgroundColor: "{colors.background}"
98
- textColor: "{colors.text}"
99
- rounded: "{rounded.md}"
100
- borderColor: "{colors.border-primary}"
147
+ backgroundColor: '{colors.background}'
148
+ textColor: '{colors.text}'
149
+ borderRadii: '{borderRadii.md}'
150
+ borderColor: '{colors.border-primary}'
101
151
  Checkbox:
102
- backgroundColor: "{colors.interface}"
103
- rounded: "{rounded.sm}"
152
+ backgroundColor: '{colors.primary}'
153
+ borderRadii: '{borderRadii.sm}'
104
154
  Checkbox-hover:
105
- backgroundColor: "{colors.interface-hover}"
155
+ backgroundColor: '{colors.primary-hover}'
156
+ Card:
157
+ backgroundColor: '{colors.background}'
158
+ borderRadii: '{borderRadii.none}'
159
+ Card-interactive:
160
+ borderRadii: '{borderRadii.md}'
161
+ Card-elevated:
162
+ backgroundColor: '{colors.background-primary}'
163
+ borderRadii: '{borderRadii.lg}'
164
+ Headline:
165
+ textColor: '{colors.text-accent}'
166
+ typography: '{typography.title}'
167
+ Tag-success:
168
+ backgroundColor: '{colors.feedback-success}'
169
+ textColor: '{colors.white}'
170
+ borderRadii: '{borderRadii.sm}'
171
+ Tag-warning:
172
+ backgroundColor: '{colors.feedback-warning}'
173
+ textColor: '{colors.text}'
174
+ borderRadii: '{borderRadii.sm}'
106
175
  Alert-error:
107
- backgroundColor: "{colors.background-error}"
108
- textColor: "{colors.feedback-error}"
176
+ backgroundColor: '{colors.background-error}'
177
+ textColor: '{colors.feedback-error}'
109
178
  Alert-success:
110
- backgroundColor: "{colors.background-success}"
111
- textColor: "{colors.text}"
179
+ backgroundColor: '{colors.background-success}'
180
+ textColor: '{colors.text}'
112
181
  Alert-warning:
113
- backgroundColor: "{colors.background-warning}"
114
- textColor: "{colors.text}"
182
+ backgroundColor: '{colors.background-warning}'
183
+ textColor: '{colors.text}'
115
184
  ---
116
185
 
117
186
  # Percipio
118
187
 
119
188
  This file defines the visual design tokens for the Skillsoft Percipio platform, implemented using the Gamut design system (`@codecademy/gamut`, `@codecademy/gamut-styles`). Percipio uses a dedicated Gamut theme that applies its own colors and typography — all Gamut components work without modification.
120
189
 
121
- **Storybook**: https://gamut.codecademy.com
190
+ Storybook: https://gamut.codecademy.com
122
191
 
123
192
  ---
124
193
 
125
- ## Visual Theme & Atmosphere
126
-
127
- Percipio communicates **professional clarity** — clean, trustworthy, and enterprise-ready. The design voice is more neutral and corporate than Codecademy: less playful, more functional. Primary blue drives interactive affordances; neutral grays define text and structure.
194
+ ## Overview
128
195
 
129
- **Density**: Medium. Consistent with Codecademy layouts but with softer shadows and a lighter overall feel due to the muted neutral palette.
196
+ Percipio communicates professional clarity clean, trustworthy, and enterprise-ready. The design voice is more neutral and corporate than Codecademy: less playful, more functional. Primary blue drives interactive affordances; neutral grays define text and structure.
130
197
 
131
- **Design philosophy**:
132
- - Light mode only — no dark mode support
133
- - Primary blue (`percipioActionPrimary`) replaces Codecademy's `hyper-500` for all interactive elements
134
- - Text is near-black dark gray rather than navy
135
- - Shadows are soft and minimal (navy at low opacity) rather than hard borders
136
- - Title font weight is 500 (medium) rather than 700 (bold) — Percipio headlines are less heavy
198
+ Density: Medium. Consistent with Codecademy layouts but with softer shadows and a lighter overall feel due to the muted neutral palette.
137
199
 
138
- ---
200
+ ### Themes
139
201
 
140
- ## Themes
202
+ Percipio uses a single Gamut theme that extends Core. Light mode applies Percipio-specific semantic overrides (sapphire primary, percipio neutrals). Dark mode inherits Core dark semantics — use `<ColorMode>` and semantic tokens the same way as Codecademy.
141
203
 
142
- Percipio uses a single Gamut theme light mode only.
204
+ | Theme | Use case | Base font | Dark mode |
205
+ | -------- | --------------------------- | --------------------- | -------------- |
206
+ | Percipio | Skillsoft Percipio platform | Skillsoft Sans / Text | ✓ light + dark |
143
207
 
144
- | Theme | Use case | Base font | Dark mode |
145
- |---|---|---|---|
146
- | **Percipio** | Skillsoft Percipio platform | Roboto | light only |
147
-
148
- The active theme is set at the app root via `<GamutProvider theme={percipioTheme}>`.
208
+ Set the active theme via `<GamutProvider theme={percipioTheme}>`. Install in app repos: `gamut plugin install cursor --theme percipio` (copies to `./DESIGN.md`).
149
209
 
150
210
  ---
151
211
 
152
- ## Semantic Color Aliases
212
+ ## Colors
213
+
214
+ Use semantic token names in code and designs. They resolve per color mode automatically. Never hardcode hex values for adaptive UI. Never hardcode core-theme values like `beige` — use `background-primary` (`#FAFBFC` on Percipio).
153
215
 
154
- Use these token names when specifying colors. Percipio is light mode only there are no dark mode counterparts.
216
+ For dark/light regions, use `<ColorMode>` or `<Background>` never swap colors manually with custom CSS.
155
217
 
156
218
  ### Text
157
219
 
158
- | Token | Value | Use for |
159
- |---|---|---|
160
- | `text` | `#222325` | Default body and UI text |
161
- | `text-accent` | `#222325` | Emphasis text (same value as `text` in Percipio) |
162
- | `text-secondary` | `#595A5C` | Supporting / secondary copy |
163
- | `text-disabled` | `#AFB6C2` | Disabled state labels |
220
+ | Token | Light | Dark | Use for |
221
+ | ---------------- | ------------------------ | --------------- | ------------------------------------------------ |
222
+ | `text` | `#222325` | white `#ffffff` | Default body and UI text |
223
+ | `text-accent` | `#222325` | beige `#FFF0E5` | Emphasis text (same as `text` in Percipio light) |
224
+ | `text-secondary` | `rgba(34, 35, 37, 0.75)` | white at 65% | Supporting / secondary copy |
225
+ | `text-disabled` | `#AFB6C2` | white at 50% | Disabled state labels |
164
226
 
165
227
  ### Background
166
228
 
167
- | Token | Value | Use for |
168
- |---|---|---|
169
- | `background` | `#ffffff` | Default page/component background |
170
- | `background-primary` | `#FAFBFC` | Slightly elevated surfaces |
171
- | `background-selected` | `#F5F6F7` (navy-100) | Selected row / item |
172
- | `background-hover` | `#E2E3E6` (navy-200) | Hover state overlay |
173
- | `background-disabled` | `#E2E3E6` (navy-200) | Disabled surface |
174
- | `background-success` | `#EEF7F3` | Success state container |
175
- | `background-warning` | `#FFF7E0` | Warning state container |
176
- | `background-error` | `#FFF1F5` | Error state container |
229
+ | Token | Light | Dark | Use for |
230
+ | --------------------- | --------------------------------- | -------------------- | --------------------------------- |
231
+ | `background` | white `#ffffff` | navy-800 `#10162F` | Default page/component background |
232
+ | `background-primary` | `#FAFBFC` | navy-900 `#0A0D1C` | Slightly elevated surfaces |
233
+ | `background-selected` | navy-100 `rgba(16, 22, 47, 0.04)` | white at 4% | Selected row / item |
234
+ | `background-hover` | navy-200 `rgba(16, 22, 47, 0.12)` | white at 9% | Hover state overlay |
235
+ | `background-disabled` | navy-200 `rgba(16, 22, 47, 0.12)` | white at 9% | Disabled surface |
236
+ | `background-success` | `#EEF7F3` | green-900 `#151C07` | Success state container |
237
+ | `background-warning` | `#FFF7E0` | yellow-900 `#211B00` | Warning state container |
238
+ | `background-error` | `#FFF1F5` | red-900 `#280503` | Error state container |
177
239
 
178
240
  ### Interactive
179
241
 
180
- | Token | Value | Use for |
181
- |---|---|---|
182
- | `primary` | `#0073C4` | Primary CTA, links, focus rings |
183
- | `primary-hover` | `#141C36` | Hover state of primary interactive |
184
- | `primary-inverse` | `#ffffff` | Primary on a colored background |
185
- | `secondary` | `#6A6E75` | Secondary CTA, ghost buttons |
186
- | `secondary-hover` | `#7F8288` | Hover state of secondary interactive |
187
- | `interface` | `#0073C4` | UI affordances (checkboxes, toggles, sliders) |
188
- | `interface-hover` | `#141C36` | Hover on interface elements |
189
- | `danger` | `#B83C3C` | Destructive actions, error states |
190
- | `danger-hover` | `#A52020` | Hover on danger interactive |
242
+ | Token | Light | Dark | Use for |
243
+ | ----------------- | --------------------------- | -------------------- | ------------------------------------ |
244
+ | `primary` | sapphire `#1C50BB` | yellow-500 `#FFD300` | Primary CTA, links, focus rings |
245
+ | `primary-hover` | `#141C36` | yellow-400 `#CCA900` | Hover state of primary interactive |
246
+ | `primary-inverse` | white `#ffffff` | hyper-500 `#3A10E5` | Primary on a colored background |
247
+ | `secondary` | `#6A6E75` | white `#ffffff` | Secondary CTA, ghost buttons |
248
+ | `secondary-hover` | `rgba(106, 110, 117, 0.86)` | white at 80% | Hover state of secondary interactive |
249
+ | `danger` | `#B83C3C` | red-300 `#E85D7F` | Destructive actions, error states |
250
+ | `danger-hover` | `#A52020` | red-400 `#DC5879` | Hover on danger interactive |
191
251
 
192
252
  ### Border
193
253
 
194
254
  Percipio's border weights use a non-standard order: `primary` is mid-weight, `secondary` is very light, `tertiary` is the strongest (solid navy). Use them for their semantic intent, not their numeric rank.
195
255
 
196
- | Token | Value | Use for |
197
- |---|---|---|
198
- | `border-primary` | `#8F919D` (navy-400) | Standard input and card borders |
199
- | `border-secondary` | `#E2E3E6` (navy-200) | Subtle dividers, section separators |
200
- | `border-tertiary` | `#10162F` (navy-800) | Strong structural borders |
201
- | `border-disabled` | `#BCBEC5` (navy-300) | Disabled input borders |
256
+ | Token | Light | Dark | Use for |
257
+ | ------------------ | --------------------------------- | --------------- | ----------------------------------- |
258
+ | `border-primary` | navy-400 `rgba(16, 22, 47, 0.47)` | white `#ffffff` | Standard input and card borders |
259
+ | `border-secondary` | navy-200 `rgba(16, 22, 47, 0.12)` | white at 65% | Subtle dividers, section separators |
260
+ | `border-tertiary` | navy-800 `#10162F` | white at 20% | Strong structural borders |
261
+ | `border-disabled` | navy-300 `rgba(16, 22, 47, 0.28)` | white at 50% | Disabled input borders |
202
262
 
203
263
  ### Feedback
204
264
 
205
- | Token | Value | Use for |
206
- |---|---|---|
207
- | `feedback-error` | `#B83C3C` | Error messages, validation |
208
- | `feedback-success` | `#1B8057` | Success messages, confirmations |
209
- | `feedback-warning` | `#EF5B0D` | Warning messages, caution states |
265
+ | Token | Light | Dark | Use for |
266
+ | ------------------ | --------- | ------------------- | -------------------------------- |
267
+ | `feedback-error` | `#B83C3C` | red-300 `#E85D7F` | Error messages, validation |
268
+ | `feedback-success` | `#1B8057` | green-400 `#AEE938` | Success messages, confirmations |
269
+ | `feedback-warning` | `#EF5B0D` | yellow-0 `#FFFAE5` | Warning messages, caution states |
210
270
 
211
271
  ### Shadow
212
272
 
213
- | Token | Value |
214
- |---|---|
215
- | `shadow-primary` | `#E2E3E6` (navy-200) |
216
- | `shadow-secondary` | `#8F919D` (navy-400) |
273
+ | Token | Light | Dark |
274
+ | ------------------ | --------------------------------- | ------------ |
275
+ | `shadow-primary` | navy-200 `rgba(16, 22, 47, 0.12)` | white |
276
+ | `shadow-secondary` | navy-400 `rgba(16, 22, 47, 0.47)` | white at 65% |
217
277
 
218
278
  Percipio shadows are softer than Codecademy's — use `shadow-primary` for standard elevated surfaces.
219
279
 
220
280
  ---
221
281
 
222
- ## Percipio Color Palette
282
+ ### Percipio color palette
223
283
 
224
284
  Percipio introduces its own named semantic colors. These are the source values behind the aliases above. Use the semantic aliases in designs, not the raw named colors.
225
285
 
226
- | Named color | Value | Mapped to |
227
- |---|---|---|
228
- | `percipioTextPrimary` | `#222325` | `text`, `text-accent` |
229
- | `percipioTextSecondary` | `#595A5C` | `text-secondary` |
230
- | `percipioTextDisabled` | `#AFB6C2` | `text-disabled` |
231
- | `percipioActionPrimary` | `#0073C4` | `primary`, `interface` |
232
- | `percipioActionPrimaryHover` | `#141C36` | `primary-hover`, `interface-hover` |
233
- | `percipioActionSecondary` | `#6A6E75` | `secondary` |
234
- | `percipioActionSecondaryHover` | `#7F8288` | `secondary-hover` |
235
- | `percipioActionDangerHover` | `#A52020` | `danger-hover` |
236
- | `percipioDanger` | `#B83C3C` | `danger`, `feedback-error` |
237
- | `percipioFeedbackSuccess` | `#1B8057` | `feedback-success` |
238
- | `percipioFeedbackWarning` | `#EF5B0D` | `feedback-warning` |
239
- | `percipioBgPrimary` | `#FAFBFC` | `background-primary` |
240
- | `percipioBgSuccess` | `#EEF7F3` | `background-success` |
241
- | `percipioBgWarning` | `#FFF7E0` | `background-warning` |
242
- | `percipioBgError` | `#FFF1F5` | `background-error` |
286
+ | Named color | Value | Mapped to |
287
+ | ------------------------------ | --------------------------- | -------------------------- |
288
+ | `percipioTextPrimary` | `#222325` | `text` |
289
+ | `percipioTextAccent` | `#222325` | `text-accent` |
290
+ | `percipioTextSecondary` | `rgba(34, 35, 37, 0.75)` | `text-secondary` |
291
+ | `percipioTextDisabled` | `#AFB6C2` | `text-disabled` |
292
+ | `sapphire` | `#1C50BB` | `primary` |
293
+ | `percipioActionPrimaryHover` | `#141C36` | `primary-hover` |
294
+ | `percipioActionSecondary` | `#6A6E75` | `secondary` |
295
+ | `percipioActionSecondaryHover` | `rgba(106, 110, 117, 0.86)` | `secondary-hover` |
296
+ | `percipioActionDangerHover` | `#A52020` | `danger-hover` |
297
+ | `percipioDanger` | `#B83C3C` | `danger`, `feedback-error` |
298
+ | `percipioFeedbackSuccess` | `#1B8057` | `feedback-success` |
299
+ | `percipioFeedbackWarning` | `#EF5B0D` | `feedback-warning` |
300
+ | `percipioBgPrimary` | `#FAFBFC` | `background-primary` |
301
+ | `percipioBgSuccess` | `#EEF7F3` | `background-success` |
302
+ | `percipioBgWarning` | `#FFF7E0` | `background-warning` |
303
+ | `percipioBgError` | `#FFF1F5` | `background-error` |
243
304
 
244
305
  The full core swatch palette (navy, blue, green, yellow, red, etc.) is also available, but the semantic aliases above are how Percipio maps them. Raw swatches should only be used for fixed colors that must not adapt (illustrations, data viz, etc.).
245
306
 
@@ -249,56 +310,56 @@ The full core swatch palette (navy, blue, green, yellow, red, etc.) is also avai
249
310
 
250
311
  ### Typefaces
251
312
 
252
- All font families in Percipio use **Roboto**. There is no separate accent or display typeface.
313
+ Percipio uses Skillsoft Text for body and Skillsoft Sans for accent/labels. Roboto is the `system` fallback; Roboto Mono is used for monospace.
253
314
 
254
- | Token | Font | Use for |
255
- |---|---|---|
256
- | `base` | `"Roboto", sans-serif` | All default UI text, headlines, body copy |
257
- | `accent` | `"Roboto", sans-serif` | Labels, captions (same as base in Percipio) |
258
- | `monospace` | `"Roboto Mono", monospace` | Code editor contexts |
259
- | `system` | `"Roboto", sans-serif` | Performance-critical surfaces |
315
+ | Token | Font | Use for |
316
+ | ----------- | -------------------------- | ----------------------------------------- |
317
+ | `base` | `"Skillsoft Text", …` | All default UI text, headlines, body copy |
318
+ | `accent` | `"Skillsoft Sans", …` | Labels, captions, technical context |
319
+ | `monospace` | `"Roboto Mono", monospace` | Code editor contexts |
320
+ | `system` | `"Roboto", sans-serif` | Performance-critical surfaces |
260
321
 
261
322
  ### Rules
262
323
 
263
- - **Roboto Medium (500)** for headlines, sub-headlines, CTAs, and buttons — not Bold (700).
264
- - **Roboto Regular (400)** for body text, UI labels, and menu items.
265
- - Text is **left-aligned** by default. Center-align only for short marketing headlines. Never right-align.
324
+ - Skillsoft Medium (500) for headlines, sub-headlines, CTAs, and buttons — not Bold (700).
325
+ - Skillsoft Regular (400) for body text, UI labels, and menu items.
326
+ - Text is left-aligned by default. Center-align only for short marketing headlines. Never right-align.
266
327
  - Do not adjust letter-spacing.
267
328
 
268
329
  ### Font weight scale
269
330
 
270
331
  Percipio overrides the title weight from Core's 700 to 500 (medium). This gives Percipio a lighter, less heavy headline style.
271
332
 
272
- | Token | Value | Use |
273
- |---|---|---|
274
- | `base` | 400 | Body text, UI labels |
275
- | `title` | **500** | Headlines, CTAs, buttons _(differs from Codecademy's 700)_ |
333
+ | Token | Value | Use |
334
+ | ------- | ----- | ---------------------------------------------------------- |
335
+ | `base` | 400 | Body text, UI labels |
336
+ | `title` | 500 | Headlines, CTAs, buttons _(differs from Codecademy's 700)_ |
276
337
 
277
338
  ### Font size scale
278
339
 
279
340
  Shared with Core — all sizes are identical.
280
341
 
281
- | Token key | Size | Common use |
282
- |---|---|---|
283
- | `64` | 64px | Hero / display |
284
- | `44` | 44px | Page titles |
285
- | `34` | 34px | Section titles |
286
- | `26` | 26px | Sub-section titles |
287
- | `22` | 22px | Card titles, large UI labels |
288
- | `20` | 20px | Secondary titles |
289
- | `18` | 18px | Large body, intro text |
290
- | `16` | 16px | Default body text |
291
- | `14` | 14px | Small body, captions, labels |
342
+ | Token key | Size | Common use |
343
+ | --------- | ---- | ---------------------------- |
344
+ | `64` | 64px | Hero / display |
345
+ | `44` | 44px | Page titles |
346
+ | `34` | 34px | Section titles |
347
+ | `26` | 26px | Sub-section titles |
348
+ | `22` | 22px | Card titles, large UI labels |
349
+ | `20` | 20px | Secondary titles |
350
+ | `18` | 18px | Large body, intro text |
351
+ | `16` | 16px | Default body text |
352
+ | `14` | 14px | Small body, captions, labels |
292
353
 
293
354
  ### Line height scale
294
355
 
295
356
  Shared with Core.
296
357
 
297
- | Token | Value | Use |
298
- |---|---|---|
299
- | `base` | 1.5 | Body text |
300
- | `spacedTitle` | 1.3 | Sub-headlines and medium titles |
301
- | `title` | 1.2 | Large headlines |
358
+ | Token | Value | Use |
359
+ | ------------- | ----- | ------------------------------- |
360
+ | `base` | 1.5 | Body text |
361
+ | `spacedTitle` | 1.3 | Sub-headlines and medium titles |
362
+ | `title` | 1.2 | Large headlines |
302
363
 
303
364
  ### Line length
304
365
 
@@ -306,76 +367,82 @@ Target 45–85 characters per line; 66 characters is ideal. Max 50 for multi-col
306
367
 
307
368
  ---
308
369
 
309
- ## Spacing Scale
370
+ ## Layout
371
+
372
+ ### Spacing scale
310
373
 
311
374
  Identical to Core. All spacing is multiples of 4px on an 8px grid.
312
375
 
313
376
  | Token | Value |
314
- |---|---|
315
- | `0` | 0 |
316
- | `4` | 4px |
317
- | `8` | 8px |
318
- | `12` | 12px |
319
- | `16` | 16px |
320
- | `24` | 24px |
321
- | `32` | 32px |
322
- | `40` | 40px |
323
- | `48` | 48px |
324
- | `64` | 64px |
325
- | `96` | 96px |
377
+ | ----- | ----- |
378
+ | `0` | 0 |
379
+ | `4` | 4px |
380
+ | `8` | 8px |
381
+ | `12` | 12px |
382
+ | `16` | 16px |
383
+ | `24` | 24px |
384
+ | `32` | 32px |
385
+ | `40` | 40px |
386
+ | `48` | 48px |
387
+ | `64` | 64px |
388
+ | `96` | 96px |
389
+
390
+ ### System props
391
+
392
+ Never use inline `style` attributes. Use system props shorthand (`m`, `mb`, `p`, `px`, etc.) and Gamut tokens for `bg`, `color`, `borderColor`, `borderRadius`.
393
+
394
+ ### Responsive behavior
395
+
396
+ Identical to Core. Mobile-first; 12-column grid. Minimum touch target 44×44px on mobile. Begin at 1440px (XL), then adapt down.
397
+
398
+ | Token | Min-width | Max content |
399
+ | -------- | --------- | ----------- |
400
+ | _(base)_ | 0 | 288px |
401
+ | `xs` | 480px | 448px |
402
+ | `sm` | 768px | 704px |
403
+ | `md` | 1024px | 896px |
404
+ | `lg` | 1200px | 1072px |
405
+ | `xl` | 1440px | 1248px |
326
406
 
327
407
  ---
328
408
 
329
- ## Border Radius Scale
330
-
331
- Identical to Core.
409
+ ## Elevation & Depth
332
410
 
333
- | Token | Value | Use |
334
- |---|---|---|
335
- | `none` | 0px | Square / non-interactive elements |
336
- | `sm` | 2px | Subtle rounding, tags |
337
- | `md` | 4px | Default buttons, inputs, interactive cards |
338
- | `lg` | 8px | Cards, panels |
339
- | `xl` | 16px | Large cards, modals |
340
- | `full` | 999px | Pills, avatars, circular elements |
411
+ Percipio shadows are softer than Codecademy's — use `shadow-primary` (navy-200) for standard elevated surfaces. Card outline shadows use theme-resolved border/shadow tokens.
341
412
 
342
413
  ---
343
414
 
344
- ## Responsive Behavior
415
+ ## Shapes
345
416
 
346
- Identical to Core. Mobile-first, apply styles from the named breakpoint up.
417
+ Identical to Core (`borderRadii` in YAML). No custom radius values.
347
418
 
348
- | Token | Min-width | Max content |
349
- |---|---|---|
350
- | _(base)_ | 0 | 288px |
351
- | `xs` | 480px | 448px |
352
- | `sm` | 768px | 704px |
353
- | `md` | 1024px | 896px |
354
- | `lg` | 1200px | 1072px |
355
- | `xl` | 1440px | 1248px |
356
-
357
- 12-column grid at all breakpoints.
358
-
359
- | Usage | Recommended values |
360
- |---|---|
361
- | Horizontal margins | 64px (lg+), 48px (md), 32px (sm/xs), 16px (base) |
362
- | Column gaps (gutters) | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
363
- | Row gaps | 32px (lg+), 24px (md), 16px (sm/xs), 8px (base) |
364
-
365
- Minimum interactive touch target: **44×44px** on mobile breakpoints.
419
+ | Token | Value | Use |
420
+ | ------ | ----- | ------------------------------------------ |
421
+ | `none` | 0px | Square / non-interactive elements |
422
+ | `sm` | 2px | Subtle rounding, tags, checkboxes |
423
+ | `md` | 4px | Default buttons, inputs, interactive cards |
424
+ | `lg` | 8px | Cards, panels |
425
+ | `xl` | 16px | Large cards, modals |
426
+ | `full` | 999px | Pills, avatars, circular elements |
366
427
 
367
428
  ---
368
429
 
369
- ## Component Library
430
+ ## Components
370
431
 
371
432
  Same component library as Codecademy — all atoms, molecules, and organisms apply. Token values resolve differently per theme automatically.
372
433
 
373
- Key Percipio-specific visual differences:
374
- - `FillButton` uses `#0073C4` (blue) instead of hyper-purple
375
- - `FillButton` hover shifts to near-black `#141C36` rather than a lighter purple
376
- - `Checkbox` / `Toggle` use the same blue `#0073C4`
377
- - `Card` has softer shadows (navy-200 vs navy-800 in Codecademy light mode)
378
- - Card shadow patterns (`patternLeft`, `patternRight`) are available but rarely used in Percipio UIs
434
+ ### Gamut implementation guardrails
435
+
436
+ Same rules as Codecademy (`DESIGN.Codecademy.md` Components section), with Percipio-specific notes:
437
+
438
+ - Buttons: no generic `Button`; `IconButton` requires `tip`; never set `mode` on buttons.
439
+ - Forms: `GridForm` / `ConnectedForm` for submit flows; atoms only for standalone/live controls.
440
+ - Cards: valid variants `default`, `white`, `yellow`, `beige`, `navy`, `hyper`; defaults `shadow="none"`, `isInteractive={false}`; set `isInteractive` only for link/interactive cards.
441
+ - DataTable / DataList: `sortable` requires `query`, `onQueryChange`, and client-sorted rows.
442
+ - Menu: always `variant="fixed"` + `as="nav"` or `variant="popover"`.
443
+ - Color mode: `<ColorMode>` / `<Background>`; no manual rgba overrides.
444
+ - Accessibility: WCAG contrast, 44×44px touch targets, `FocusTrap` in modals/drawers.
445
+ - Assets: `@codecademy/gamut-icons`, `gamut-illustrations`, `gamut-patterns`.
379
446
 
380
447
  ---
381
448
 
@@ -383,24 +450,35 @@ Key Percipio-specific visual differences:
383
450
 
384
451
  ### Colors
385
452
 
386
- - **Do** use semantic color aliases (`primary`, `text`, `background`, etc.) — never hardcode hex values.
387
- - **Do** use `#0073C4` blue as the only primary interactive color.
388
- - **Don't** use Codecademy's hyper-purple or yellow in Percipio contexts.
389
- - **Don't** attempt dark mode Percipio is light only.
453
+ - Do use semantic color aliases (`primary`, `text`, `background`, etc.) — never hardcode hex values.
454
+ - Do use `sapphire` (`#1C50BB`) as the primary interactive color via semantic aliases in light mode.
455
+ - Do use `<ColorMode>` and `<Background>` for scoped light/dark — dark mode inherits from Core.
456
+ - Don't use Codecademy's hyper-purple or yellow directly in Percipio contexts use semantic tokens.
390
457
 
391
458
  ### Typography
392
459
 
393
- - **Do** use title weight (500) for headlines, CTAs, and buttons — not 700.
394
- - **Do** keep body text at 150–175% line height for readability.
395
- - **Don't** use a separate accent typefaceRoboto is used uniformly for base and accent.
396
- - **Don't** right-align or center-align body paragraphs.
397
- - **Don't** adjust letter-spacing.
460
+ - Do use title weight (500) for headlines, CTAs, and buttons — not 700.
461
+ - Do keep body text at 150–175% line height for readability.
462
+ - Don't use Codecademy fonts (Apercu, Suisse)Percipio uses Skillsoft Text and Skillsoft Sans.
463
+ - Don't right-align or center-align body paragraphs.
464
+ - Don't adjust letter-spacing.
398
465
 
399
466
  ### Layout & Spacing
400
467
 
401
- - **Do** use multiples of 8px for block-element spacing (4px only for inline / typographic relationships).
402
- - **Do** begin design work at 1440px (XL), then adapt down.
403
- - **Do** align elements to the 12-column grid.
468
+ - Do use multiples of 8px for block-element spacing (4px only for inline / typographic relationships).
469
+ - Do begin design work at 1440px (XL), then adapt down.
470
+ - Do align elements to the 12-column grid.
471
+
472
+ ### Components
473
+
474
+ - Don't import a generic `Button` or use Codecademy hyper/yellow directly — use semantic tokens.
475
+ - Don't use bare form atoms for functional forms.
476
+
477
+ ### Pre-ship validation
478
+
479
+ 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 percipio`; Claude Code — `gamut plugin install claude --theme percipio`.
480
+
481
+ 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).
404
482
 
405
483
  ---
406
484
 
@@ -408,27 +486,27 @@ Key Percipio-specific visual differences:
408
486
 
409
487
  Quick color/token reference for generating or specifying Percipio UI:
410
488
 
411
- | Scenario | Tokens |
412
- |---|---|
413
- | Primary button | `bg: primary (#0073C4)`, `color: white`, `hover: primary-hover (#141C36)` |
414
- | Body text | `color: text (#222325)`, `font: Roboto`, `size: 16px`, `weight: 400`, `lineHeight: base (1.5)` |
415
- | Headline | `color: text (#222325)`, `font: Roboto`, `size: 34–64px`, `weight: title (500)`, `lineHeight: title (1.2)` |
416
- | Secondary text | `color: text-secondary (#595A5C)` |
417
- | Disabled text | `color: text-disabled (#AFB6C2)` |
418
- | Elevated surface | `bg: background-primary (#FAFBFC)` |
419
- | Card default | `bg: background (#ffffff)`, `borderRadius: none` — add `isInteractive` for hover shadow + `borderRadius: md` |
420
- | Error state | `color: feedback-error (#B83C3C)`, `bg: background-error (#FFF1F5)`, `border: danger` |
421
- | Success state | `color: feedback-success (#1B8057)`, `bg: background-success (#EEF7F3)` |
422
- | Warning state | `color: feedback-warning (#EF5B0D)`, `bg: background-warning (#FFF7E0)` |
423
- | Disabled state | `color: text-disabled (#AFB6C2)`, `bg: background-disabled (#E2E3E6, navy-200)`, `border: border-disabled` |
489
+ | Scenario | Tokens |
490
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------ |
491
+ | Primary button | `bg: primary (#1C50BB)`, `color: white`, `hover: primary-hover (#141C36)` |
492
+ | Body text | `color: text (#222325)`, `font: Skillsoft Text`, `size: 16px`, `weight: 400`, `lineHeight: base (1.5)` |
493
+ | Headline | `color: text (#222325)`, `font: Skillsoft Text`, `size: 34–64px`, `weight: title (500)`, `lineHeight: title (1.2)` |
494
+ | Secondary text | `color: text-secondary (rgba(34, 35, 37, 0.75))` |
495
+ | Disabled text | `color: text-disabled (#AFB6C2)` |
496
+ | Elevated surface | `bg: background-primary (#FAFBFC)` |
497
+ | Card default | `bg: background (#ffffff)`, `borderRadius: none` — add `isInteractive` for hover shadow + `borderRadius: md` |
498
+ | Error state | `color: feedback-error (#B83C3C)`, `bg: background-error (#FFF1F5)`, `border: danger` |
499
+ | Success state | `color: feedback-success (#1B8057)`, `bg: background-success (#EEF7F3)` |
500
+ | Warning state | `color: feedback-warning (#EF5B0D)`, `bg: background-warning (#FFF7E0)` |
501
+ | Disabled state | `color: text-disabled (#AFB6C2)`, `bg: background-disabled (navy-200)`, `border: border-disabled` |
424
502
 
425
503
  ### Component token cheatsheet
426
504
 
427
505
  ```
428
- FillButton → bg: primary (#0073C4), color: white, hover: primary-hover (#141C36)
506
+ FillButton → bg: primary (#1C50BB), color: white, hover: primary-hover (#141C36)
429
507
  StrokeButton → bg: transparent, border: secondary (#6A6E75)
430
- Checkbox/Toggle → interface (#0073C4), hover: interface-hover (#141C36)
431
- Card → bg: background, shadow: shadow-primary (#E2E3E6, navy-200, soft)
508
+ Checkbox/Toggle → primary (theme-resolved), hover: primary-hover
509
+ Card → bg: background, shadow: shadow-primary (navy-200, soft)
432
510
  Alert (error) → uses feedback-error + background-error
433
511
  Alert (success) → uses feedback-success + background-success
434
512
  Alert (warning) → uses feedback-warning + background-warning