Repository navigation
Conversation
Each brand theme now carries and injects only its own brand's CSS (about 21 KB, down from 56 KB for every brand at once). The CSS puts the brand's palette and initial mode on :root, and folds its role overrides into its mode blocks. Every brand resolves to the same values as before in both modes. The generator, role table, checks, sandbox fixture and gamut's brand inputs move from gamut-styles' src/colorSystem to a new build-time package, @skillsoft/gamut-brand. Its `gamut-brand build` CLI writes a brand's values.ts, tokens.css and DTCG JSON (--watch, --check), and refuses a brand that fails a check, including on its own tokens.css read back. gamut-styles' build:tokens uses the same writeBrand on gamut's five brands. - createBrandTheme, BrandBase and BrandValues are exported. A team's theme has the same type as gamut's, so system props stay typed. - BREAKING: generated files move to one folder per brand (tokens/<brand>.css, tokens/<brand>/*.tokens.json; tokens.css stays the default brand). initialMode moves from the theme onto the brand. - DTCG mode files include each brand's role overrides. - Role names in brand overrides are typed (ROLE_TOKENS). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sandbox parity only matters while the port is in development. The generator is now held to its own rules for any hue (fixed lightness per grade, chroma inside sRGB, the separation guarantee, one role mapping for every brand, every check passing) instead of a saved copy of the sandbox's output. The checked-in generated files still catch value drift. Drops the sandbox fixture, dump script and comments that pointed at sandbox commits and files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 9, 2026
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…s' into cass-gmt-1825-color-white-label # Conflicts: # .changeset/color-system-brands.md # packages/gamut-brand/src/__tests__/system.test.ts # packages/gamut-styles/scripts/build-tokens.ts # packages/gamut-styles/scripts/validate-tokens.ts # packages/gamut-styles/src/colorSystem/README.md # packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap # packages/gamut-styles/src/themes/__tests__/brandElevation.test.ts # packages/gamut-styles/src/themes/__tests__/theme.test.ts # packages/gamut-styles/src/themes/platform.ts # packages/gamut-styles/src/tokens/generated/tokens.css # packages/gamut-styles/src/tokens/generated/tokensCss.ts
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Two connected changes. Every brand now ships its own
tokens.css, instead of all brands sharing one file. And the color generator moves into a new package,@skillsoft/gamut-brand, so a team can build its own brand (white-labeling) at build time and keep gamut's types.A team's brand can't be baked into a file gamut ships, so per-brand output and a public generator are one piece of work.
Per-brand CSS
tokens.cssholds one brand: its palette on:root, [data-brand], its initial mode's roles on:root, then the other mode. A page starts in the right brand and mode with nothing set on<html>.data-color-modekeeps them.src/tokens/generated/<brand>/, and a page carries only its own brand. The split was checked against the old shared file: all five brands, both modes, identical values.initialModemoves onto the brand, because its CSS has to know it at build time. Platform starts dark in its CSS too, so a server-rendered page starts dark.@skillsoft/gamut-brandgamut-styles/src/colorSystem.defineBrand({ name, hues })types a brand.gamut-brand build <file>writesvalues.ts,tokens.cssand DTCG JSON.--watchregenerates on change and only rewrites files whose contents changed.--checkwrites nothing and fails CI when the files are stale.tokens.cssread back, so what ships is what was checked.createBrandTheme,BrandBaseandBrandValuesare exported from gamut-styles. A team's theme has the same type as gamut's own, so system props,css(),variant()andstates()stay typed.build:tokensis now a loop over gamut's five brands using the same API.Tests
generator.test.tsholds the generator to its rules, at every 5° of hue:cli.test.tsruns the CLI end to end: build, check, unchanged files, a refused brand, a bad name.Breaking
@skillsoft/gamut-styles/tokens/<brand>.cssfor a brand's CSS (tokens.cssstays the default brand), andtokens/<brand>/{palette,light,dark}.tokens.json.initialModemoves fromcreateBrandThemeto the brand.BrandBasenow requires gamut's font family and font weight keys.Merge order: after #58, before #54. #54, #63, #59 and #73 already have this merged in.
PR Checklist
.changeset/color-system-white-label.md)Testing Instructions
npx nx run-many -t test,lint,build,verify-package -p gamut-brand gamut-styles: all pass.yarn workspace @skillsoft/gamut-styles build:tokens --check: "generated tokens are current".packages/gamut-styles/src/tokens/generated/originalCodecademy/tokens.css. It has one brand's palette, its light roles on:root, and the dark mode's yellowaction-primary.defineBrand({ name: 'acme', hues: { primary: '#0A5CFF' } })to a file and rungamut-brand buildon it. Then change the hue with--watchrunning and see only the changed files rewritten.🤖 Generated with Claude Code