Skip to content

feat(gamut-codemods): add a color-roles preset - #53

Draft
dreamwasp wants to merge 52 commits into
mainfrom
cass-gmt-1825-color-codemod
Draft

dreamwasp wants to merge 52 commits into
mainfrom
cass-gmt-1825-color-codemod

Conversation

@dreamwasp

@dreamwasp dreamwasp commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Adds a color-roles preset that moves Gamut color names onto the generated system's roles, using design's migration map. Presets can now have their own manifest, pre-filter and leftovers report; scope-swap's behavior is unchanged. Draft, stacked on #45.

Framework

  • lib/types.ts, lib/run-source.ts, presets/index.ts — Preset<M> and the migration types are generic over their manifest, defaulting to scope-swap's. A preset brings its own mentions pre-filter and an optional leftovers report.
  • cli.ts, transform.ts, presets/scope-swap — call those hooks; scope-swap passes the pre-filter and leftovers report the CLI used to call directly.
  • __tests__/manifest.test.ts, fixtures.test.ts — the export check covers manifests that promise exports; end-to-end fixtures for other presets live in __tests__/__testfixtures__/<preset>/.

color-roles

  • migrations/color-roles — one splice migration. Rename and merge rows are applied. Split, retired and pending names stay (they still fail the type check), with a /* color-roles: … */ comment carrying design's rule and a warning. Old palette names and runtime-built names (${variant}-hover) only warn.
  • Positions: the ten color props in JSX and style objects (ternaries, &&, responsive values), theme.colors.x / ['x'], themed('colors.x'). variant="primary" and other look-alike strings are left alone.
  • presets/color-roles — the manifest (32 aliases, 43 roles, 104 old palette names), a leftovers report of unresolved color-roles: comments, the checklist. aliases.ts is generated from gamut-styles colorSystem/migration.ts.

Tests and docs

  • Three fixture cases (JSX, style objects, manual kinds), each checked for idempotence, and color-roles-manifest.test.ts: every applied row names exactly one real role.
  • README.md — running color-roles, what it changes, its limits.

PR Checklist

  • Related to designs: gamut-sandbox PR #34 (migration map at c363244)
  • Related to JIRA ticket: GMT-1825
  • Changeset added (or empty changeset if no release)
  • I have run this code to verify it works
  • This PR includes unit tests for the code change
  • This PR includes testing instructions for the code change
  • Package previews from this PR are passing end-to-end tests in all relevant consuming repositories

Testing Instructions

  1. yarn nx test gamut-codemods → 128 tests pass; scope-swap's 100 are unchanged.
  2. yarn nx build gamut-codemods, then node packages/gamut-codemods/dist/bin.js list → color-roles is listed.
  3. Dry run over gamut: node packages/gamut-codemods/dist/bin.js color-roles packages/gamut/src --dry. On feat(gamut-styles)!: brands, overrides and Original Codecademy on the color system #52's branch: 60 files, 171 warnings, 0 errors.
  4. For real, on a scratch copy: run it twice. The second run changes nothing. On feat(gamut-styles)!: brands, overrides and Original Codecademy on the color system #52's branch, gamut's type errors go from 284 to 145.
  5. Check out https://github.skillsoft.com/skillsoft-common/gamut-sandbox/pull/53 which combines all of the current PRs and runs this codemod

Known gap: aliases.ts is a generated copy of design's map, and the generator (gen-manifest.ts) still lives outside the repo, because this branch and #50 don't share gamut-styles yet. It should move in and read the workspace package once they do.

🤖 Generated with Claude Code

dreamwasp and others added 30 commits October 20, 2026 10:07
…eset

Adds @skillsoft/gamut-codemods: the CLI (clean-tree guard, --dry, --only,
--no-report, --force), the runner for ast, splice, and file migrations,
the shared helpers (module-string finder, leftovers report, version-range
checks, flat ESLint config detection), the preset registry, and the
fixture harness.

The scope-swap preset has its package map but no migrations yet, so a run
only reports the lines that still name an old package. Each breaking
change lands its migration in its own PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…swap

scope-rename swaps @Codecademy/gamut* and eslint-plugin-gamut for their
@SkillSoft names in every module position and in comments. package-json,
mdx-imports, and yarnrc cover dependencies, MDX ESM blocks, and
.yarnrc.yml lists.

@codecademy/gamut-kit has no replacement, so package-json and mf-shared
expand it into the packages it bundled. In Module Federation config that
makes Gamut a real singleton, which the checklist calls out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ides

