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