Repository navigation
Conversation
… color system - Brands: every brand shares one role mapping and supplies a palette, emitted under [data-brand]. Palette and role overrides go through the same checks. - originalCodecademy: old core rebuilt as a brand, with core's fonts, weights and elevation. - ColorMode sets data-color-mode and data-brand on generated themes instead of injecting role variables. - Background paints the outer mode's palette color for an alias bg. - BREAKING: --color-* variables are renamed to --colors-* (variance too); tokens.css rules live in @layer tokens. - migration.ts re-synced to gamut-sandbox c363244. Alert General moves to surface-brand-subtle. - Styling contract test across seven themes in both modes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ands # Conflicts: # packages/gamut-styles/src/colorSystem/__tests__/system.test.ts # packages/gamut-styles/src/themes/skillsoft.ts
… pairs primary with accent-alt Brands can move a gradient role's circles to another slot (`overrides.gradients`), keeping design's grades, sizes and positions. ColorSystem carries each brand's resolved gradients, and the emitters, the migration contrast checks and tokens.css read from it. A brand whose gradient differs from the default's gets a `--gradient-*` line under `[data-brand][data-color-mode]`, like a role override. Original Codecademy's two-color gradient takes accent-alt in place of the yellow accent, per design, so it reads blue to violet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ps pinning navy and beige Design feedback on the comparison page (2026-10-29), and design's migration map at sandbox 15c2195: - Status alerts (error, success, notice) put their status ink on the message, and a floating Alert's border takes the same ink. General, feature and subtle keep text-default. - The CTA follows the Alert's own color mode. Forcing it dark only made sense on the old saturated fills; on the tints, light-mode alerts get light-mode buttons. - The ported migration map is synced to 15c2195 (Alert message slot, Alert and floating-surface summaries). - Original Codecademy no longer pins navy-800 and beige. Pinned, they carried much more chroma than the generated surfaces beside them; the generated steps at their hues are used as is. Yellow stays exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design feedback on the comparison page (2026-10-29): pinned navy-800 carries much more chroma than the generated surfaces beside it. As for Original Codecademy (#52), lxStudio uses the generated step at navy's hue. Admin and platform inherit Original Codecademy's pins, so they follow; platform's blue-0 and the near-white pages are nearly achromatic and stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp
left a comment
There was a problem hiding this comment.
Fixes from design feedback on the comparison pages (2026-10-29), plus design's migration map at sandbox 15c2195. Notes on the lines below.
| const buttonColorMode = isSubtleVariant ? currentColorMode : 'dark'; | ||
|
|
||
| /* | ||
| * The tints follow the color mode, so the CTA does too: it inherits the |
There was a problem hiding this comment.
Design feedback (comparison page, 2026-10-29): now that the alert fills are mode-following tints, light-mode alerts need light-mode buttons. The CTA was forced to mode="dark" for every non-subtle variant, a holdover from the old saturated fills. It now inherits the mode the Alert's Background sets. The test checks that no forced mode's variables land on the button.
| * (design's preview at sandbox 15c2195; its shared "Floating border" slot | ||
| * still says border-default and is out of date). | ||
| */ | ||
| const alertInk = ({ ink, placement, theme }: InkProps) => { |
There was a problem hiding this comment.
Design's map at sandbox 15c2195: status alerts (error, success, notice) put their status ink on the message, not text-default, and a floating Alert's border takes the same ink. General, feature and subtle keep text-default.
Note: the map's shared "Floating border" slot still says border-default, but design's coded preview and the Alert summary both use the message ink. I followed the preview, so a floating general or feature alert's edge is text-default.
| kind: 'text', | ||
| legacy: 'auto (Background)', | ||
| role: 'text-default', | ||
| role: `text-${status}`, |
There was a problem hiding this comment.
Synced to design's sandbox at 15c2195 (commit c5190b6, "Alert: status ink on message text, floating border matches text"). This message slot plus the Alert and floating-surface summaries are the whole diff; the components section now matches the sandbox exactly. validate:tokens still passes: each status ink clears 4.5:1 on its own tint for every brand.
| overrides: { | ||
| /* Each at the grade whose fixed lightness is nearest. */ | ||
| palette: { | ||
| 'accent-300': '#FFD300', // yellow-500 |
There was a problem hiding this comment.
Design feedback (comparison page, 2026-10-29): pinned navy-800 at neutral-dark-1100 carried much more chroma than the generated sunken and raised surfaces next to it, which made block tabs and table zebra rows look off. Beige had the same problem, less strongly. Both pins are gone; the generated steps at navy's and beige's hues are used as is. Yellow stays exact. Admin and platform (#58) inherit this.
| }, | ||
| }, | ||
| gradients: { | ||
| 'surface-brand': { accent: 'accent-alt' }, |
There was a problem hiding this comment.
Feature (and general) alerts on Original Codecademy render this gradient: primary + accent-alt rather than blue to yellow. Design approved the override on the brand comparison page (2026-10-29). The components screenshot page was captured before this landed, so its feature alerts still show the yellow pair until it's retaken.
…ndbox update Ports gamut-sandbox df33b47 (design, 2026-11-03): - backdrop is renamed shadow-scrim; shadow-key and shadow-ambient are added at design's draft alphas (key 0.16 / 0.32, ambient 0.12 / 0.24). - Brand themes draw elevation in the shadow roles, so shadows no longer render undefined. Skillsoft gets the soft raised shadow (design's Card geometry); Original Codecademy keeps old core's hard offset, with shadow-ambient transparent in both modes. - A role override can set an alpha as well as a ref, and the build emits the translucent palette values overrides introduce. - The migration map follows the sandbox: shadow-primary/secondary split onto the shadow roles, Card collapses to default and brand, overlays take a border-subtle hairline, Modal and Flyout use shadow-scrim. - The parity fixture is re-dumped at df33b47; the generator is unchanged. - The styling contract test only applies a brand's own role overrides. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp
left a comment
There was a problem hiding this comment.
Port of design's sandbox update, 2026-11-03 (gamut-sandbox df33b47). Notes on the lines below.
| * is a fill for the element behind a modal, not a box-shadow color. | ||
| */ | ||
| { | ||
| token: 'shadow-key', |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: backdrop becomes shadow-scrim, and shadow-key / shadow-ambient are the two layers every elevation shadow is drawn in. Alphas are design's current values (key 0.16 / 0.32, ambient 0.12 / 0.24), still marked draft in the sandbox. The parity fixture was re-dumped at df33b47 and all 45 roles match.
| }, | ||
| }, | ||
| dark: { | ||
| 'shadow-ambient': { ref: 'black', alpha: 0 }, |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: the Codecademy theme's shadow is a hard offset with no soft falloff, so shadow-ambient resolves to transparent and the layer stays in every elevation. This is why role overrides can now set an alpha (system.ts).
| const valueFor = (r: Role, mode: Mode): RoleValue => { | ||
| const o = roleOverrides[mode]?.[r.token]; | ||
| if (!o) return r[mode]; | ||
| return typeof o === 'object' && 'alpha' in o |
There was a problem hiding this comment.
A bare ref still keeps the role's alpha; { ref, alpha } replaces it. emit.ts now emits the translucent palette values an override introduces (palette-neutral-dark-1100-a0), which it didn't before.
| `${key} ${colors['shadow-key']}, ${ambient} ${colors['shadow-ambient']}`; | ||
|
|
||
| /* The soft raised shadow: design's Card geometry at rest and on hover. */ | ||
| export const softElevation: ElevationScaleFactory = ({ colors }) => { |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: Card's preview geometry, rest 0 1px 4px + 0 2px 7px and hover 0 2px 6px + 0 6px 16px, drawn in the shadow roles. Fixes brand themes' shadows rendering undefined (they read shadow-primary, which brand themes don't define). Geometry stays here until elevation becomes CSS variables.
| * in `shadow-key`. The brand makes `shadow-ambient` transparent, and the | ||
| * ambient layer stays at zero size. | ||
| */ | ||
| export const offsetElevation: ElevationScaleFactory = ({ colors }) => { |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: the hard offset is for the Codecademy theme only; every other brand gets the soft shadow. Old core's geometry, drawn in shadow-key, with a zero-size ambient layer so the layer count matches.
| @@ -229,17 +230,15 @@ export const TOKEN_MAP: TokenMapping[] = [ | |||
| // ---------------------------------------------------------------- shadow | |||
| { | |||
| legacy: 'shadow-primary', | |||
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: shadow-primary and shadow-secondary go from pending to a split onto shadow-key + shadow-ambient.
| ], | ||
| }, | ||
| { | ||
| name: 'Interactive', |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: Card collapses to default and brand, every card has a border-subtle hairline, and an interactive card adds the raised shadow at rest and the veil plus raised-hover shadow on hover; the border no longer changes. Components follow in #54.
| s('Fill', 'fill', 'background-contrast', 'surface-raised'), | ||
| s('Text', 'text', 'text', 'text-default'), | ||
| s('Border + beak', 'border', 'border-primary', 'border-default'), | ||
| s('Border + beak', 'border', 'border-primary', 'border-subtle'), |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: menus, popovers, tips, toasts and the SelectDropdown menu drop to a border-subtle hairline, and their shadow separates them. Controls keep border-default. Components follow in #54.
…-legacy-brands - admin, platform and lxStudio take the soft elevation drawn in the shadow roles; only Original Codecademy keeps the hard offset (design's sandbox, df33b47: the hard offset is the Codecademy theme's only). - Original Codecademy's overrides split into coreFamilyOverrides (the pins, the yellow dark primary, the gradient), which admin and platform share, and its own transparent shadow-ambient, which they don't. - Tokens regenerated; theme snapshots updated for the shadow roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CTA offset Design gives the Codecademy theme "the hard offset" at raised depth (sandbox df33b47, CTAButton) without its geometry. Raised is now old core's CTAButton offset at rest (-4px) and raised-hover its hover offset (-8px, with the Card lift), drawn in shadow-key. Floating surfaces and interactive cards get a visible edge in this theme too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… names its source The check that the generator reproduces design's sandbox lived only in the validate:tokens report, which CI doesn't run, and its fixture was named for one sandbox commit, so every port renamed it and edited the script. - `__tests__/sandboxParity.test.ts` compares every palette step and every role, both modes, for the default brand, and lists each difference. - `__fixtures__/sandbox.json` records its repo, branch, commit and dump date; the test names that source. - `yarn workspace @skillsoft/gamut-styles dump:sandbox <checkout>` refreshes the fixture from any sandbox checkout. On df33b47 it reproduces the old fixture exactly. - validate:tokens drops the parity section; the README says how to take a sandbox update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves three conflicts: - Background.tsx: take the color-system comment wording. - colorSystem/README.md: the color-system README, plus the brands and parity section from this branch. - Alert tests: the color tests move to Alert.test.tsx, where main moved the file. The color-system tests now expect the --colors- variable prefix this branch emits. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
The stack emitted --colors-* (the color scale's name) while main uses --color-*. This restores the singular prefix everywhere: the generator's Style Dictionary prefix, ColorMode's scale key, the variance package (back to main), gamut-styles, the Alert and codemod-facing names, and the docs. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ands # Conflicts: # packages/.storybook/preview.ts # packages/.storybook/theming/GamutThemeProvider.tsx
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Overview
Builds on #50 so the generated system can carry the old themes: a brand supplies only a palette, every brand shares one role mapping, and manual overrides keep colors the generator wouldn't produce. Adds
originalCodecademy, old core rebuilt as a brand. Also moves mode switches to an attribute and renames the color variables to--color-*(singular, matchingmain). Draft, stacked on #50.Brands
colorSystem/brands/—BRANDSdrives the build; every brand has to pass every check or nothing is emitted.tokens.cssemits roles once and a palette-only block per brand under[data-brand].colorSystem/brand.ts,system.ts—overrides.palette(an exact hex in place of a generated step),overrides.roles(a role repointed in one mode) andoverrides.gradients(a gradient role's circles moved to another slot, keeping design's grades, sizes and positions). Rounded to whattokens.cssprints and guarded to round-trip; unknown roles or steps throw.brand.sourcesreports how far each supplied hex lands from its nearest step.brands/originalCodecademy.ts,themes/originalCodecademy.ts:themes/createBrandTheme.ts— one factory for every brand theme;skillsoftThemeuses it too.emit.ts— brand gradients are written against palette variables (color-mix(in oklab, …)), so one gradient follows every brand (pixel diff against the old literal gradients: max 1/255).Runtime
ColorMode— on generated themes, a mode switch setsdata-color-modeanddata-brandand injects no role variables. 100 nested switches, server-rendered: 7.6 → 4.2 ms, 5 KB → 46 B of injected CSS. Old themes behave as before.Background— fixes a bug that's also onmain(see fix(gamut-styles): Background paints the outer mode's color for an alias bg #51): an aliasbgthat flips the mode painted the flipped mode's color.Breaking
variancecreateTheme, gamut-styles — color variables are--color-*, the same prefix asmain. variance'screateThemeuses thecolorprefix for the color scale, and its mode types follow it.tokens.css— all blocks inside@layer tokens, withreset, base, tokens, recipes, utilitiesdeclared up front (Panda's order). Emotion's styles stay unlayered and win.Design sync
colorSystem/migration.ts— re-synced to sandbox15c2195. Sincec363244: apendingkind for shadows, and status alerts carry their ink on the message and on a floating border.gamut/src/Alert:surface-brand-subtle.cqwradii measure the alert, not the viewport.text-<status>on the message and on a floating border. General, feature and subtle staytext-default.Tests
__tests__/stylingContract.test.tsx—css,variant,states, system props,themed,modeColorProps,_getColorValue,ColorMode,Backgroundand the mode hooks on all seven themes in both modes. Every color they write has to resolve through everyvar()to a real color.validate-tokens.ts— every check per brand, including a round-trip of each brand's roles throughtokens.css.Storybook and docs
.storybook— an "Original Codecademy (color spike)" theme option.gamut-agent-toolsskills,colorSystem/README.md, the CSS contract story —--color-*.Known gaps
text-default, following design's coded preview. Design's map still has a shared "Floating border:border-default" slot.shadow-primary, which has no role yet (design has it as pending), so shadows renderundefinedon the new themes.PR Checklist
15c2195)Testing Instructions
yarn workspace @skillsoft/gamut-styles validate:tokens→ both brands pass every check, including thetokens.cssround-trip.yarn nx test gamut-styles(281 tests),yarn nx test variance(104), andnpx jest packages/gamut/src/Alert(16).ColorModeelement: it carriesdata-color-modeanddata-brand="originalCodecademy", and its class has no--color-*role declarations.🤖 Generated with Claude Code