moved-exports moves Video and VideoProps from the root to
@Codecademy/gamut/Video (scope-rename then swaps the scope).
markdown-video-overrides adds explicit iframeOverride/videoOverride to
each <Markdown>, since it no longer renders video through Video by
default, and drops the removed skipDefaultOverrides keys. tsconfig-dom
warns about tsconfigs without "dom", which Video used to supply.

On the Gamut side, a Video/package.json stub makes the subpath resolve
under node10, MarkdownVideo no longer passes NaN sizes, and a one-line
<video> is no longer wrapped in a <p>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ectDropdown elements

Exports formFieldStyles, formFieldPaddingStyles, conditionalStyles,
ButtonBaseProps, HTMLToReactNode, MenuToolTipWrapper, and Menu's list
elements from the root. The list elements and SelectDropdown's option
component are renamed (MenuList*, MenuListButton, IconOptionComponent) so
they don't collide with the public List component and IconOption type.

The deep-imports codemod rewrites known /dist/ paths to the root, keeps
local names across renames, merges into existing imports, and warns on
paths with no public replacement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eslint-comments rewrites gamut/ directives to @skillsoft/gamut/, and
eslint-config updates the plugin name, plugin: extends, and rule keys in
legacy .eslintrc files. Under a flat config the prefix is the plugins key,
so eslint-comments skips itself and the checklist explains why.

Also types eslint-plugin-gamut's recommended severities as literals, so the
plugin fits ESLint's Plugin type in a typed flat config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@skillsoft/gamut ships strict-ESM .mjs. With experimental.esmExternals
off, Next require()s server externals and default imports from
@emotion/styled and react-select break while it collects page data.
next-config is report-only: dropping the flag can take other config with
it, so it warns and adds a checklist item instead of rewriting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ap-skill

# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/__tests__/lib.test.ts
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
…p-skill

# Conflicts:
#	packages/gamut-codemods/README.md
#	packages/gamut-codemods/src/__tests__/cli.test.ts
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
The skill works alongside the scope-swap codemod: it reads the warnings,
leftovers, and next steps, and helps with what the codemod can't rewrite.
BREAKING_CHANGES.md lists every change in the move, and
NEXT_EMOTION_INTEROP.md covers the Next.js esmExternals fallback.

Also restores the end-to-end and MDX fixtures that run every migration
together, and fixes the cli.test merge between the sibling branches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CodeQL read `migration.match(file)` in cli.ts as String.prototype.match
on a command-line argument and flagged js/regex-injection. Nothing builds
a regex from the path; the method just had the same name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…migrations

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The package-json warning already names each range that's newer than the
fork point, so the extra next step only repeated it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rkdown

# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
…swap-skill

# Conflicts:
#	packages/gamut-codemods/src/presets/scope-swap/index.ts
dreamwasp and others added 6 commits October 20, 2026 11:46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves Gamut color names onto the generated color system's roles using
design's migration map (gamut-sandbox c363244). Renames and merges are
applied; splits, retired and pending names get a color-roles comment and
a warning. Old palette names and runtime-built names only warn.

Presets are now generic over their manifest and bring their own
pre-filter and leftovers report. scope-swap's behavior is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from cass-gmt-1790-scope-swap-skill to main October 28, 2026 17:33
#45 landed as a squash with later edits, so the color-roles preset is
reapplied on main's codemod framework.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pkg-pr-new

pkg-pr-new Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@skillsoft/eslint-plugin-gamut

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/eslint-plugin-gamut@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/eslint-plugin-gamut@53.tgz

@skillsoft/gamut

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut@53.tgz

@skillsoft/gamut-agent-tools

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-agent-tools@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-agent-tools@53.tgz

@skillsoft/gamut-codemods

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-codemods@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-codemods@53.tgz

@skillsoft/gamut-icons

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-icons@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-icons@53.tgz

@skillsoft/gamut-illustrations

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-illustrations@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-illustrations@53.tgz

@skillsoft/gamut-patterns

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-patterns@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-patterns@53.tgz

@skillsoft/gamut-styles

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-styles@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-styles@53.tgz

@skillsoft/gamut-tests

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-tests@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/gamut-tests@53.tgz

@skillsoft/variance

npm i https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/variance@53
yarn add https://pkg.pr.new/Codecademy/skillsoft-gamut/@skillsoft/variance@53.tgz

commit: fc1fe42

@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.46006% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.99%. Comparing base (4138841) to head (fc1fe42).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...gamut-codemods/src/migrations/color-roles/index.ts 90.95% 16 Missing and 1 partial ⚠️
...es/gamut-codemods/src/presets/color-roles/index.ts 50.00% 11 Missing ⚠️
...ds/src/migrations/color-roles-stylesheets/index.ts 93.33% 2 Missing ⚠️
...mut-codemods/src/migrations/color-roles/cssVars.ts 97.29% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main      #53       +/-   ##
===========================================
+ Coverage   79.78%   96.99%   +17.21%     
===========================================
  Files         296       31      -265     
  Lines        6643     1299     -5344     
  Branches     2287      422     -1865     
