Repository navigation
Conversation
- Ran the color-roles codemod on gamut, then resolved every leftover (splits, retired and pending names, old palette colors, runtime names) from design's component map in colorSystem/migration.ts. - Buttons follow design's button map: action families per variant, interface merges into primary, text-inverse labels, one action-primary focus ring, state veils for StrokeButton and TextButton, disabled knockout, CTAButton's offset shadow on action-neutral. - Card variants become surfaces, with the brand gradient for yellow and hyper; Alert's gradient layer moves to utils/gradientLayer for both. - Stories that painted old palette colors use roles or mode scopes. - BREAKING: old themes don't define the roles, so components lose their colors on core, admin, platform, percipio and lxStudio until they're rebuilt as brands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mponents Picks up #50's tokensCss injection in GamutProvider and the merge of main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mponents Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s brands Each old theme becomes a brand of the generated color system, so every theme defines every role and components on roles work under all of them. - Hues come from what the old theme rendered in each job, including what it inherited from core. Page backgrounds, text and yellow are pinned. Percipio and lxStudio keep core's yellow accent and dark mode. - Fonts, weights, elevation and lxStudio's radii are unchanged. createBrandTheme takes `borderRadii` and `initialMode`; platform starts in dark mode and adds its editor-* colors with their old values. - The two-color gradient pairs primary with accent-alt for every brand with a yellow accent, as design asked for Original Codecademy. - validate-tokens treats a polished rounding tie as agreement with the content-mode table and reports it (percipio primary-600). - The styling contract test only reads the blocks of the brand it tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design's card map (Interactive) puts a `state-hover` veil over the card's own fill and darkens the edge to `border-strong` on hover. The veil is a layer behind the content, so it tints the fill and a brand gradient but not the text, and it keys off the same wrapper hover as the elevation lift. The theme's elevation hover stays until elevation tokens exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ran the color-roles codemod over packages/styleguide and resolved every split, retirement and old palette color it flagged, following design's migration map and the choices gamut's components already made. Stories and docs that list color names now show roles, MDX examples included. The Storybook provider sets the page surface from the selected theme's `background-current` instead of a white/navy palette pair. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…olor-components Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ption Every theme in the toolbar is now a brand of the generated color system. Old core has no roles, so components on roles render without color under it; Original Codecademy is its rebuild. The core theme docs page shows Original Codecademy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
packages/.storybook is the config Storybook runs; the theme default change only belongs there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gacy-brands 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>
…olor-components Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Design feedback on the comparison page (2026-10-29): selection and navigation tags shouldn't have a fill. - Navigation and suggestion tags rest on a transparent fill instead of surface-raised, so they don't show as a white box on beige or paper pages. Hover and press stay veils. - A disabled tag drops the action-disabled fill. With nothing under it, the knockout label becomes the action-disabled ink; a disabled selection chip keeps an inset border-default edge so it doesn't resize. - The selection chip at rest stays a text-subtle chip, per design's map. - Adds Tag's first unit tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dreamwasp
left a comment
There was a problem hiding this comment.
Fix from design feedback on the components comparison page (2026-10-29).
| */ | ||
| navigation: { | ||
| bg: 'background-current', | ||
| bg: 'transparent', |
There was a problem hiding this comment.
Design feedback (2026-10-29): selection and navigation tags shouldn't have a fill. Navigation and suggestion tags were surface-raised, which shows as a white box on beige or paper pages. They're transparent now, so they sit on whatever surface they're on; hover and press stay state-hover / state-pressed veils. The selection chip at rest stays text-subtle behind text-inverse, per design's map.
| * a knockout label to sit on. The label takes the disabled ink instead, | ||
| * and the chip keeps an edge, inset so disabling it doesn't resize it. | ||
| */ | ||
| selectionDisabled: { |
There was a problem hiding this comment.
Disabled tags lose the action-disabled fill too. A knockout label (surface-raised) needs a fill under it, so the label becomes the action-disabled ink instead. This is contrast-exempt as an inactive control. The chip keeps a border-default edge as an inset shadow, so disabling it doesn't change its size. Navigation, suggestion and the dismiss button get the same treatment below.
- percipioTheme, the PercipioTheme type and the percipio brand are removed, with its Storybook theme option and Foundations page. The Percipio fonts and elevation stay, since skillsoftTheme and lxStudioTheme use them. - lxStudio's yellow is generated rather than pinned; only its near-white page stays pinned, and its dark primary stays sapphire. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-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>
…-components Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cass-gmt-1825-color-legacy-brands
…s' into cass-gmt-1825-color-components
Ports design's 2026-11-03 component map (gamut-sandbox df33b47): - Card: variants are default and brand (old white, beige and navy become default; yellow and hyper become brand). Every card keeps a border-subtle hairline. An interactive card has the raised shadow at rest and the state-hover veil with the raised-hover shadow on hover; the border no longer changes. A card that isn't interactive has no shadow. The outline offset is drawn in shadow-key. - Menus, popovers, tooltips, toasts and the SelectDropdown menu take a border-subtle hairline backed by the theme's raised shadow (a placeholder until design defines an overlay depth). - CTAButton's shadow comes from the theme's elevation. - Overlay's scrim is shadow-scrim; Flyout's panel has a border-subtle inner edge. 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.
| * `surface-brand` gradient (old yellow and hyper). Every card has a | ||
| * `border-subtle` hairline. | ||
| */ | ||
| export const cardSurfaces = { |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: Card's variants collapse to default (old default, white, beige, navy) and brand (old yellow, hyper). Navy was a dark card and now renders light; the color-roles codemod will rename the variants and comment every navy.
| * raised-hover shadow on hover. A card that isn't interactive has no shadow | ||
| * (design's card map, sandbox df33b47). | ||
| */ | ||
| export const useCardElevation = ( |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: an interactive card has the raised shadow at rest and the raised-hover shadow (plus the veil) on hover, and the border no longer switches to border-strong. A card that isn't interactive has no shadow.
| * (sandbox df33b47). Design hasn't defined an overlay depth yet, so they use | ||
| * the raised depth for now. | ||
| */ | ||
| export const raisedShadow = ({ theme }: { theme: Theme }) => ({ |
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 because "the shadow separates them". Most of them had no shadow, so they take the theme's raised shadow as a placeholder until design defines an overlay depth. Internal, not exported.
| * offset in the Codecademy theme. `ctaButtonElevation` runs before the | ||
| * variants, so pressed and disabled still clear it. | ||
| */ | ||
| export const ctaButtonElevation = ({ theme }: { theme: Theme }) => ({ |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: CTAButton's shadow comes from the elevation system, soft by default and the hard offset in the Codecademy theme only. Judgement call, flagged for design: the sandbox doesn't give the hard offset's geometry, so Original Codecademy's raised depth is old core's CTAButton offset (-4px at rest, -8px on hover), set in #52.
| position="fixed" | ||
| top={0} | ||
| {...{ [openFrom]: 0 }} | ||
| /* Design's map (sandbox df33b47): a `border-subtle` inner edge. */ |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: the Flyout panel gets a border-subtle inner edge.
| states({ | ||
| shroud: { | ||
| bg: 'navy-600', | ||
| bg: 'shadow-scrim', |
There was a problem hiding this comment.
Design's sandbox update, 2026-11-03: backdrop is renamed shadow-scrim.
…heme Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mponents # Conflicts: # packages/gamut/src/Card/Card.stories.tsx # packages/gamut/src/Form/inputs/SelectDropdown/core/styles.ts
…-subtle in a SelectDropdown story
ctaButtonElevation took `{ theme: Theme }`, which createButtonComponent's
`<T extends ThemeProps>` argument rejects, so the gamut build failed.
The SelectDropdown story main added in #47 used `text-secondary`, which
is `text-subtle` now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…om design's map Design's feedback on the drafts: inputs use surface-raised. The fields already did; an unchecked Checkbox didn't, and a disabled DatePicker never looked disabled (its `:disabled` rule is on a div). Its error ring was the raw string `feedback-error`, so it never drew. The rest comes from an audit against design's component map (sandbox df33b47) and the elevation proposal (sandbox #50): - Field placeholder `text-subtle` (the selector was `&:placeholder`), error a 2px `text-error` outline at rest, `activated` keeps `border-default`; the same in SelectDropdown, whose disabled value is `text-subtle` and whose focused selected option keeps its selected veil. - Checkbox disabled check mark and Disclosure's disabled overline are `surface-raised` knockouts. Radio's disabled ring is `border-default`; its error hover ring is `action-primary`. - FormGroupDescription `text-subtle`, Breadcrumbs separator `text-subtle`, Anchor hover `action-primary-hover` and focus rings `action-primary`. - List: hover is a veil over the row's fill, focus a ring with no fill, the first row's top rule `border-subtle`, the table header rule `border-default`, the scroll shadow `border-subtle`. - Shimmer: `surface-sunken` with a `surface-raised` sweep, no rgba. - The checker pattern (Modal, Toast, PreviewTip, DatePicker calendar) only on Original Codecademy, in `border-subtle`; the calendar gets a `border-subtle` edge. - The Core Theme page says brand themes draw shadows in shadow-key and shadow-ambient. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| height: 22, | ||
| borderColor: 'border-primary', | ||
| /* An unchecked box is a field: `surface-raised`, whatever it sits on. */ | ||
| bg: 'surface-raised', |
There was a problem hiding this comment.
Design feedback (Stacey, 2026-11-04): inputs use surface-raised. Input, TextArea, Select, SelectDropdown, Radio and the DatePicker field already did; an unchecked Checkbox had no fill, so it showed whatever it sat on. Fixed here, and Form/__tests__/fieldColors.test.tsx checks the fills.
Same commit: the other mismatches from an audit against design's component map (sandbox df33b47) and the elevation proposal (sandbox #50). They're listed in the commit message, including the DatePicker error ring that never drew and the checker pattern now drawn only on Original Codecademy.
… cass-gmt-1825-color-legacy-brands
… cass-gmt-1825-color-components
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>
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>
…or-components # Conflicts: # packages/gamut/src/Alert/elements.tsx
…mmits Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
… cass-gmt-1825-color-components # Conflicts: # .changeset/color-system-brands.md # packages/.storybook/components/Elements/DocsContainer.tsx # packages/.storybook/preview.ts # packages/.storybook/theming/GamutThemeProvider.tsx # 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/__tests__/AssetProvider.test.tsx # packages/gamut-styles/src/__tests__/fontLoading.test.tsx # packages/gamut-styles/src/__tests__/stylingContract.test.tsx # packages/gamut-styles/src/colorSystem/README.md # packages/gamut-styles/src/themes/__tests__/__snapshots__/theme.test.ts.snap # packages/gamut-styles/src/themes/__tests__/theme.test.ts # packages/gamut-styles/src/themes/admin.ts # packages/gamut-styles/src/themes/index.ts # packages/gamut-styles/src/themes/lxStudio.ts # packages/gamut-styles/src/themes/platform.ts # packages/gamut-styles/src/tokens/generated/tokens.css # packages/gamut-styles/src/tokens/generated/tokensCss.ts # packages/gamut/src/Alert/__tests__/Alert.test.tsx # packages/gamut/src/Menu/elements.tsx # packages/gamut/src/Modals/__tests__/Modal.test.tsx # packages/styleguide/src/lib/Molecules/Flyout/Flyout.stories.tsx # packages/styleguide/src/lib/Molecules/Menu/Menu.stories.tsx # packages/styleguide/src/lib/Molecules/Modals/Modal/Modal.stories.tsx # packages/styleguide/src/lib/Molecules/Tabs/Tabs.stories.tsx # packages/styleguide/src/lib/Molecules/Tips/InfoTip/InfoTip.stories.tsx
Overview
Moves every gamut component onto the color system's roles, following design's component map (gamut-sandbox PR #34, Migration → Components, head
ef1272a). Stacked on #52. Draft, for showing the team what the components look like on the new colors.Screenshots: today's core next to this branch on
skillsoftandoriginalCodecademy, light and dark, 37 stories: https://claude.ai/artifact/QJ836yQVQ6ghKHwawDqE5m (private; ask for access).How it was made
color-rolespreset from feat(gamut-codemods): add a color-roles preset #53 onpackages/gamut/src: 60 files, 273 lines. A second run changes nothing.Buttons (
Button/shared/variants.ts)variantvalues don't change.primary,secondaryanddangermap toaction-primary,action-neutralandaction-danger, each with its own hover and pressed step;interfacemerges into primary (TextButton's interface label istext-default).text-inverse; every variant gets oneaction-primaryfocus ring; disabled fill labels are a knockout.state-hover/state-pressedveils instead of a gray fill and a full inversion.action-neutraluntil there's a shadow role.Components, by design's map
Alert,Toast,Popover,Menu,Tip,Modals,Flyout,Overlay— floating surfaces aresurface-raisedwith aborder-defaultedge; the scrim isbackdrop. Tooltips are no longer black.Form,GridForm,ConnectedForm,Toggle— fields onsurface-raised, hover darkens the outline (border-strong) so blue only means focus; a checked Radio isaction-primary; Toggle disabled isaction-disabledinstead of 50% opacity.Card— yellow and hyper become a featured card on the brand gradient (Alert's gradient layer moved toutils/gradientLayer.tsso both use it); navy is a dark-mode scope; every card gets aborder-subtlehairline.Badge,Tag— filled badges aretext-default/text-subtlebehindtext-inverse; accent issurface-brandwith aborder-defaultedge.Tabs,Disclosure,Pagination,Anchor— block Tabs follow the page's mode instead of forcing dark; Disclosure focus is a ring, not a fill.ProgressBar,DataList,List,BarChart,Markdown,Video,Loading,Typographyand the rest — mapped by the closest documented rule; see "Choices" below.Choices where design has no exact mapping (each has a
/* */comment at the spot)background-currentstays in List, Drawer, DataList empty rows, Card's offset shadow, RadialProgress and Text fade. Design describes asurface-currentalias that every surface sets; that rename belongs in gamut-styles, as a follow-up.surface-brand, not the gradient: the gradient is sized incqw, and a content-sized badge can't be its own container.text-subtle, todayaction-primary, selected day a dark chip withtext-inverse, rangestate-selected, disabledaction-disabled.text-subtle/text-inverse). Design's slot sayssurface-sunkenbut its note says "follows the selection Tag", so this needs design to pick.primary-inverse(Text highlight) →action-primary, flagged for design.whiteandblack.viz-*is a follow-up.Added 2026-10-29
coreThemestill has no roles; Original Codecademy is its rebuild.styleguide: its stories and docs are on roles (52 files). The type check has no color errors left; the 23 remaining errors exist on the base branch too (argTypes alias, InfoTip, BarChart, Tabs types).Card: an interactive card gets design's hover. The border goes toborder-strong, and astate-hoverveil sits under the content, on the same trigger as the existing lift. 4 new tests.Tag(design feedback, 2026-10-29): navigation and suggestion tags have no fill of their own, so they sit on the surface they're on. Disabled tags drop theaction-disabledfill, takeaction-disabledink, and keep their edge. The selection chip at rest stays a dark chip, per design's map. Adds Tag's first tests.background-selectedas a resting panel →surface-sunkentext-disabledas muted helper text →text-subtlebackground-contrastas a label on a fill →text-inversedangeras red text →text-errorsurface-brandKnown gaps
undefined.viz-*is a follow-up.PR Checklist
.changeset/color-roles-components.md)Testing Instructions
npx jest -c packages/gamut/jest.config.ts→ 1208 of 1208 pass. On feat(gamut-styles)!: brands, overrides and Original Codecademy on the color system #52, 48 BarChart and Modal tests fail because they expect old names.npx jest -c packages/gamut-styles/jest.config.ts→ 280 of 280;yarn workspace @skillsoft/gamut-styles validate:tokens→ every check passes for both brands.🤖 Generated with Claude Code