@codecademy/gamut 68.6.1 → 68.6.2-alpha.671e56.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/.claude-plugin/marketplace.json +16 -0
- package/agent-tools/.claude-plugin/plugin.json +7 -0
- package/agent-tools/.cursor-plugin/plugin.json +7 -0
- package/agent-tools/DESIGN.Codecademy.md +643 -0
- package/agent-tools/DESIGN.LXStudio.md +437 -0
- package/agent-tools/DESIGN.Percipio.md +433 -0
- package/agent-tools/DESIGN.md +1 -0
- package/agent-tools/agents/.gitkeep +0 -0
- package/agent-tools/rules/accessibility.mdc +78 -0
- package/agent-tools/skills/gamut-accessibility/SKILL.md +214 -0
- package/agent-tools/skills/gamut-buttons/SKILL.md +96 -0
- package/agent-tools/skills/gamut-color-mode/SKILL.md +257 -0
- package/agent-tools/skills/gamut-forms/SKILL.md +84 -0
- package/agent-tools/skills/gamut-layout/SKILL.md +109 -0
- package/agent-tools/skills/gamut-list/SKILL.md +273 -0
- package/agent-tools/skills/gamut-review/SKILL.md +254 -0
- package/agent-tools/skills/gamut-style-utilities/SKILL.md +107 -0
- package/agent-tools/skills/gamut-system-props/SKILL.md +203 -0
- package/agent-tools/skills/gamut-testing/SKILL.md +221 -0
- package/agent-tools/skills/gamut-theming/SKILL.md +115 -0
- package/agent-tools/skills/gamut-typography/SKILL.md +98 -0
- package/bin/commands/plugin/install.mjs +212 -0
- package/bin/commands/plugin/list.mjs +73 -0
- package/bin/commands/plugin/remove.mjs +108 -0
- package/bin/commands/plugin/update.mjs +59 -0
- package/bin/gamut.mjs +96 -0
- package/bin/lib/claude.mjs +52 -0
- package/bin/lib/cursor.mjs +40 -0
- package/bin/lib/design.mjs +71 -0
- package/bin/lib/io.mjs +14 -0
- package/bin/lib/resolve-plugin-dir.mjs +38 -0
- package/bin/lib/run-command.mjs +22 -0
- package/package.json +11 -8
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gamut-review
|
|
3
|
+
description: Use this skill when auditing existing code for Gamut usage — dependencies, GamutProvider, deep imports, hardcoded hex colors, and test patterns — and you need a consolidated report with pointers to matching Gamut skills.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Gamut Review
|
|
7
|
+
|
|
8
|
+
Audit existing code at the path the user provides (default: current working directory). Find violations and misuse; do not generate new code.
|
|
9
|
+
|
|
10
|
+
When `DESIGN.md` is present at the audit root, use it as the authoritative reference for product design intent, token names, and component patterns. It is copied from `DESIGN.Codecademy.md`, `DESIGN.Percipio.md`, or `DESIGN.LXStudio.md` in `@codecademy/gamut` agent-tools (via `gamut plugin install --theme <name>`). When a finding maps to a skill, note it in the report so the developer knows where to get remediation guidance.
|
|
11
|
+
|
|
12
|
+
Run Check 0 first, then Checks 1–5, then print a single consolidated report using the format at the end of this file.
|
|
13
|
+
|
|
14
|
+
Remediation skills: [`gamut-theming`](../gamut-theming/SKILL.md) · [`gamut-color-mode`](../gamut-color-mode/SKILL.md) · [`gamut-testing`](../gamut-testing/SKILL.md)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Check 0 — DESIGN.md present
|
|
19
|
+
|
|
20
|
+
Resolve the audit root from the user's path if provided, otherwise the current working directory. Look for `DESIGN.md` at that root (not inside `node_modules` or package subfolders unless the audit path is explicitly that folder).
|
|
21
|
+
|
|
22
|
+
| Result | Action |
|
|
23
|
+
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| Found | Report `✓ DESIGN.md present (<path>)`. Proceed with Checks 1–5 using this file for product/theme context. |
|
|
25
|
+
| Missing | Report `✗ DESIGN.md not found` as a blocking finding. Include remediation: from the repo root run `gamut plugin install cursor --theme core` (or `percipio`, `lxstudio`, `admin`, `platform`; also `claude`), or manually copy the matching `DESIGN.*.md` from `@codecademy/gamut` agent-tools and rename to `DESIGN.md`. Still run Checks 1–3 and 5. For Check 4, list hex violations with `palette:` / `semantic:` only where Appendix A/B apply without product YAML — prefix the Hardcoded colors section with `⚠ low confidence — no DESIGN.md` and do not assume Codecademy Core semantics; do not use Appendix B shortcuts as authoritative. |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Check 1 — Dependencies
|
|
30
|
+
|
|
31
|
+
Read `package.json` at the audit root. Inspect `dependencies`, `devDependencies`, and `peerDependencies` combined.
|
|
32
|
+
|
|
33
|
+
| Package | Expectation |
|
|
34
|
+
| -------------------------- | ------------------------------------------------------- |
|
|
35
|
+
| `@codecademy/gamut` | Required — core component library |
|
|
36
|
+
| `@codecademy/gamut-styles` | Recommended — design tokens and theme primitives |
|
|
37
|
+
| `@codecademy/variance` | Recommended — style-prop system used by Gamut internals |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Check 2 — Setup
|
|
42
|
+
|
|
43
|
+
First detect whether the project uses TypeScript by checking for `tsconfig.json` at the audit root or `typescript` in `package.json` `dependencies`/`devDependencies`. Use this to set severity for the Theme augmentation row below.
|
|
44
|
+
|
|
45
|
+
Search source files (`.ts`, `.tsx`, `.js`, `.jsx`) for these symbols. Skip `node_modules`, `dist`, `.next`, `build`, `.turbo`.
|
|
46
|
+
|
|
47
|
+
| Symbol | Expectation |
|
|
48
|
+
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `GamutProvider` | Required — must appear at least once (app root wrapper) |
|
|
50
|
+
| `ColorMode` | Recommended — enables semantic light/dark theming |
|
|
51
|
+
| `Background` | Recommended — semantic surface color via `@codecademy/gamut-styles` |
|
|
52
|
+
| `declare module '@emotion/react'` | **Required if TypeScript** — `Theme` must be augmented with the active theme type (e.g. `CoreTheme`) so scale props type-check correctly; grep `.d.ts` and `.ts`/`.tsx` source files for this string. **Recommended if not TypeScript** — note that adopting TypeScript is recommended and that `theme.d.ts` will be needed when it is. |
|
|
53
|
+
|
|
54
|
+
For each found symbol report the first file path where it appears.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Check 3 — Import patterns
|
|
59
|
+
|
|
60
|
+
Grep source files for any of these patterns. Each match is an error.
|
|
61
|
+
|
|
62
|
+
| Pattern | Reason |
|
|
63
|
+
| ------------------------------- | ----------------------------------------------------------------------- |
|
|
64
|
+
| `@codecademy/gamut/dist/` | Deep dist import — bypasses public API and breaks on internal refactors |
|
|
65
|
+
| `@codecademy/gamut/src/` | Deep src import — not part of the published package |
|
|
66
|
+
| `@codecademy/gamut-styles/src/` | Deep src import — use the package root |
|
|
67
|
+
| `@codecademy/variance/src/` | Deep src import — use the package root |
|
|
68
|
+
|
|
69
|
+
Report each violation as `file:line`.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Check 4 — Hardcoded colors (semantic-first)
|
|
74
|
+
|
|
75
|
+
Rule: Inline hex literals in application UI code are violations. Remediation is not “replace hex with `navy-800`” — prefer semantic ColorMode tokens (`text`, `background`, `primary`, …) so light/dark and theme switches stay correct. Reserve raw palette tokens for colors that must stay fixed and for `bg` on `<Background>` from `@codecademy/gamut-styles` (section surfaces with content).
|
|
76
|
+
|
|
77
|
+
Align findings with project docs and Storybook:
|
|
78
|
+
|
|
79
|
+
- [`gamut-color-mode`](../gamut-color-mode/SKILL.md) skill — semantic alias tables and decision guide.
|
|
80
|
+
- [Foundations / ColorMode](https://gamut.codecademy.com/?path=/docs-foundations-colormode--page) — aliases per mode; `<Background>` behavior.
|
|
81
|
+
- [Meta / Best practices](https://gamut.codecademy.com/?path=/docs-meta-best-practices--page) — semantic colors + `css` / `variant` / `states` from `gamut-styles`.
|
|
82
|
+
- Foundations / Theme stories (Core, Admin, Platform, Percipio, LX Studio) — verify hex ↔ semantic if the product is not Codecademy Core.
|
|
83
|
+
|
|
84
|
+
Theme context: If Check 0 passed, infer product/theme from root `DESIGN.md` and `GamutProvider` / app config. If Check 0 failed, follow the low-confidence rules in Check 0 — do not assume Codecademy Core semantics. If `DESIGN.md` exists but theme is still unclear, add a report note to confirm against the correct theme Storybook page.
|
|
85
|
+
|
|
86
|
+
Discovery: Grep source files (`.ts`, `.tsx`, `.js`, `.jsx`, `.css`, `.scss`, `.less`) for inline hex literals (`#RGB` or `#RRGGBB`). Comparison is case-insensitive. Skip `node_modules`, `dist`, `.next`, `build`, `.turbo` (same spirit as other checks).
|
|
87
|
+
|
|
88
|
+
### Workflow (each hex match)
|
|
89
|
+
|
|
90
|
+
1. Context — Inspect the surrounding line(s): CSS property (`color`, `background`, `border-color`, …), JSX prop (`color`, `bg`, `borderColor`, SVG fill), or asset. Note whether the subtree is a section with content (candidate for `<Background>` + palette `bg`) vs component chrome (prefer semantics).
|
|
91
|
+
2. Identify palette — Normalize hex (case-insensitive); map to a Gamut palette name using Appendix A below. If missing from the appendix, match against `DESIGN.md` / `packages/gamut-styles` palette definitions.
|
|
92
|
+
3. Recommend semantic first — Use Appendix B (Core light literals) plus role:
|
|
93
|
+
- Body / UI foreground → `text`; strong emphasis → `text-accent`.
|
|
94
|
+
- Page or card fill → `background` / `background-primary` / state surfaces (`background-success`, `background-warning`, `background-error`).
|
|
95
|
+
- CTAs, links, hyper accents → `primary` (+ `primary-hover` on hover).
|
|
96
|
+
- Ghost / secondary buttons → `secondary`.
|
|
97
|
+
- Destructive → `danger` / `danger-hover`.
|
|
98
|
+
- Dividers / outlines → `border-primary` / `border-secondary` / `border-tertiary`.
|
|
99
|
+
- Inline feedback copy → `feedback-error` / `feedback-success` / `feedback-warning`.
|
|
100
|
+
- Disambiguation: `#FFD300` — warning copy → `feedback-warning`; yellow accent on top of primary-colored surfaces → `primary-inverse`.
|
|
101
|
+
- Same hex can map to multiple semantics (e.g. `#10162F` → `text` vs `border-primary` vs `secondary`): pick from property + component role.
|
|
102
|
+
4. When palette-only is OK — `bg` prop on `<Background>` (`<Background bg="hyper">`, etc.) is the primary place for fixed surface palette colors on sections. After replacing hex there, use a named palette token, not hex. Exceptions (flag with rationale): charts/data viz, third-party widgets, exported static illustrations — still prefer tokens over hex when feasible.
|
|
103
|
+
|
|
104
|
+
Severity: Hex on adaptive UI (random wrappers, `styled-components`, inline `style`) → error. Hex inside documented exceptions → warning with note.
|
|
105
|
+
|
|
106
|
+
Reporting: For each match outside token definition files:
|
|
107
|
+
|
|
108
|
+
`file:line 'HEX' → semantic: <token(s)> | palette: <token> | note: <theme/disambiguation>`
|
|
109
|
+
|
|
110
|
+
Use `semantic: (n/a)` only when no semantic applies (e.g. pure illustration); still give `palette: …`. If unmappable: `→ no Gamut token`.
|
|
111
|
+
|
|
112
|
+
Ignore hex inside design token definition files (e.g. `variables/colors.ts`, `_colors.scss`) — source of truth, not violations.
|
|
113
|
+
|
|
114
|
+
### Appendix B — Core light: hex → suggested semantic (shortcut)
|
|
115
|
+
|
|
116
|
+
Use with step 3; verify for non-Core themes.
|
|
117
|
+
|
|
118
|
+
| Hex (normalized) | Typical semantic direction |
|
|
119
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
120
|
+
| `#10162f` | `text`, `border-primary`, or `secondary` (by role) |
|
|
121
|
+
| `#0a0d1c` | `text-accent` |
|
|
122
|
+
| `#ffffff` | `background` (fills), `secondary` (inverse ghost on dark — rare in light-only grep context) |
|
|
123
|
+
| `#fff0e5` | `background-primary` |
|
|
124
|
+
| `#f5ffe3` | `background-success` |
|
|
125
|
+
| `#fffae5` | `background-warning` |
|
|
126
|
+
| `#fbf1f0` | `background-error` |
|
|
127
|
+
| `#3a10e5` | `primary` |
|
|
128
|
+
| `#5533ff` | `primary-hover` |
|
|
129
|
+
| `#ffd300` | `feedback-warning` or `primary-inverse` (see disambiguation above) |
|
|
130
|
+
| `#cca900` | Often pairs with hover in dark mode; in light UI as literal hex → check palette appendix (`yellow-400`) then assign role |
|
|
131
|
+
| `#e91c11` | `danger` |
|
|
132
|
+
| `#be1809` | `danger-hover`, `feedback-error` |
|
|
133
|
+
| `#008a27` | `feedback-success` |
|
|
134
|
+
|
|
135
|
+
Hexes with no row above still get Appendix A palette id + role-based semantic guess (e.g. blue scale → often decorative or legacy marketing; prefer design review unless mapping clearly to `primary`).
|
|
136
|
+
|
|
137
|
+
### Appendix A — Hex → palette token (identification only)
|
|
138
|
+
|
|
139
|
+
Case-insensitive. Use to label `palette:` in the report; do not stop at this step without Appendix B / role triage.
|
|
140
|
+
|
|
141
|
+
| Hex | Token |
|
|
142
|
+
| --------- | -------------------------- |
|
|
143
|
+
| `#000000` | `black` |
|
|
144
|
+
| `#ffffff` | `white` |
|
|
145
|
+
| `#10162f` | `navy` / `navy-800` |
|
|
146
|
+
| `#0a0d1c` | `navy-900` |
|
|
147
|
+
| `#fff0e5` | `beige-100` |
|
|
148
|
+
| `#f5fcff` | `blue-0` |
|
|
149
|
+
| `#d3f2ff` | `blue-100` |
|
|
150
|
+
| `#66c4ff` | `blue-300` |
|
|
151
|
+
| `#3388ff` | `blue-400` |
|
|
152
|
+
| `#1557ff` | `blue-500` |
|
|
153
|
+
| `#1d2340` | `blue-800` |
|
|
154
|
+
| `#f5ffe3` | `green-0` |
|
|
155
|
+
| `#eafdc6` | `green-100` |
|
|
156
|
+
| `#aee938` | `green-400` / `lightGreen` |
|
|
157
|
+
| `#008a27` | `green-700` |
|
|
158
|
+
| `#151c07` | `green-900` |
|
|
159
|
+
| `#fffae5` | `yellow-0` |
|
|
160
|
+
| `#cca900` | `yellow-400` |
|
|
161
|
+
| `#ffd300` | `yellow-500` / `yellow` |
|
|
162
|
+
| `#211b00` | `yellow-900` |
|
|
163
|
+
| `#fff5ff` | `pink-0` |
|
|
164
|
+
| `#f966ff` | `pink-400` / `pink` |
|
|
165
|
+
| `#fbf1f0` | `red-0` |
|
|
166
|
+
| `#e85d7f` | `red-300` |
|
|
167
|
+
| `#dc5879` | `red-400` / `paleRed` |
|
|
168
|
+
| `#e91c11` | `red-500` / `red` |
|
|
169
|
+
| `#be1809` | `red-600` |
|
|
170
|
+
| `#280503` | `red-900` |
|
|
171
|
+
| `#ffe8cc` | `orange-100` |
|
|
172
|
+
| `#ff8c00` | `orange-500` / `orange` |
|
|
173
|
+
| `#5533ff` | `hyper-400` |
|
|
174
|
+
| `#3a10e5` | `hyper-500` / `hyper` |
|
|
175
|
+
| `#f5f5f5` | `gray-100` |
|
|
176
|
+
| `#eeeeee` | `gray-200` |
|
|
177
|
+
| `#e0e0e0` | `gray-300` |
|
|
178
|
+
| `#9e9e9e` | `gray-600` |
|
|
179
|
+
| `#616161` | `gray-800` |
|
|
180
|
+
| `#424242` | `gray-900` |
|
|
181
|
+
| `#fffbf8` | `beige-0` |
|
|
182
|
+
| `#8a7300` | `gold-800` / `gold` |
|
|
183
|
+
| `#d14900` | `orange-800` |
|
|
184
|
+
| `#ca00d1` | `pink-800` |
|
|
185
|
+
| `#006d82` | `teal-500` / `teal` |
|
|
186
|
+
| `#b3ccff` | `purple-300` / `purple` |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Check 5 — Test setup
|
|
191
|
+
|
|
192
|
+
Grep test files (`**/__tests__/**/*.{ts,tsx}`, `**/*.test.{ts,tsx}`, `**/*.spec.{ts,tsx}`) for these patterns. Skip `node_modules`, `dist`.
|
|
193
|
+
|
|
194
|
+
| Pattern | Verdict | Reason |
|
|
195
|
+
| ----------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
196
|
+
| `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 |
|
|
197
|
+
| `jest.mock\(.*@codecademy/gamut-styles` | Error | Same issue as above — mocking gamut-styles breaks token resolution |
|
|
198
|
+
| `from '@codecademy/gamut-tests'` | Good — report count of files using it | Correct import for `setupRtl` and `MockGamutProvider` |
|
|
199
|
+
| `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 |
|
|
200
|
+
| `new GamutProvider` or `<GamutProvider` in test files | Warning | Prefer `setupRtl`; use `MockGamutProvider` (sets `useCache={false}`, `useGlobals={false}`) in harnesses or stories, not `GamutProvider` directly |
|
|
201
|
+
|
|
202
|
+
Skill reference for remediation: [`gamut-testing`](../gamut-testing/SKILL.md)
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Output format
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
Gamut Review — <absolute path>
|
|
210
|
+
══════════════════════════════════════════════════
|
|
211
|
+
|
|
212
|
+
DESIGN.md
|
|
213
|
+
✓ present <path>/DESIGN.md
|
|
214
|
+
✗ missing run: gamut plugin install cursor --theme <core|percipio|lxstudio|…> [blocking for color audit]
|
|
215
|
+
|
|
216
|
+
Dependencies
|
|
217
|
+
✓ @codecademy/gamut <version>
|
|
218
|
+
⚠ @codecademy/gamut-styles not found — recommended
|
|
219
|
+
✗ @codecademy/variance not found — recommended
|
|
220
|
+
|
|
221
|
+
Setup
|
|
222
|
+
✓ GamutProvider found (src/App.tsx)
|
|
223
|
+
⚠ ColorMode not found — use ColorMode for light/dark theming [→ gamut-color-mode]
|
|
224
|
+
⚠ Background not found — use <Background> for semantic surfaces [→ gamut-color-mode]
|
|
225
|
+
✗ Theme augmentation not found — create src/theme.d.ts extending CoreTheme [→ gamut-theming]
|
|
226
|
+
(⚠ if TypeScript not detected: TypeScript not detected — recommended to adopt TypeScript; add src/theme.d.ts extending CoreTheme when you do [→ gamut-theming])
|
|
227
|
+
|
|
228
|
+
Import patterns
|
|
229
|
+
✓ Deep dist imports none found
|
|
230
|
+
✗ Deep src imports 2 occurrences
|
|
231
|
+
src/Thing.tsx:7
|
|
232
|
+
src/Other.tsx:12
|
|
233
|
+
|
|
234
|
+
Hardcoded colors [→ gamut-color-mode]
|
|
235
|
+
✗ src/Card.tsx:22 '#10162F' → semantic: text | palette: navy-800 | note: Core light body copy
|
|
236
|
+
⚠ src/Hero.tsx:14 '#1557FF' → semantic: primary (if link/CTA) | palette: blue-500 | note: no exact semantic; confirm theme
|
|
237
|
+
⚠ src/Nav.tsx:8 '#BADA55' → semantic: (n/a) | palette: — | note: no Gamut token
|
|
238
|
+
|
|
239
|
+
Test setup [→ gamut-testing]
|
|
240
|
+
✓ @codecademy/gamut-tests used in 12 test files
|
|
241
|
+
✗ jest.mock(@codecademy/gamut) 2 occurrences — remove; prefer setupRtl (or harness + setupRtl)
|
|
242
|
+
src/components/Foo/__tests__/Foo.test.tsx:3
|
|
243
|
+
src/components/Bar/__tests__/Bar.test.tsx:5
|
|
244
|
+
⚠ direct component-test-setup import 1 occurrence — import from @codecademy/gamut-tests
|
|
245
|
+
src/components/Baz/__tests__/Baz.test.tsx:2
|
|
246
|
+
|
|
247
|
+
══════════════════════════════════════════════════
|
|
248
|
+
<N> error(s), <N> warning(s) found. (or "All checks passed." if none)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Icons: `✓` = pass, `⚠` = warning (recommended, not required), `✗` = error (required).
|
|
252
|
+
`[→ skill-name]` annotations indicate which Gamut skill has remediation guidance for that category.
|
|
253
|
+
|
|
254
|
+
After printing the report, offer one sentence of prioritized next-step advice based on what was found.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gamut-style-utilities
|
|
3
|
+
description: Use this skill when authoring Gamut styles with @codecademy/gamut-styles — css(), variant(), states(), StyleProps from variance, or the useTheme() escape hatch; choosing between these APIs and system props; semantic tokens with ColorMode.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Gamut style utilities
|
|
7
|
+
|
|
8
|
+
Source: `@codecademy/gamut-styles` — [`variance/props.ts`](https://github.com/Codecademy/gamut/blob/main/packages/gamut-styles/src/variance/props.ts) (`css`, `variant`, `states` built on `PROPERTIES.all`).
|
|
9
|
+
|
|
10
|
+
See also: [`gamut-theming`](../gamut-theming/SKILL.md) (which theme, `GamutProvider`, new themes). [`gamut-system-props`](../gamut-system-props/SKILL.md) (`system.*`, responsive props, `Box`). [`gamut-color-mode`](../gamut-color-mode/SKILL.md) (semantic color, `<ColorMode>`, `<Background>`). [Best practices](../../../../styleguide/src/lib/Meta/Best%20practices.mdx) and [system compose](https://gamut.codecademy.com/?path=/docs-foundations-system-compose--page).
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Use `css()`, `variant()`, and `states()` from `@codecademy/gamut-styles` for typed, token-scaled style objects (same scales as composed `system.*` props). Prefer semantic color keys so styles track ColorMode and theme.
|
|
15
|
+
|
|
16
|
+
For layout-heavy styled components, prefer composing `system.*` via `variance.compose()` (see `gamut-system-props`) instead of re-stating every longhand in `css()`.
|
|
17
|
+
|
|
18
|
+
## `css()` — static style objects
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { css } from '@codecademy/gamut-styles';
|
|
22
|
+
import styled from '@emotion/styled';
|
|
23
|
+
|
|
24
|
+
const Box = styled.div(css({ bg: 'navy-400', p: 4 }));
|
|
25
|
+
const Text = styled.div(css({ color: 'primary', p: 4 }));
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## `variant()` and `states()` — branching and toggles
|
|
29
|
+
|
|
30
|
+
- `variant()` — mutually exclusive modes: `base`, `defaultVariant`, and a `variants` map (semantic colors, spacing shorthands, nested selectors such as `'&:hover'`).
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { variant } from '@codecademy/gamut-styles';
|
|
34
|
+
import styled from '@emotion/styled';
|
|
35
|
+
|
|
36
|
+
const Anchor = styled.a(
|
|
37
|
+
variant({
|
|
38
|
+
base: { p: 4 },
|
|
39
|
+
defaultVariant: 'interface',
|
|
40
|
+
variants: {
|
|
41
|
+
interface: {
|
|
42
|
+
color: 'text',
|
|
43
|
+
'&:hover': { color: 'text-accent' },
|
|
44
|
+
},
|
|
45
|
+
inline: {
|
|
46
|
+
color: 'primary',
|
|
47
|
+
'&:hover': { color: 'secondary' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `states()` — independent boolean-style props (`base` + named keys).
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { states } from '@codecademy/gamut-styles';
|
|
58
|
+
import styled from '@emotion/styled';
|
|
59
|
+
|
|
60
|
+
const UtilityBox = styled.div(
|
|
61
|
+
states({
|
|
62
|
+
base: { mx: 4, my: 8, p: 16 },
|
|
63
|
+
disabled: { bg: 'background-disabled', color: 'text-disabled' },
|
|
64
|
+
center: { alignItems: 'center', justifyContent: 'center' },
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `StyleProps` on React components
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import { states } from '@codecademy/gamut-styles';
|
|
73
|
+
import { StyleProps } from '@codecademy/variance';
|
|
74
|
+
import styled from '@emotion/styled';
|
|
75
|
+
|
|
76
|
+
const panelShellStates = states({ base: { p: 4 }, dense: { p: 2 } });
|
|
77
|
+
const PanelShell = styled.div(panelShellStates);
|
|
78
|
+
|
|
79
|
+
export type PanelShellProps = StyleProps<typeof panelShellStates> & {
|
|
80
|
+
title: string;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const Panel: React.FC<PanelShellProps> = ({ title, ...rest }) => (
|
|
84
|
+
<PanelShell {...rest}>{title}</PanelShell>
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Prefer `variant` / `states` over branching on raw `theme` fields inside styled template literals when the output is Emotion-managed CSS.
|
|
89
|
+
|
|
90
|
+
## `useTheme()` — escape hatch
|
|
91
|
+
|
|
92
|
+
Prefer `css()`, `system.*`, `variant()`, and `states()` for styling. Use `useTheme()` from `@emotion/react` when a token value must be read in plain JS (charts, canvas, third-party props), not as the default way to color DOM nodes.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { useTheme } from '@emotion/react';
|
|
96
|
+
|
|
97
|
+
const Sparkline = () => {
|
|
98
|
+
const theme = useTheme();
|
|
99
|
+
return <path strokeWidth={theme.spacing[4]} d="M0 0 L10 10" />;
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Key principles
|
|
104
|
+
|
|
105
|
+
- Prefer semantic color keys in `css` / `variant` / `states` so ColorMode and theme switches apply; see `gamut-color-mode`.
|
|
106
|
+
- Never hardcode hex in component styles — use tokens / semantic aliases.
|
|
107
|
+
- Prefer `variant` / `states` for modes and toggles instead of ad-hoc `theme` interpolation in template literals.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gamut-system-props
|
|
3
|
+
description: Use this skill when building or refactoring styled Gamut components that need layout, spacing, color, border, background, typography, positioning, grid, flex, shadow, list styles, or responsive values from @codecademy/gamut-styles — including composing system prop groups with variance.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Gamut System Props
|
|
7
|
+
|
|
8
|
+
Source: `@codecademy/gamut-styles` — [`variance/config.ts`](https://github.com/Codecademy/gamut/blob/main/packages/gamut-styles/src/variance/config.ts) (definitions) and [`variance/props.ts`](https://github.com/Codecademy/gamut/blob/main/packages/gamut-styles/src/variance/props.ts) (`variance.create` groups). `Box`, `FlexBox`, and `GridBox` compose the same groups in [`packages/gamut/src/Box/props.ts`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Box/props.ts).
|
|
9
|
+
|
|
10
|
+
See also: [`gamut-style-utilities`](../gamut-style-utilities/SKILL.md) (`css`, `variant`, `states`, `StyleProps`). [Styleguide — Best practices](../../../../styleguide/src/lib/Meta/Best%20practices.mdx) (semantic colors, responsive examples) and Storybook [Responsive properties](https://gamut.codecademy.com/storybook/?path=/docs-foundations-system-responsive-properties--page).
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
System props are strongly-typed, theme-connected CSS prop groups from `@codecademy/gamut-styles`. They give styled components a consistent, responsive API. All props are built on top of `@codecademy/variance`.
|
|
15
|
+
|
|
16
|
+
Each prop group has:
|
|
17
|
+
|
|
18
|
+
- `properties`: The CSS properties it controls
|
|
19
|
+
- `scale`: Token scale it's restricted to (theme colors, spacing values, etc.)
|
|
20
|
+
- `transform`: Optional transform applied before output (e.g. `width={0.5}` → `width: 50%`)
|
|
21
|
+
|
|
22
|
+
## Basic usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import styled from '@emotion/styled';
|
|
26
|
+
import { system } from '@codecademy/gamut-styles';
|
|
27
|
+
|
|
28
|
+
// Apply a single group
|
|
29
|
+
const Box = styled.div(system.layout);
|
|
30
|
+
|
|
31
|
+
// Compose multiple groups
|
|
32
|
+
import { variance } from '@codecademy/variance';
|
|
33
|
+
|
|
34
|
+
const FlexBox = styled.div(
|
|
35
|
+
variance.compose(system.layout, system.flex, system.space)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
<FlexBox display="flex" p={16} gap={8} width="100%" />;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Prop groups
|
|
42
|
+
|
|
43
|
+
### `system.layout`
|
|
44
|
+
|
|
45
|
+
Controls dimensions, display, overflow, and container behavior. This group also carries flex/grid item props used when laying out children: `flexGrow`, `flexShrink`, `flexBasis`, `order`, `gridColumn`, `gridRow`, `gridColumnStart`, `gridRowStart`, `gridColumnEnd`, `gridRowEnd`, `alignSelf`, `justifySelf`, `gridArea`.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
const Box = styled.div(system.layout);
|
|
49
|
+
|
|
50
|
+
<Box display="flex" width="50%" height="300px" verticalAlign="middle" />;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Key props: `containerType`, `display`, `direction`, `dimensions`, `width`, `height`, `minWidth`, `maxWidth`, `minHeight`, `maxHeight`, `overflow`, `overflowX`, `overflowY`, `verticalAlign`, plus the item props above (see `config.ts` for the full map).
|
|
54
|
+
|
|
55
|
+
### `system.space`
|
|
56
|
+
|
|
57
|
+
Margin and padding using the theme's spacing scale. Supports logical properties (switches based on `useLogicalProperties` in `<GamutProvider>`).
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
const Box = styled.div(system.space);
|
|
61
|
+
|
|
62
|
+
// Single value
|
|
63
|
+
<Box p={8} m={16} />;
|
|
64
|
+
|
|
65
|
+
// Responsive (array / object — see Responsive values)
|
|
66
|
+
<Box my={[16, 24, 32]} px={[8, 16]} />;
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Key props: `p`, `pt`, `pr`, `pb`, `pl`, `px`, `py`, `m`, `mt`, `mr`, `mb`, `ml`, `mx`, `my`
|
|
70
|
+
|
|
71
|
+
### `system.color`
|
|
72
|
+
|
|
73
|
+
Foreground, background, and border colors restricted to the theme's color palette.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
const Box = styled.div(system.color);
|
|
77
|
+
|
|
78
|
+
<Box bg="navy" color="gray-900" textColor="gray-100" borderColor="blue" />;
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Key props: `color`, `textColor` (both set CSS `color`), `bg`, `borderColor`, plus directional `borderColor*` variants — see `config.ts` for the full set.
|
|
82
|
+
|
|
83
|
+
### `system.typography`
|
|
84
|
+
|
|
85
|
+
Text styling connected to theme typography scales.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
const Text = styled.p(system.typography);
|
|
89
|
+
|
|
90
|
+
<Text
|
|
91
|
+
fontSize={16}
|
|
92
|
+
fontFamily="accent"
|
|
93
|
+
fontStyle="italic"
|
|
94
|
+
textTransform="uppercase"
|
|
95
|
+
lineHeight="base"
|
|
96
|
+
/>;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Key props: `fontFamily`, `fontSize`, `fontWeight`, `fontStyle`, `lineHeight`, `textAlign`, `textTransform`, `textDecoration`, `letterSpacing`, `whiteSpace` — prefer `lineHeight` scale keys (`base`, `title`, `spacedTitle`) from the theme over raw numbers when possible.
|
|
100
|
+
|
|
101
|
+
### `system.border`
|
|
102
|
+
|
|
103
|
+
Border width, style, radius, and color. Many logical shorthands exist (`borderX`, `borderColorY`, `borderRadiusTop`, …); see `config.ts` for the full map.
|
|
104
|
+
|
|
105
|
+
Key props (non-exhaustive): `border`, `borderTop`, `borderRight`, `borderBottom`, `borderLeft`, `borderRadius`, `borderWidth`, `borderStyle`
|
|
106
|
+
|
|
107
|
+
### `system.background`
|
|
108
|
+
|
|
109
|
+
Background image, size, position, and repeat (for images/patterns — use `system.color` for solid background colors).
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import myBg from './myBg.png';
|
|
113
|
+
|
|
114
|
+
const Box = styled.div(system.background);
|
|
115
|
+
|
|
116
|
+
<Box
|
|
117
|
+
background={`url(${myBg})`}
|
|
118
|
+
backgroundSize="cover"
|
|
119
|
+
backgroundPosition="center"
|
|
120
|
+
/>;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Key props: `background`, `backgroundImage`, `backgroundSize`, `backgroundPosition`, `backgroundRepeat`
|
|
124
|
+
|
|
125
|
+
### `system.flex`
|
|
126
|
+
|
|
127
|
+
Flexbox child and container properties.
|
|
128
|
+
|
|
129
|
+
Key props (non-exhaustive): `flex`, `flexDirection`, `flexWrap`, `flexGrow`, `flexShrink`, `flexBasis`, `alignItems`, `alignContent`, `alignSelf`, `justifyContent`, `justifyItems`, `justifySelf`, `gap`, `rowGap`, `columnGap`
|
|
130
|
+
|
|
131
|
+
### `system.grid`
|
|
132
|
+
|
|
133
|
+
CSS Grid container and child properties.
|
|
134
|
+
|
|
135
|
+
Key props (non-exhaustive): `gridTemplateColumns`, `gridTemplateRows`, `gridTemplateAreas`, `gridColumn`, `gridRow`, `gridArea`, `gridAutoFlow`, `gridAutoColumns`, `gridAutoRows`, `gap`, `rowGap`, `columnGap`
|
|
136
|
+
|
|
137
|
+
### `system.positioning`
|
|
138
|
+
|
|
139
|
+
Position and offset properties. Inset shorthands use `transformSize`; physical vs logical edges follow `useLogicalProperties`.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const Overlay = styled.div(variance.compose(system.layout, system.positioning));
|
|
143
|
+
|
|
144
|
+
<Overlay position="absolute" top={0} left={0} width="100%" height="100%" />;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Key props: `position`, `inset`, `top`, `right`, `bottom`, `left`, `zIndex`, `opacity`
|
|
148
|
+
|
|
149
|
+
### `system.shadow`
|
|
150
|
+
|
|
151
|
+
Box and text shadow.
|
|
152
|
+
|
|
153
|
+
Key props: `boxShadow`, `textShadow`
|
|
154
|
+
|
|
155
|
+
### `system.list`
|
|
156
|
+
|
|
157
|
+
List marker styling (`listStyle`, `listStyleType`, `listStylePosition`, `listStyleImage`). Included on `Box` alongside the other composed groups.
|
|
158
|
+
|
|
159
|
+
## Responsive values
|
|
160
|
+
|
|
161
|
+
All system props accept responsive values mobile-first (min-width queries). Two shapes are supported:
|
|
162
|
+
|
|
163
|
+
### Object syntax
|
|
164
|
+
|
|
165
|
+
Keys are breakpoints; `_` is the base (no breakpoint). Includes `xs`, `sm`, `md`, `lg`, `xl`, and container keys `c_xs` … `c_xl`.
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
<Box width={{ _: '100%', sm: '50%', md: '33%' }} px={{ _: 8, md: 16 }} />
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Array syntax
|
|
172
|
+
|
|
173
|
+
Slots map in order to: base, `xs`, `sm`, `md`, `lg`, `xl`, then `c_xs` … `c_xl`. Leave a slot empty (or use `undefined`) to skip a breakpoint.
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
<Box width={['100%', , '50%']} p={[8, 16, , 24]} />
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Full typings and behavior: [Responsive properties (Storybook)](https://gamut.codecademy.com/storybook/?path=/docs-foundations-system-responsive-properties--page).
|
|
180
|
+
|
|
181
|
+
## Using `css()` for styled definitions
|
|
182
|
+
|
|
183
|
+
For static styles in styled components, use `css()` from `@codecademy/gamut-styles` (same implementation as `system.css` on the `system` namespace).
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { css } from '@codecademy/gamut-styles';
|
|
187
|
+
import styled from '@emotion/styled';
|
|
188
|
+
|
|
189
|
+
// Static color using raw token
|
|
190
|
+
const Box = styled.div(css({ bg: 'navy-400', p: 4 }));
|
|
191
|
+
|
|
192
|
+
// Semantic color (adapts to color mode)
|
|
193
|
+
const Text = styled.div(css({ color: 'primary', p: 4 }));
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Key principles
|
|
197
|
+
|
|
198
|
+
- Compose `system.*` groups via `variance.compose()` — don't apply multiple groups by chaining `styled.div(system.a)(system.b)`.
|
|
199
|
+
- Prefer semantic color names on `system.color` (e.g. `bg="background"`, `textColor="text"`) so values track ColorMode; use raw palette keys only when the design should stay fixed across modes.
|
|
200
|
+
- Use `bg` with semantic tokens for most mode-aware surfaces; use `<Background>` from `@codecademy/gamut-styles` when you need its contrast- and mode-aware behavior, not for every tinted panel.
|
|
201
|
+
- Use `system.space` values on the spacing scale rather than arbitrary pixel strings to keep rhythm consistent.
|
|
202
|
+
- For background images/patterns use `system.background`; for solid fills use `system.color` / semantic `bg` (or `Background` when that component’s behavior is required).
|
|
203
|
+
- For reusable variants or boolean states on styled primitives, use `variant` / `states` from `@codecademy/gamut-styles` and expose props with `StyleProps<typeof …>` from `@codecademy/variance` — see [Best practices](../../../../styleguide/src/lib/Meta/Best%20practices.mdx).
|