@codecademy/gamut
68.7.068.7.1-alpha.1ea5a8.0
agent-tools/DESIGN.Codecademy.md~
agent-tools/DESIGN.Codecademy.mdModified+90β120
Index: package/agent-tools/DESIGN.Codecademy.md
===================================================================
--- package/agent-tools/DESIGN.Codecademy.md
+++ package/agent-tools/DESIGN.Codecademy.md
@@ -7,14 +7,15 @@
hyper-500: '#3A10E5'
hyper-400: '#5533FF'
navy-900: '#0A0D1C'
navy-800: '#10162F'
- navy-700: '#31374C'
- navy-600: '#4C5063'
- navy-500: '#686C7C'
- navy-300: '#BCBEC5'
- navy-200: '#E2E3E6'
- navy-100: '#F5F6F7'
+ navy-700: 'rgba(16, 22, 47, 0.86)'
+ navy-600: 'rgba(16, 22, 47, 0.75)'
+ navy-500: 'rgba(16, 22, 47, 0.63)'
+ navy-400: 'rgba(16, 22, 47, 0.47)'
+ navy-300: 'rgba(16, 22, 47, 0.28)'
+ navy-200: 'rgba(16, 22, 47, 0.12)'
+ navy-100: 'rgba(16, 22, 47, 0.04)'
yellow-500: '#FFD300'
yellow-400: '#CCA900'
yellow-0: '#FFFAE5'
yellow-900: '#211B00'
@@ -76,10 +77,8 @@
fontFamily: '"Apercu", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
fontSize: '2.125rem'
fontWeight: '700'
lineHeight: '1.2'
- hankenGrotesk:
- fontFamily: '"Hanken Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif'
monospace:
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", "Droid Sans Mono", Consolas, monospace'
rounded:
none: '0px'
@@ -180,8 +179,10 @@
**Figma file**: https://www.figma.com/design/ReGfRNillGABAj5SlITalN/π-Gamut
**Storybook**: https://gamut.codecademy.com
+> **Other Gamut themes:** This document covers **Codecademy** (Core, Admin, Platform) only. For Percipio or LX Studio, install that product's `DESIGN.md` instead: `gamut plugin install cursor --theme percipio` or `--theme lxstudio` (see `DESIGN.Percipio.md` / `DESIGN.LXStudio.md` in agent-tools).
+
---
## Visual Theme & Atmosphere
@@ -191,114 +192,85 @@
**Design philosophy**:
- Components are color modeβaware by default β never hardcode hex values for adaptive UI
-- Every component works across all themes without modification
+- Every component works across Core, Admin, and Platform without modification
- Mobile-first responsive design built on a 12-column grid
- Accessibility is guaranteed by design: semantic color tokens meet contrast requirements per mode automatically
---
## Themes
-Codecademy products use one of four Gamut themes, all sharing the same core visual identity. Token aliases resolve to the right values per theme automatically β components require no modification.
+Codecademy products use **Core**, **Admin**, or **Platform** β the same visual identity with theme-specific palette additions on Platform. Token aliases resolve per theme automatically; components require no modification.
-| Theme | Use case | Base font | Dark mode |
-| ------------- | ------------------------------- | -------------- | -------------- |
-| **Core** | Codecademy (default) | Apercu | β light + dark |
-| **Admin** | Codecademy admin tools | Apercu | β light + dark |
-| **Platform** | Codecademy learning environment | Apercu | β light + dark |
-| **LX Studio** | LX Studio application | Hanken Grotesk | light only |
+| Theme | Use case | Base font | Dark mode |
+| ------------ | ------------------------------- | --------- | -------------- |
+| **Core** | Codecademy (default) | Apercu | β light + dark |
+| **Admin** | Codecademy admin tools | Apercu | β light + dark |
+| **Platform** | Codecademy learning environment | Apercu | β light + dark |
-The active theme is set at the app root via `<GamutProvider>`. When designing, know which theme your screen targets β it affects primary colors, font families, and available color weights.
+Set the active theme at the app root via `<GamutProvider theme={coreTheme | adminTheme | platformTheme}>`.
-**Font licensing**: Apercu is licensed for codecademy.com only. LX Studio uses Hanken Grotesk.
+**Font licensing:** Apercu is licensed for codecademy.com only.
-For Percipio projects, use `DESIGN.Percipio.md` from the same package instead.
-
-### LX Studio theme overrides
-
-LX Studio extends Core with these differences:
-
-**Font**: All families β `"Hanken Grotesk"` (no Apercu, no Suisse).
-
-**Border radii** (all values shift up one step):
-
-| Token | Core | LX Studio |
-| ----- | ---- | --------- |
-| `sm` | 2px | 4px |
-| `md` | 4px | 8px |
-| `lg` | 8px | 12px |
-
-**Semantic color overrides (light mode)**:
-
-| Token | Core value | LX Studio value |
-| -------------------- | ------------------- | ------------------------------- |
-| `primary` | hyper-500 `#3A10E5` | `#5628FE` (lxStudioPurple) |
-| `primary-hover` | hyper-400 `#5533FF` | `#7955FC` (lxStudioPurpleHover) |
-| `feedback-success` | green-700 `#008A27` | `#06844F` (lxStudioSuccess) |
-| `background-primary` | beige `#FFF0E5` | `#FAFBFC` (lxStudioBgPrimary) |
-| `shadow-primary` | navy-800 | navy-200 |
-| `border-primary` | navy-800 | navy-400 |
-| `border-disabled` | navy-500 | navy-300 |
-
---
## Semantic Color Aliases
Use these token names when specifying colors in designs. They resolve to the correct raw value for the active theme and color mode automatically. **Never hardcode hex values** for anything that needs to adapt across modes.
### Text
-| Token | Light | Dark | Use for |
-| ---------------- | -------------------------- | --------------- | --------------------------- |
-| `text` | navy-800 `#10162F` at 100% | white `#ffffff` | Default body and UI text |
-| `text-accent` | navy-900 `#0A0D1C` | beige `#FFF0E5` | Stronger emphasis text |
-| `text-secondary` | navy-800 at 75% | white at 65% | Supporting / secondary copy |
-| `text-disabled` | navy-800 at 63% | white at 50% | Disabled state labels |
+| Token | Light | Dark | Use for |
+| ---------------- | --------------- | ------------ | --------------------------- |
+| `text` | `navy-800` | `white` | Default body and UI text |
+| `text-accent` | `navy-900` | `beige` | Stronger emphasis text |
+| `text-secondary` | navy-800 at 75% | white at 65% | Supporting / secondary copy |
+| `text-disabled` | navy-800 at 63% | white at 50% | Disabled state labels |
### Background
-| Token | Light | Dark | Use for |
-| --------------------- | ------------------ | -------------------- | --------------------------------- |
-| `background` | white `#ffffff` | navy-800 `#10162F` | Default page/component background |
-| `background-primary` | beige `#FFF0E5` | navy-900 `#0A0D1C` | Slightly elevated surfaces |
-| `background-contrast` | white | black `#000000` | Maximum contrast surface |
-| `background-selected` | navy-800 at 4% | white at 4% | Selected row / item |
-| `background-hover` | navy-800 at 12% | white at 9% | Hover state overlay |
-| `background-disabled` | navy-800 at 12% | white at 9% | Disabled surface |
-| `background-success` | green-0 `#F5FFE3` | green-900 `#151C07` | Success state container |
-| `background-warning` | yellow-0 `#FFFAE5` | yellow-900 `#211B00` | Warning state container |
-| `background-error` | red-0 `#FBF1F0` | red-900 `#280503` | Error state container |
+| Token | Light | Dark | Use for |
+| --------------------- | --------------- | ------------ | --------------------------------- |
+| `background` | `white` | `navy-800` | Default page/component background |
+| `background-primary` | `beige` | `navy-900` | Slightly elevated surfaces |
+| `background-contrast` | `white` | `black` | Maximum contrast surface |
+| `background-selected` | navy-800 at 4% | white at 4% | Selected row / item |
+| `background-hover` | navy-800 at 12% | white at 9% | Hover state overlay |
+| `background-disabled` | navy-800 at 12% | white at 9% | Disabled surface |
+| `background-success` | `green-0` | `green-900` | Success state container |
+| `background-warning` | `yellow-0` | `yellow-900` | Warning state container |
+| `background-error` | `red-0` | `red-900` | Error state container |
### Interactive
-| Token | Light | Dark | Use for |
-| ----------------- | -------------------- | -------------------- | ------------------------------------ |
-| `primary` | hyper-500 `#3A10E5` | yellow-500 `#FFD300` | Primary CTA, links, focus rings |
-| `primary-hover` | hyper-400 `#5533FF` | yellow-400 `#CCA900` | Hover state of primary interactive |
-| `primary-inverse` | yellow-500 `#FFD300` | hyper-500 `#3A10E5` | Primary on a colored background |
-| `secondary` | navy-800 `#10162F` | white `#ffffff` | Secondary CTA, ghost buttons |
-| `secondary-hover` | navy-800 at 86% | white at 80% | Hover state of secondary interactive |
-| `danger` | red-500 `#E91C11` | red-300 `#E85D7F` | Destructive actions, error states |
-| `danger-hover` | red-600 `#BE1809` | red-400 `#DC5879` | Hover on danger interactive |
+| Token | Light | Dark | Use for |
+| ----------------- | --------------- | ------------ | ------------------------------------ |
+| `primary` | `hyper-500` | `yellow-500` | Primary CTA, links, focus rings |
+| `primary-hover` | `hyper-400` | `yellow-400` | Hover state of primary interactive |
+| `primary-inverse` | `yellow-500` | `hyper-500` | Primary on a colored background |
+| `secondary` | `navy-800` | `white` | Secondary CTA, ghost buttons |
+| `secondary-hover` | navy-800 at 86% | white at 80% | Hover state of secondary interactive |
+| `danger` | `red-500` | `red-300` | Destructive actions, error states |
+| `danger-hover` | `red-600` | `red-400` | Hover on danger interactive |
### Border
-| Token | Light | Dark | Use for |
-| ------------------ | ------------------ | --------------- | -------------------------- |
-| `border-primary` | navy-800 `#10162F` | white `#ffffff` | Strong borders, dividers |
-| `border-secondary` | navy-800 at 75% | white at 65% | Medium-weight borders |
-| `border-tertiary` | navy-800 at 28% | white at 20% | Subtle borders, separators |
-| `border-disabled` | navy-800 at 63% | white at 50% | Disabled input borders |
+| Token | Light | Dark | Use for |
+| ------------------ | --------------- | ------------ | -------------------------- |
+| `border-primary` | `navy-800` | `white` | Strong borders, dividers |
+| `border-secondary` | navy-800 at 75% | white at 65% | Medium-weight borders |
+| `border-tertiary` | navy-800 at 28% | white at 20% | Subtle borders, separators |
+| `border-disabled` | navy-800 at 63% | white at 50% | Disabled input borders |
### Feedback
-| Token | Light | Dark | Use for |
-| ------------------ | ------------------- | ------------------- | -------------------------------- |
-| `feedback-error` | red-600 `#BE1809` | red-300 `#E85D7F` | Error messages, validation |
-| `feedback-success` | green-700 `#008A27` | green-400 `#AEE938` | Success messages, confirmations |
-| `feedback-warning` | yellow `#FFD300` | yellow-0 `#FFFAE5` | Warning messages, caution states |
+| Token | Light | Dark | Use for |
+| ------------------ | ----------- | ----------- | -------------------------------- |
+| `feedback-error` | `red-600` | `red-300` | Error messages, validation |
+| `feedback-success` | `green-700` | `green-400` | Success messages, confirmations |
+| `feedback-warning` | `yellow` | `yellow-0` | Warning messages, caution states |
### Shadow
| Token | Light | Dark |
@@ -313,49 +285,45 @@
All colors available as static tokens regardless of color mode. Use these only when a color should be **fixed** and not adapt to dark mode.
### Core Palette
-| Name | Weights available | Notes |
-| --------------- | ---------------------------- | --------------------------------------------------------------------------------- |
-| `navy` | 100β900 | 100β700 are rgba transparencies of `#10162F`; 800 = `#10162F`; 900 = `#0A0D1C` |
-| `white` | 100β700 | rgba transparencies of `#ffffff` (no solid white weight β use `white` for `#fff`) |
-| `blue` | 0, 100, 300, 400, 500, 800 | 500 = `#1557FF` |
-| `hyper` | 400, 500 | 500 = `#3A10E5` (purple-blue), 400 = `#5533FF` |
-| `green` | 0, 100, 400, 700, 900 | 700 = `#008A27` |
-| `yellow` | 0, 400, 500, 900 | 500 = `#FFD300` |
-| `red` | 0, 300, 400, 500, 600, 900 | 500 = `#E91C11` |
-| `gray` | 100, 200, 300, 600, 800, 900 | |
-| `pink` | 0, 400 | 400 = `#F966FF` |
-| `orange` | 100, 500 | 500 = `#FF8C00` |
-| `beige` | 100 (alias: `beige`) | `#FFF0E5` |
-| `black` | β | `#000000` |
-| `white` (solid) | β | `#ffffff` |
+| Name | Weights available | Notes |
+| --------------- | ---------------------------- | -------------------------------------------------------------------------- |
+| `navy` | 100β900 | 100β700 are rgba transparencies of `navy-800`; 800 and 900 are solid |
+| `white` | 100β700 | rgba transparencies of `white` (no solid white weight β use `white` token) |
+| `blue` | 0, 100, 300, 400, 500, 800 | named alias `blue` maps to `blue-500` |
+| `hyper` | 400, 500 | named alias `hyper` maps to `hyper-500` |
+| `green` | 0, 100, 400, 700, 900 | named alias `green` maps to `green-700` |
+| `yellow` | 0, 400, 500, 900 | named alias `yellow` maps to `yellow-500` |
+| `red` | 0, 300, 400, 500, 600, 900 | named alias `red` maps to `red-500` |
+| `gray` | 100, 200, 300, 600, 800, 900 | |
+| `pink` | 0, 400 | named alias `pink` maps to `pink-400` |
+| `orange` | 100, 500 | named alias `orange` maps to `orange-500` |
+| `beige` | 100 (alias: `beige`) | solid `beige` token |
+| `black` | β | `black` token |
+| `white` (solid) | β | `white` token |
**Named aliases** (shorthand for common weights):
`beige`, `blue`, `green`, `hyper`, `lightBlue`, `lightGreen`, `navy`, `orange`, `paleBlue`, `paleGreen`, `palePink`, `paleRed`, `paleYellow`, `pink`, `red`, `yellow`, `black`, `white`
### Platform-only additions
-`lightBeige` (`#FFFBF8`), `gold` (`#8A7300`), `teal` (`#006D82`), `purple` (`#B3CCFF`)
+`lightBeige`, `gold`, `teal`, `purple` (Platform theme palette)
-### LX Studio additions
-
-`lxStudioPurple` (`#5628FE`), `lxStudioPurpleHover` (`#7955FC`), `lxStudioSuccess` (`#06844F`)
-
---
## Typography
### Typefaces
-| Token | Core / Admin / Platform | LX Studio | Use for |
-| ----------- | -------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------ |
-| `base` | Apercu Pro (CSS: `Apercu`) | Hanken Grotesk | All default UI text, headlines, body copy |
-| `accent` | Suisse Intl Mono (CSS: `Suisse`); falls back to `Apercu` | Hanken Grotesk | Code, captions, labels, lists, technical context |
-| `monospace` | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Code editor contexts |
-| `system` | System UI fonts | System UI fonts | Performance-critical surfaces |
+| Token | Font stack | Use for |
+| ----------- | -------------------------------------------------------- | ------------------------------------------------ |
+| `base` | Apercu Pro (CSS: `Apercu`) | All default UI text, headlines, body copy |
+| `accent` | Suisse Intl Mono (CSS: `Suisse`); falls back to `Apercu` | Code, captions, labels, lists, technical context |
+| `monospace` | Monaco, Menlo, Ubuntu Mono, Droid Sans Mono, Consolas | Code editor contexts |
+| `system` | System UI fonts | Performance-critical surfaces |
-**Apercu is licensed for codecademy.com only.** LX Studio uses Hanken Grotesk for both `base` and `accent`.
+**Apercu is licensed for codecademy.com only.**
### Rules
- **Apercu Bold** for headlines, sub-headlines, CTAs, and buttons.
@@ -453,19 +421,21 @@
---
## Border Radius Scale
-| Token | Value | Use |
-| ------ | ----- | ------------------------------------------ |
-| `none` | 0px | Square / non-interactive elements |
-| `sm` | 2px | Subtle rounding, tags |
-| `md` | 4px | Default buttons, inputs, interactive cards |
-| `lg` | 8px | Cards, panels |
-| `xl` | 16px | Large cards, modals |
-| `full` | 999px | Pills, avatars, circular elements |
+| Token | Value | Use |
+| ------ | ----- | ------------------------ |
+| `none` | 0px | Non-interactive elements |
+| `sm` | 2px | Overlays |
+| `md` | 4px | Interactive elements |
+| `lg` | 8px | Non-interactive elements |
+| `xl` | 16px | Non-interactive elements |
+| `full` | 999px | Toggles, badges |
---
+---
+
## Responsive Behavior
Mobile-first. Apply styles from the named breakpoint and up.
@@ -602,10 +572,10 @@
Quick color/token reference for generating or specifying UI:
| Scenario | Tokens |
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
-| Primary button (light) | `bg: primary (#3A10E5)`, `color: white`, `hover: primary-hover (#5533FF)` |
-| Primary button (dark) | `bg: primary (#FFD300)`, `color: navy-800`, `hover: primary-hover (#CCA900)` |
+| Primary button (light) | `bg: primary`, `color: white`, `hover: primary-hover` |
+| Primary button (dark) | `bg: primary`, `color: text`, `hover: primary-hover` |
| Body text | `color: text`, `font: base (Apercu Pro)`, `size: 16px`, `weight: 400`, `lineHeight: base (1.5)` |
| Headline | `color: text-accent`, `font: base`, `size: 34β64px`, `weight: title (700)`, `lineHeight: title (1.2)` |
| Caption / label | `color: text-secondary`, `font: accent (Suisse Int'l Mono)`, `size: 14px` |
| Card default | `bg: background`, `borderRadius: none` β add `isInteractive` for hover shadow + `borderRadius: md` |