===========================================
- Hits         5300     1260     -4040     
+ Misses       1334       38     -1296     
+ Partials        9        1        -8     
Flag Coverage Δ
main ?
pull-request 96.99% <91.46%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…Card variants

- A color-roles-stylesheets migration renames old --color-* variables to
  --colors-<role> in .css, .scss and .less, and marks splits with design's
  rule. The leftovers report now scans stylesheets too.
- The color-roles transform does the same for var(--color-*) inside JS
  string and template literals, with the comment outside the string.
- Card's variant moves onto design's default and brand; a navy card gets a
  comment, since it now renders light.
- The alias map is regenerated from design's map at sandbox df33b47:
  shadow-primary and shadow-secondary split onto shadow-key and
  shadow-ambient, and border-primary's rule covers floating surfaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dreamwasp dreamwasp left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylesheets, JS strings and Card variants, plus design's map at sandbox df33b47. Notes on the lines below.

kind: 'file',
description:
'Rename old `--color-*` variables in stylesheets; mark the ones a person has to pick.',
appliesTo: (file) => /\.(css|scss|less)$/.test(file),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type check can't see var(--color-*) in stylesheets, so without this pass they'd break silently. Indented-syntax .sass is left out on purpose: a comment mid-value can break it. The checklist tells people to search those by hand.

});

/*
`var(--color-…)` in a string or template literal. A rename is applied

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a split inside a string, the comment goes in front of the literal rather than inside it, so it never ships in runtime CSS and can't break the string's quoting.

Card's variants are design's two (sandbox df33b47): white, beige and navy
become `default`; yellow and hyper become `brand`.
*/
const CARD_VARIANTS: Record<string, string> = {

@dreamwasp dreamwasp Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design's sandbox update, 2026-11-03: Card collapses to default and brand. Only Cards imported from gamut are touched. navy gets a comment because it now renders light; wrapping it in <ColorMode mode="dark"> keeps it dark.

to: ['surface-error'],
rule: 'The error tint behind an alert or inline validation message.',
},
'shadow-primary': {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design's sandbox update, 2026-11-03: regenerated from design's map at df33b47. shadow-primary and shadow-secondary go from pending to a split onto shadow-key + shadow-ambient, and border-primary's rule now covers floating surfaces.

A color-roles-themes migration moves imports from gamut-styles onto the
brand themes that replace them: percipioTheme → skillsoftTheme,
coreTheme → originalCodecademyTheme, PercipioTheme → SkillsoftTheme. An
aliased import only changes its imported name; an unaliased one is renamed
wherever the file uses it, keeping shorthand object keys. A CoreTheme type
gets a color-roles comment pointing at SkillsoftTheme.

Front's provider imports `percipioTheme as theme`, and no codemod handled it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@dreamwasp dreamwasp left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theme names.

person, since it's usually a Theme augmentation the app owns.
*/

const RENAMES: Record<string, string> = {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

percipioTheme is removed and skillsoftTheme replaces it; coreTheme still exists but defines no roles, so components lose their colors on it, and originalCodecademyTheme is its rebuild. Front's provider imports percipioTheme as theme, which no codemod handled before this. CoreTheme has no same-named successor, so it gets a comment instead of a rename.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp and others added 4 commits November 4, 2026 11:28
`n is Node` narrowed every non-string node to `never`, so the tsc build
failed on the branches after an isString check. Jest doesn't typecheck,
so the tests still passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generator, and design's migration map with it, moved from gamut-styles'
src/colorSystem to @skillsoft/gamut-brand. aliases.ts is regenerated from
the moved map; only its header comments change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dreamwasp
dreamwasp marked this pull request as ready for review November 9, 2026 18:07
@dreamwasp
dreamwasp requested a review from a team as a code owner November 9, 2026 18:07
dreamwasp and others added 2 commits November 10, 2026 09:02
… prefix

The codemod emitted --colors-<role>. main and gamut-styles use --color-, so the
rewrite now emits --color-<role> too: the stylesheet rename, the JS var
rewrite, the changeset and the fixtures. A rewritten name is a role, and no
role is also an old alias key, so a second run leaves it alone.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
The fixture test ran the second-run check only for JS migrations. The stylesheet
pass returned early, so a rewritten --color-<role> was never re-checked.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@dreamwasp
dreamwasp marked this pull request as draft November 10, 2026 16:24

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant