@codecademy/gamut 68.6.1-alpha.d52035.0 → 68.6.1-alpha.df4bce.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/commands/gamut-review.md +8 -8
- package/agent-tools/guidelines/components/overview.md +15 -7
- package/agent-tools/guidelines/foundations/spacing.md +78 -37
- package/agent-tools/guidelines/foundations/typography.md +70 -37
- package/agent-tools/guidelines/overview.md +21 -19
- package/agent-tools/guidelines/setup.md +57 -18
- package/agent-tools/rules/accessibility.mdc +21 -11
- package/agent-tools/skills/gamut-accessibility/SKILL.md +99 -124
- package/agent-tools/skills/gamut-forms/SKILL.md +84 -0
- package/agent-tools/skills/gamut-system-props/SKILL.md +56 -26
- package/agent-tools/skills/gamut-testing/SKILL.md +102 -62
- package/agent-tools/skills/gamut-typography/SKILL.md +34 -82
- package/package.json +6 -6
|
@@ -174,13 +174,13 @@ Case-insensitive. Use to label `palette:` in the report; **do not** stop at this
|
|
|
174
174
|
|
|
175
175
|
Grep test files (`**/__tests__/**/*.{ts,tsx}`, `**/*.test.{ts,tsx}`, `**/*.spec.{ts,tsx}`) for these patterns. Skip `node_modules`, `dist`.
|
|
176
176
|
|
|
177
|
-
| Pattern | Verdict | Reason
|
|
178
|
-
| ----------------------------------------------------- | ------------------------------------- |
|
|
179
|
-
| `jest.mock\(.*@codecademy/gamut` | **Error** | Manual mocking bypasses theme context and produces false-positive tests;
|
|
180
|
-
| `jest.mock\(.*@codecademy/gamut-styles` | **Error** | Same issue as above — mocking gamut-styles breaks token resolution
|
|
181
|
-
| `from '@codecademy/gamut-tests'` | Good — report count of files using it | Correct import for `setupRtl` and `MockGamutProvider`
|
|
182
|
-
| `from 'component-test-setup'` (without gamut-tests) | **Warning** | Should import `setupRtl` from `@codecademy/gamut-tests`, not directly from `component-test-setup` — the gamut-tests wrapper adds `MockGamutProvider` automatically
|
|
183
|
-
| `new GamutProvider` or `<GamutProvider` in test files | **Warning** |
|
|
177
|
+
| Pattern | Verdict | Reason |
|
|
178
|
+
| ----------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
179
|
+
| `jest.mock\(.*@codecademy/gamut` | **Error** | Manual mocking bypasses theme context and produces false-positive tests; prefer **`setupRtl`** from `@codecademy/gamut-tests` (or a harness + **`setupRtl`**); use raw **`MockGamutProvider`** + **`render`** only for rare one-offs or Storybook mocks |
|
|
180
|
+
| `jest.mock\(.*@codecademy/gamut-styles` | **Error** | Same issue as above — mocking gamut-styles breaks token resolution |
|
|
181
|
+
| `from '@codecademy/gamut-tests'` | Good — report count of files using it | Correct import for `setupRtl` and `MockGamutProvider` |
|
|
182
|
+
| `from 'component-test-setup'` (without gamut-tests) | **Warning** | Should import `setupRtl` from `@codecademy/gamut-tests`, not directly from `component-test-setup` — the gamut-tests wrapper adds `MockGamutProvider` automatically |
|
|
183
|
+
| `new GamutProvider` or `<GamutProvider` in test files | **Warning** | Prefer **`setupRtl`**; use **`MockGamutProvider`** (sets `useCache={false}`, `useGlobals={false}`) in harnesses or stories, not **`GamutProvider`** directly |
|
|
184
184
|
|
|
185
185
|
Skill reference for remediation: `gamut-testing`
|
|
186
186
|
|
|
@@ -215,7 +215,7 @@ Hardcoded colors [→ ga
|
|
|
215
215
|
|
|
216
216
|
Test setup [→ gamut-testing]
|
|
217
217
|
✓ @codecademy/gamut-tests used in 12 test files
|
|
218
|
-
✗ jest.mock(@codecademy/gamut) 2 occurrences — remove
|
|
218
|
+
✗ jest.mock(@codecademy/gamut) 2 occurrences — remove; prefer setupRtl (or harness + setupRtl)
|
|
219
219
|
src/components/Foo/__tests__/Foo.test.tsx:3
|
|
220
220
|
src/components/Bar/__tests__/Bar.test.tsx:5
|
|
221
221
|
⚠ direct component-test-setup import 1 occurrence — import from @codecademy/gamut-tests
|
|
@@ -17,28 +17,36 @@ ContentContainer, GridContainer, Layout, LayoutGrid
|
|
|
17
17
|
## Key patterns
|
|
18
18
|
|
|
19
19
|
### Buttons
|
|
20
|
+
|
|
20
21
|
See [buttons.md](buttons.md) for full reference. Use `FillButton` for primary actions, `StrokeButton` for secondary.
|
|
21
22
|
|
|
23
|
+
### Forms and accessibility
|
|
24
|
+
|
|
25
|
+
**`FormGroup`**, **`GridForm`**, **`ConnectedForm`**, tips, dialogs, composite widgets: **[`skills/gamut-forms/SKILL.md`](../../skills/gamut-forms/SKILL.md)** (forms) · **[`skills/gamut-accessibility/SKILL.md`](../../skills/gamut-accessibility/SKILL.md)** (overlays, composites, checklists) · Storybook [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page).
|
|
26
|
+
|
|
22
27
|
### Cards
|
|
28
|
+
|
|
23
29
|
- **Background variants**: `default` (ColorMode-responsive), `white`, `yellow`, `beige`, `navy`, `hyper`
|
|
24
30
|
- **Shadow variants**: `none` (default), `outline`, `patternLeft`, `patternRight`
|
|
25
31
|
- Add `isInteractive` when wrapping in `<Anchor>` — enables hover shadow + `borderRadius: md`
|
|
26
32
|
- Default `borderRadius` is `none`; override with `borderRadius` prop
|
|
27
33
|
|
|
28
34
|
### Color-aware components
|
|
35
|
+
|
|
29
36
|
- `<ColorMode mode="light|dark|system">` — scopes a subtree to an explicit color mode
|
|
30
37
|
- `<Background bg="<color>">` — applies background color + auto-switches inner color mode for contrast
|
|
31
38
|
|
|
32
39
|
### Alerts
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
|
40
|
+
|
|
41
|
+
| Variant | Tokens |
|
|
42
|
+
| ------- | ----------------------------------------- |
|
|
43
|
+
| Error | `feedback-error` + `background-error` |
|
|
36
44
|
| Success | `feedback-success` + `background-success` |
|
|
37
45
|
| Warning | `feedback-warning` + `background-warning` |
|
|
38
46
|
|
|
39
47
|
## Global tokens
|
|
40
48
|
|
|
41
|
-
| Token
|
|
42
|
-
|
|
43
|
-
| `headerHeight` | 64px (base), 80px (md+) | Global page header height
|
|
44
|
-
| `headerZ`
|
|
49
|
+
| Token | Value | Use |
|
|
50
|
+
| -------------- | ----------------------- | ------------------------------ |
|
|
51
|
+
| `headerHeight` | 64px (base), 80px (md+) | Global page header height |
|
|
52
|
+
| `headerZ` | 15 | Z-index for global page header |
|
|
@@ -1,62 +1,103 @@
|
|
|
1
1
|
# Spacing, Border Radius & Layout
|
|
2
2
|
|
|
3
|
+
Token values match [`packages/gamut-styles/src/variables`](https://github.com/Codecademy/gamut/tree/main/packages/gamut-styles/src/variables) (`spacing.ts`, `borderRadii.ts`, `responsive.ts`). Breakpoints and max-content widths align with Storybook [Foundations / Layout](https://gamut.codecademy.com/?path=/docs-foundations-layout--docs).
|
|
4
|
+
|
|
5
|
+
**In code — use system props for spacing:** Gamut layout primitives (`Box`, `FlexBox`, `GridBox`, …) expose margin, padding, and gap props backed by **`system.space`** from `@codecademy/gamut-styles`. Pass **spacing scale numbers** (`4`, `8`, `16`, …), not raw pixel strings. For custom `styled` components, compose `system.space` (see [`gamut-system-props` skill](../../skills/gamut-system-props/SKILL.md)). [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) shows responsive `Box` examples.
|
|
6
|
+
|
|
7
|
+
**Responsive behavior:** All those props accept mobile-first **object** (`{ _: 8, md: 24 }`) or **array** syntax per [Responsive properties](https://gamut.codecademy.com/?path=/docs-foundations-system-responsive-properties--page). **Container queries** use keys `c_base`, `c_xs`, … `c_xl`; the parent must set a container (e.g. `containerType="inline-size"` on `FlexBox`). Prefer a media-query fallback when mixing `c_*` with viewport breakpoints.
|
|
8
|
+
|
|
9
|
+
**Two different “grids”:**
|
|
10
|
+
|
|
11
|
+
- **Design / page grid** (this doc’s “Grid” section, 12 columns, margins/gutters) — product layout guidelines; implement with [`LayoutGrid`](https://gamut.codecademy.com/?path=/docs-layouts-layoutgrid-layoutgrid--docs) and responsive `columnGap` / `rowGap` where appropriate.
|
|
12
|
+
- **CSS Grid system props** — `system.grid` on styled components or `GridBox` for **local** regions; not the same as full-page `LayoutGrid`. See [System props / Grid](https://gamut.codecademy.com/?path=/docs-foundations-system-props-grid--page). `LayoutGrid` is for flexible full-page sections; use `FlexBox` / `GridBox` / `Box` for smaller areas ([LayoutGrid usage](https://gamut.codecademy.com/?path=/docs-layouts-layoutgrid-layoutgrid--docs)).
|
|
13
|
+
|
|
14
|
+
**Designer vs code names:** Figma / Layout docs often label artboards **XL, LG, MD, SM, XS, Base**. In code, viewport breakpoints are **`xl`, `lg`, `md`, `sm`, `xs`** (min-widths below); **`_`** is the base (no min-width query). The “Max content width” column maps to those design sizes, not the `xs` token name alone.
|
|
15
|
+
|
|
3
16
|
## Spacing scale
|
|
4
17
|
|
|
5
18
|
All spacing is multiples of 4px on an 8px grid.
|
|
6
19
|
|
|
7
20
|
| Token | Value |
|
|
8
|
-
|
|
9
|
-
| `0`
|
|
10
|
-
| `4`
|
|
11
|
-
| `8`
|
|
12
|
-
| `12`
|
|
13
|
-
| `16`
|
|
14
|
-
| `24`
|
|
15
|
-
| `32`
|
|
16
|
-
| `40`
|
|
17
|
-
| `48`
|
|
18
|
-
| `64`
|
|
19
|
-
| `96`
|
|
21
|
+
| ----- | ----- |
|
|
22
|
+
| `0` | 0 |
|
|
23
|
+
| `4` | 4px |
|
|
24
|
+
| `8` | 8px |
|
|
25
|
+
| `12` | 12px |
|
|
26
|
+
| `16` | 16px |
|
|
27
|
+
| `24` | 24px |
|
|
28
|
+
| `32` | 32px |
|
|
29
|
+
| `40` | 40px |
|
|
30
|
+
| `48` | 48px |
|
|
31
|
+
| `64` | 64px |
|
|
32
|
+
| `96` | 96px |
|
|
20
33
|
|
|
21
34
|
Use multiples of 8px for block-element spacing. Use 4px only for inline or typographic relationships.
|
|
22
35
|
|
|
23
36
|
## Border radius
|
|
24
37
|
|
|
25
|
-
| Token
|
|
26
|
-
|
|
27
|
-
| `none` | 0px
|
|
28
|
-
| `sm`
|
|
29
|
-
| `md`
|
|
30
|
-
| `lg`
|
|
31
|
-
| `xl`
|
|
32
|
-
| `full` | 999px | Pills, avatars, circular elements
|
|
38
|
+
| Token | Value | Use |
|
|
39
|
+
| ------ | ----- | ---------------------------------- |
|
|
40
|
+
| `none` | 0px | Square / non-interactive elements |
|
|
41
|
+
| `sm` | 2px | Subtle rounding, tags |
|
|
42
|
+
| `md` | 4px | Buttons, inputs, interactive cards |
|
|
43
|
+
| `lg` | 8px | Cards, panels |
|
|
44
|
+
| `xl` | 16px | Large cards, modals |
|
|
45
|
+
| `full` | 999px | Pills, avatars, circular elements |
|
|
33
46
|
|
|
34
47
|
## Breakpoints
|
|
35
48
|
|
|
36
49
|
Mobile-first. Styles apply from the named breakpoint and up.
|
|
37
50
|
|
|
38
|
-
| Token
|
|
39
|
-
|
|
40
|
-
| _(base)_ | 0
|
|
41
|
-
| `xs`
|
|
42
|
-
| `sm`
|
|
43
|
-
| `md`
|
|
44
|
-
| `lg`
|
|
45
|
-
| `xl`
|
|
51
|
+
| Token | Min-width | Max content width |
|
|
52
|
+
| -------- | --------- | ----------------- |
|
|
53
|
+
| _(base)_ | 0 | 288px |
|
|
54
|
+
| `xs` | 480px | 448px |
|
|
55
|
+
| `sm` | 768px | 704px |
|
|
56
|
+
| `md` | 1024px | 896px |
|
|
57
|
+
| `lg` | 1200px | 1072px |
|
|
58
|
+
| `xl` | 1440px | 1248px |
|
|
59
|
+
|
|
60
|
+
The grid table below collapses **xl+lg**, **md**, **sm+xs**, and **base** to four implementation tiers; max-content widths still follow the six design sizes in Layout.
|
|
61
|
+
|
|
62
|
+
## Container query breakpoints
|
|
63
|
+
|
|
64
|
+
Container keys (`c_*`) use the **same min-width numbers** as viewport breakpoints, but they apply to the **width of a CSS containment context** (usually a parent), not the browser viewport. Use them when a component must adapt inside sidebars, split layouts, or embeds. Full detail: [Responsive properties — Container Queries](https://gamut.codecademy.com/?path=/docs-foundations-system-responsive-properties--page).
|
|
65
|
+
|
|
66
|
+
| Key | Min container width | Typical use |
|
|
67
|
+
| -------- | ------------------- | ----------------------------------------------------------------------- |
|
|
68
|
+
| `c_base` | 1px | Always matches once a container exists; base style inside the container |
|
|
69
|
+
| `c_xs` | 480px | Matches viewport `xs` threshold, but on **container** width |
|
|
70
|
+
| `c_sm` | 768px | |
|
|
71
|
+
| `c_md` | 1024px | |
|
|
72
|
+
| `c_lg` | 1200px | |
|
|
73
|
+
| `c_xl` | 1440px | |
|
|
74
|
+
|
|
75
|
+
**Requirements**
|
|
76
|
+
|
|
77
|
+
- A **descendant** of an element that establishes a container — e.g. parent `<FlexBox containerType="inline-size">` (or other `container-type`). Without that, `c_*` rules never match.
|
|
78
|
+
- Prefer a **viewport fallback** alongside `c_*` (e.g. `display={{ _: 'block', sm: 'flex', c_md: 'grid' }}`) for browsers or trees without container support.
|
|
79
|
+
|
|
80
|
+
**Object vs array**
|
|
81
|
+
|
|
82
|
+
- **Object:** `p={{ _: 8, c_md: 24 }}` — readable for a few container-only overrides.
|
|
83
|
+
- **Array:** after the six viewport slots (`_` through `xl`), indices **6–11** are `c_base`, `c_xs`, `c_sm`, `c_md`, `c_lg`, `c_xl` respectively. Use when you need the full ordered chain.
|
|
84
|
+
|
|
85
|
+
**When to use which**
|
|
46
86
|
|
|
47
|
-
|
|
87
|
+
- **Viewport keys** (`_`, `xs`, … `xl`) — page-level layout, full-bleed sections, global nav.
|
|
88
|
+
- **Container keys** (`c_base`, … `c_xl`) — reusable widgets whose width is driven by layout, not the device alone.
|
|
48
89
|
|
|
49
|
-
##
|
|
90
|
+
## Page layout grid (12 columns)
|
|
50
91
|
|
|
51
|
-
12-column grid at all breakpoints.
|
|
92
|
+
12-column grid at all breakpoints. Tier columns group breakpoints with identical margin/gutter/row-gap numbers from [Layout](https://gamut.codecademy.com/?path=/docs-foundations-layout--docs).
|
|
52
93
|
|
|
53
|
-
| Property
|
|
54
|
-
|
|
55
|
-
| Horizontal margins | 64px
|
|
56
|
-
| Column gutters
|
|
57
|
-
| Row gaps
|
|
94
|
+
| Property | xl/lg | md | sm/xs | base |
|
|
95
|
+
| ------------------ | ----- | ---- | ----- | ---- |
|
|
96
|
+
| Horizontal margins | 64px | 48px | 32px | 16px |
|
|
97
|
+
| Column gutters | 32px | 24px | 16px | 8px |
|
|
98
|
+
| Row gaps | 32px | 24px | 16px | 8px |
|
|
58
99
|
|
|
59
|
-
Minimum touch target on mobile: **44×44px
|
|
100
|
+
Minimum touch target on mobile: **44×44px** — see `gamut-accessibility` skill for hit-target guidance.
|
|
60
101
|
|
|
61
102
|
## Responsive rules
|
|
62
103
|
|
|
@@ -1,50 +1,83 @@
|
|
|
1
1
|
# Typography
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use **theme typography tokens** (`fontFamily`, `fontSize`, `fontWeight`, `lineHeight`) — never hardcoded font-family strings or magic px for product UI. Prefer `<Text>` from `@codecademy/gamut`, or `system.typography` / `css()` from `@codecademy/gamut-styles` ([`gamut-system-props` skill](../../skills/gamut-system-props/SKILL.md)).
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|---|---|---|---|
|
|
7
|
-
| `base` | Apercu Pro | Hanken Grotesk | All UI text, headlines, body copy |
|
|
8
|
-
| `accent` | Suisse Intl Mono | Hanken Grotesk | Code, captions, labels, technical context |
|
|
9
|
-
| `monospace` | Monaco / Menlo / Consolas | Monaco / Menlo / Consolas | Code editor contexts |
|
|
5
|
+
Source of truth for scales and stacks: [`packages/gamut-styles/src/variables/typography.ts`](https://github.com/Codecademy/gamut/blob/main/packages/gamut-styles/src/variables/typography.ts) and theme builders under [`packages/gamut-styles/src/themes`](https://github.com/Codecademy/gamut/tree/main/packages/gamut-styles/src/themes).
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
**Storybook:** [Typography / Text](https://gamut.codecademy.com/?path=/docs-typography-text--docs) · [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) (system props) · Foundations / Theme: [Core](https://gamut.codecademy.com/?path=/docs-foundations-theme-core-theme--docs), [Percipio](https://gamut.codecademy.com/?path=/docs-foundations-theme-percipio-theme--docs), [LX Studio](https://gamut.codecademy.com/?path=/docs-foundations-theme-lx-studio-theme--docs)
|
|
8
|
+
|
|
9
|
+
**DESIGN.md drift:** [`DESIGN.Percipio.md`](../../DESIGN.Percipio.md) and [`DESIGN.LXStudio.md`](../../DESIGN.LXStudio.md) sometimes describe **Roboto** or **Hanken Grotesk** for those products. **Shipped `gamut-styles` themes** currently use the stacks below (Skillsoft Text / Sans). Treat DESIGN YAML as product narrative until it matches code — confirm with the design platform when they disagree.
|
|
10
|
+
|
|
11
|
+
## Themes × font families
|
|
12
|
+
|
|
13
|
+
Semantic keys (`base`, `accent`, `monospace`, `system`) are stable; resolved stacks depend on `GamutProvider` theme ([`setup.md`](../setup.md)).
|
|
14
|
+
|
|
15
|
+
| Theme | `fontFamily.base` | `fontFamily.accent` | `fontFamily.monospace` | `fontFamily.system` |
|
|
16
|
+
| --------------------------------- | ------------------------------------ | ------------------------------------- | -------------------------------------- | ------------------------------ |
|
|
17
|
+
| **Core**, **Admin**, **Platform** | Apercu stack (`fontBase`) | Suisse + Apercu stack (`fontAccent`) | Monaco / Menlo stack (`fontMonospace`) | System UI stack (`fontSystem`) |
|
|
18
|
+
| **Percipio** | Skillsoft Text (`fontPercipioBase`) | Skillsoft Sans (`fontPercipioAccent`) | Roboto Mono | Roboto (`system`) |
|
|
19
|
+
| **LX Studio** | Same as Percipio (`base` / `accent`) | Same | Same stack as Core (`fontMonospace`) | Same as Core (`fontSystem`) |
|
|
20
|
+
|
|
21
|
+
Admin and Platform extend Core for colors / modes only — typography matches Core.
|
|
22
|
+
|
|
23
|
+
**Licensing:** Apercu is licensed for Codecademy surfaces only; Skillsoft products use Percipio/LX stacks above.
|
|
12
24
|
|
|
13
25
|
## Font size scale
|
|
14
26
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
| Token | Value | Use |
|
|
30
|
-
|---|---|---|
|
|
31
|
-
| `base` | 400 | Body text, UI labels |
|
|
32
|
-
| `title` | 700 | Headlines, CTAs, buttons |
|
|
27
|
+
Values are `rem`-backed keys on `theme.fontSize` (aliases shown as px for readability).
|
|
28
|
+
|
|
29
|
+
| Token | Size | Common use |
|
|
30
|
+
| ----- | ---- | ---------------------------- |
|
|
31
|
+
| `64` | 64px | Hero / display |
|
|
32
|
+
| `44` | 44px | Page titles |
|
|
33
|
+
| `34` | 34px | Section titles |
|
|
34
|
+
| `26` | 26px | Sub-section titles |
|
|
35
|
+
| `22` | 22px | Card titles, large UI labels |
|
|
36
|
+
| `20` | 20px | Secondary titles |
|
|
37
|
+
| `18` | 18px | Large body, intro text |
|
|
38
|
+
| `16` | 16px | Default body text |
|
|
39
|
+
| `14` | 14px | Small body, captions, labels |
|
|
33
40
|
|
|
34
41
|
## Line height
|
|
35
42
|
|
|
36
|
-
| Token
|
|
37
|
-
|
|
38
|
-
| `base`
|
|
39
|
-
| `spacedTitle` | 1.3
|
|
40
|
-
| `title`
|
|
43
|
+
| Token | Value | Use |
|
|
44
|
+
| ------------- | ----- | ---------------------------- |
|
|
45
|
+
| `base` | 1.5 | Body text |
|
|
46
|
+
| `spacedTitle` | 1.3 | Sub-headlines, medium titles |
|
|
47
|
+
| `title` | 1.2 | Large headlines |
|
|
48
|
+
|
|
49
|
+
## Font weight (semantic)
|
|
50
|
+
|
|
51
|
+
Use **semantic keys** on components — do not assume a numeric bold everywhere.
|
|
52
|
+
|
|
53
|
+
| Token | Core / Admin / Platform | Percipio / LX Studio |
|
|
54
|
+
| ------- | ----------------------- | --------------------------------- |
|
|
55
|
+
| `base` | 400 | 400 |
|
|
56
|
+
| `title` | **700** | **500** (`fontWeightMediumTitle`) |
|
|
57
|
+
|
|
58
|
+
Headlines, CTAs, and buttons should use **`fontWeight="title"`** so Percipio/LX get **500**, Core gets **700**. Literal `700` breaks Skillsoft branding on those themes.
|
|
59
|
+
|
|
60
|
+
Numeric **`400`** and **`700`** keys also exist on the theme for rare explicit needs.
|
|
61
|
+
|
|
62
|
+
## Codecademy (Core / Admin / Platform) — voice & layout
|
|
63
|
+
|
|
64
|
+
These UX rules target **Apercu + Suisse** products; do not blindly apply to Percipio/LX without brand guidance.
|
|
65
|
+
|
|
66
|
+
- **`fontFamily="base"` (Apercu):** default UI and marketing type. Emphasis inside body copy: **Italic**, not Bold for intra-paragraph stress.
|
|
67
|
+
- **`fontFamily="accent"` (Suisse stack):** technical accent — code snippets, captions, labels with engineering flavor, figures. Use sparingly; glyph box reads larger — step **down ~10–15%** vs equivalent `base` size; give comfortable line-height.
|
|
68
|
+
- **Alignment:** left-align by default; center only short marketing headlines; avoid right-align except tabs / numerics.
|
|
69
|
+
- **Letter-spacing:** do not tweak tracking unless design specifies.
|
|
70
|
+
- **Line length:** ~45–85 characters per line (~66 ideal for single-column body); constrain container width, not arbitrary CSS letter-spacing.
|
|
71
|
+
|
|
72
|
+
## Line length (all products)
|
|
41
73
|
|
|
42
|
-
|
|
74
|
+
| Context | Target |
|
|
75
|
+
| ------------------ | ------------------- |
|
|
76
|
+
| Single-column body | ~66 chars (max ~85) |
|
|
77
|
+
| Multi-column | ≤50 chars per line |
|
|
78
|
+
| Minimum | ~45 chars |
|
|
43
79
|
|
|
44
|
-
##
|
|
80
|
+
## Related skills
|
|
45
81
|
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
- Use `accent` (Suisse) sparingly: code snippets, captions, enumerated items. Suisse reads ~10–15% large — size it down relative to Apercu equivalents.
|
|
49
|
-
- Left-align text by default. Center-align only for short marketing headlines. Never right-align.
|
|
50
|
-
- Do not adjust letter-spacing.
|
|
82
|
+
- [`gamut-typography`](../../skills/gamut-typography/SKILL.md) — deeper editorial patterns for agents.
|
|
83
|
+
- [`gamut-theming`](../../skills/gamut-theming/SKILL.md) — accessing `theme.font*` in styled components.
|
|
@@ -6,33 +6,35 @@ Gamut is the Codecademy / Skillsoft design system — React component library (`
|
|
|
6
6
|
|
|
7
7
|
**Core principles**:
|
|
8
8
|
|
|
9
|
-
- Components are color mode–aware by default — never hardcode hex values for adaptive UI
|
|
9
|
+
- Components are color mode–aware by default — never hardcode hex values for adaptive, accessible UI
|
|
10
10
|
- All components work across all themes without modification
|
|
11
|
-
-
|
|
12
|
-
-
|
|
11
|
+
- 12-column grid
|
|
12
|
+
- Use **semantic theme tokens** from `@codecademy/gamut-styles` for **color roles** (ColorMode-aware), **typography**, **spacing**, **border radii**, and shared **layout** values (`elements`, …) — not raw palette hex or magic numbers. Defaults support accessible pairings, but **no token set guarantees WCAG AA** for every composition; validate non-standard combinations.
|
|
13
13
|
|
|
14
14
|
**ColorMode in product UI:** Use `<ColorMode>` and `<Background>` from `@codecademy/gamut-styles` for scoped light/dark and contrast-safe surfaces — see [foundations/modes.md](foundations/modes.md) and the `gamut-color-mode` skill. Storybook: [ColorMode](https://gamut.codecademy.com/?path=/docs-foundations-colormode--page), [Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) (semantic tokens + `css` / `variant` / `states`).
|
|
15
15
|
|
|
16
16
|
## Themes
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| **
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
18
|
+
Runtime stacks come from `@codecademy/gamut-styles` (see [foundations/typography.md](foundations/typography.md)). Product `DESIGN.*.md` may differ until reconciled.
|
|
19
|
+
|
|
20
|
+
| Theme | Product | Primary UI fonts (shipped theme) | Dark mode |
|
|
21
|
+
| ------------- | ------------------------------- | ------------------------------------------------------------------------------ | --------- |
|
|
22
|
+
| **Core** | Codecademy (default) | Apercu + Suisse (`accent`) | ✓ |
|
|
23
|
+
| **Admin** | Codecademy admin tools | Same as Core | ✓ |
|
|
24
|
+
| **Platform** | Codecademy learning environment | Same as Core | ✓ |
|
|
25
|
+
| **LX Studio** | LX Studio application | Skillsoft Text / Sans (`base` / `accent`); DESIGN docs may list Hanken Grotesk | — |
|
|
26
|
+
| **Percipio** | Skillsoft Percipio | Skillsoft Text / Sans; DESIGN docs may list Roboto | — |
|
|
25
27
|
|
|
26
28
|
Set the theme at the app root via `<GamutProvider theme={...}>`.
|
|
27
29
|
|
|
28
30
|
## Reading order
|
|
29
31
|
|
|
30
|
-
| File | What it covers
|
|
31
|
-
| ------------------------------------------------------ |
|
|
32
|
-
| [setup.md](setup.md) | Packages, GamutProvider, theme selection
|
|
33
|
-
| [foundations/color.md](foundations/color.md) | Semantic roles (all themes), where to verify hex, Core-only cheatsheets
|
|
34
|
-
| [foundations/modes.md](foundations/modes.md) | Light/dark ColorMode, Background component
|
|
35
|
-
| [foundations/typography.md](foundations/typography.md) |
|
|
36
|
-
| [foundations/spacing.md](foundations/spacing.md) | Spacing,
|
|
37
|
-
| [components/overview.md](components/overview.md) | Full component catalog
|
|
38
|
-
| [components/buttons.md](components/buttons.md) | Button variants, props, decision tree
|
|
32
|
+
| File | What it covers |
|
|
33
|
+
| ------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
34
|
+
| [setup.md](setup.md) | Packages, GamutProvider, theme selection |
|
|
35
|
+
| [foundations/color.md](foundations/color.md) | Semantic roles (all themes), where to verify hex, Core-only cheatsheets |
|
|
36
|
+
| [foundations/modes.md](foundations/modes.md) | Light/dark ColorMode, Background component |
|
|
37
|
+
| [foundations/typography.md](foundations/typography.md) | Theme fonts, scales, semantic `title` weight (700 vs 500), Core voice rules |
|
|
38
|
+
| [foundations/spacing.md](foundations/spacing.md) | Spacing scale, radii, Layout grid; system props + responsive/container queries |
|
|
39
|
+
| [components/overview.md](components/overview.md) | Full component catalog |
|
|
40
|
+
| [components/buttons.md](components/buttons.md) | Button variants, props, decision tree |
|
|
@@ -3,40 +3,79 @@
|
|
|
3
3
|
## Install
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
|
|
6
|
+
yarn add @codecademy/gamut-kit @emotion/react @emotion/styled
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
`gamut-kit` bundles `gamut`, `gamut-icons`, `gamut-illustrations`, `gamut-patterns`, `gamut-styles`, `variance`, and `gamut-tests`.
|
|
10
10
|
|
|
11
|
+
**Full guide:** [Meta / Installation](https://gamut.codecademy.com/?path=/docs-meta-installation--page) in Storybook (CSP `nonce` on `GamutProvider`, Jest, Next/Gatsby entry points). For Emotion + TypeScript, add `theme.d.ts` as in [TypeScript (`theme.d.ts`)](#typescript-themedts) below.
|
|
12
|
+
|
|
13
|
+
Optionally add a `peerDependencies` block in `package.json` listing `@codecademy/gamut`, `@codecademy/gamut-icons`, `@codecademy/gamut-illustrations`, `@codecademy/gamut-patterns`, `@codecademy/gamut-styles`, `@codecademy/gamut-tests`, and `@codecademy/variance` (e.g. `"*"`) so editors surface those packages — see Meta / Installation for the JSON snippet.
|
|
14
|
+
|
|
11
15
|
## Required wrapper
|
|
12
16
|
|
|
13
|
-
Wrap the app root in `<GamutProvider
|
|
17
|
+
Wrap the app root in `<GamutProvider>` from `@codecademy/gamut-styles`. This wires up the theme, color mode, and logical properties for all child components.
|
|
18
|
+
|
|
19
|
+
At runtime, `GamutProvider` defaults to Core when `theme` is omitted (`theme = coreTheme` in the implementation). For non-Core products and for TypeScript (`theme` is required on `GamutProviderProps`), pass `theme` explicitly using the table below.
|
|
14
20
|
|
|
15
21
|
```tsx
|
|
16
|
-
import { GamutProvider } from '@codecademy/gamut';
|
|
17
|
-
import { theme } from '@codecademy/gamut-styles';
|
|
22
|
+
import { GamutProvider, theme } from '@codecademy/gamut-styles';
|
|
18
23
|
|
|
19
24
|
const App = () => (
|
|
20
|
-
<GamutProvider theme={theme}>
|
|
21
|
-
{/* app content */}
|
|
22
|
-
</GamutProvider>
|
|
25
|
+
<GamutProvider theme={theme}>{/* app content */}</GamutProvider>
|
|
23
26
|
);
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
## Theme selection
|
|
27
30
|
|
|
28
|
-
| Product
|
|
29
|
-
|
|
30
|
-
| Codecademy public
|
|
31
|
-
| Codecademy admin
|
|
32
|
-
| Codecademy platform | `platformTheme`
|
|
33
|
-
| LX Studio
|
|
34
|
-
| Percipio
|
|
31
|
+
| Product | Theme to import |
|
|
32
|
+
| ------------------- | ----------------------------- |
|
|
33
|
+
| Codecademy public | `coreTheme` (default `theme`) |
|
|
34
|
+
| Codecademy admin | `adminTheme` |
|
|
35
|
+
| Codecademy platform | `platformTheme` |
|
|
36
|
+
| LX Studio | `lxStudioTheme` |
|
|
37
|
+
| Percipio | `percipioTheme` |
|
|
35
38
|
|
|
36
39
|
All themes are exported from `@codecademy/gamut-styles`.
|
|
37
40
|
|
|
38
|
-
##
|
|
41
|
+
## TypeScript (`theme.d.ts`)
|
|
42
|
+
|
|
43
|
+
Augment `@emotion/react` so `props.theme` in `styled` / `css` matches the **same theme object** you pass to `<GamutProvider theme={...}>`. If the types disagree, system props and token autocomplete will not line up with runtime.
|
|
44
|
+
|
|
45
|
+
Add a root `theme.d.ts` (or merge into your existing global types):
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// theme.d.ts
|
|
49
|
+
import '@emotion/react';
|
|
50
|
+
|
|
51
|
+
import type { CoreTheme } from '@codecademy/gamut-styles';
|
|
52
|
+
|
|
53
|
+
declare module '@emotion/react' {
|
|
54
|
+
export interface Theme extends CoreTheme {}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use the **theme interface that matches your provider** — same row as the [theme selection](#theme-selection) table:
|
|
59
|
+
|
|
60
|
+
| `GamutProvider` `theme` prop | Import for `Theme extends …` |
|
|
61
|
+
| ---------------------------- | ---------------------------- |
|
|
62
|
+
| `theme` or `coreTheme` | `CoreTheme` |
|
|
63
|
+
| `adminTheme` | `AdminTheme` |
|
|
64
|
+
| `platformTheme` | `PlatformTheme` |
|
|
65
|
+
| `lxStudioTheme` | `LxStudioTheme` |
|
|
66
|
+
| `percipioTheme` | `PercipioTheme` |
|
|
67
|
+
|
|
68
|
+
Example when the app uses Percipio:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
// theme.d.ts
|
|
72
|
+
import '@emotion/react';
|
|
73
|
+
|
|
74
|
+
import type { PercipioTheme } from '@codecademy/gamut-styles';
|
|
75
|
+
|
|
76
|
+
declare module '@emotion/react' {
|
|
77
|
+
export interface Theme extends PercipioTheme {}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
- LX Studio → Hanken Grotesk
|
|
42
|
-
- Percipio → Roboto
|
|
81
|
+
See Emotion’s [TypeScript / define a theme](https://emotion.sh/docs/typescript#define-a-theme) for details.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Apply these guardrails when editing Gamut UI in TS/JS/TSX/JSX.
|
|
2
|
+
description: Apply these guardrails when editing Gamut UI in TS/JS/TSX/JSX. Universal rules (always loaded). Form wiring depth: **`gamut-forms`** skill; other component matrix and audit detail: **`gamut-accessibility`** — those skills do not repeat this rule set.
|
|
3
3
|
alwaysApply: true
|
|
4
4
|
globs: ['*.tsx', '*.ts', '*.jsx', '*.js']
|
|
5
5
|
---
|
|
@@ -39,21 +39,20 @@ When there is no visible text for a nameable element, consider this a sign that
|
|
|
39
39
|
</ul>
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
## Use Gamut
|
|
42
|
+
## Use Gamut primitives — do not fake buttons or dialogs
|
|
43
43
|
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
- `<Dialog>` / `<Modal>` — always provide an accessible name; **prefer `aria-labelledby`** to a visible title when one exists, otherwise `aria-label`. Focus lock and Escape are handled by the components.
|
|
44
|
+
- **Actions:** use Gamut button atoms (`FillButton`, `TextButton`, `StrokeButton`, `CTAButton`, `IconButton`) — not `<div onClick>`, not `<span role="button">`, not `<a>` without `href` for actions. Variant and `tip` guidance: [`guidelines/components/buttons.md`](../guidelines/components/buttons.md).
|
|
45
|
+
- **Tabs / overlays:** `Tabs`, `Dialog`, `Modal`, and related primitives implement keyboard and focus patterns in code — still supply labels, titles, and trigger semantics as documented in the skill.
|
|
47
46
|
|
|
48
47
|
## Every interactive control needs an accessible name
|
|
49
48
|
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
49
|
+
- **`IconButton`** — provide `tip` (accessible name for icon-only).
|
|
50
|
+
- **`InfoTip`** — provide `ariaLabel` or `ariaLabelledby`; there is no automatic fallback.
|
|
51
|
+
- Decorative icon SVGs next to visible text — `aria-hidden="true"` on the icon.
|
|
53
52
|
|
|
54
53
|
## Form label association
|
|
55
54
|
|
|
56
|
-
Match
|
|
55
|
+
Match **`htmlFor`** on **`<FormGroupLabel>`** with the **`id`** on the control. Base **`<FormGroup>`** renders live regions for **`error`** and **`description`**; **`GridForm`** and **`ConnectedForm`** add field wiring (**`aria-describedby`**, **`aria-invalid`**, first-error **`aria-live`** behavior) — do not add redundant duplicate regions. Depth: **[`skills/gamut-forms/SKILL.md`](../skills/gamut-forms/SKILL.md)**.
|
|
57
56
|
|
|
58
57
|
## Screen-reader-only text
|
|
59
58
|
|
|
@@ -61,8 +60,19 @@ Use `<Text screenreader>` for visually hidden but announced content. `<HiddenTex
|
|
|
61
60
|
|
|
62
61
|
## Color and contrast
|
|
63
62
|
|
|
64
|
-
Do not hardcode hex
|
|
63
|
+
Do not hardcode hex for adaptive UI. Prefer semantic tokens and **`ColorMode` / `<Background>`** so surfaces track theme and mode — see the **`gamut-color-mode`** skill and [`foundations/modes.md`](../guidelines/foundations/modes.md). Default pairings support accessible UI, but **tokens do not guarantee WCAG compliance** for every layout; validate non-standard combinations.
|
|
65
64
|
|
|
66
65
|
## Focus visibility
|
|
67
66
|
|
|
68
|
-
Never suppress focus indicators with `outline: none` or `outline: 0` without a visible replacement. Gamut
|
|
67
|
+
Never suppress focus indicators with `outline: none` or `outline: 0` without a visible replacement. Gamut’s focus styles are intentional (WCAG 2.4.7).
|
|
68
|
+
|
|
69
|
+
## Where to read more (minimal index)
|
|
70
|
+
|
|
71
|
+
| Topic | Primary doc |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| Forms (`GridForm`, `ConnectedForm`, `FormGroup`, validation, live regions) | [`skills/gamut-forms/SKILL.md`](../skills/gamut-forms/SKILL.md) |
|
|
74
|
+
| Component matrix (tips, overlays, composites, checklists; not form wiring) | [`skills/gamut-accessibility/SKILL.md`](../skills/gamut-accessibility/SKILL.md) |
|
|
75
|
+
| Button variants, `IconButton` `tip`, `disabled` vs `aria-disabled` | [`guidelines/components/buttons.md`](../guidelines/components/buttons.md) |
|
|
76
|
+
| ColorMode, `Background`, semantic color roles | **`gamut-color-mode`** skill · [`foundations/modes.md`](../guidelines/foundations/modes.md) · [`foundations/color.md`](../guidelines/foundations/color.md) |
|
|
77
|
+
| Tokens, `css` / `variant` / `states` | Storybook [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) |
|
|
78
|
+
| Install, `GamutProvider`, CSP | Storybook [Meta / Installation](https://gamut.codecademy.com/?path=/docs-meta-installation--page) |
|