Add support for derived color modes#1333
Conversation
🦋 Changeset detectedLatest commit: 9a8794a The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
….com/primer/brand into danielguillan/color-scheme-attribute
🔍 Design token changes foundView CSS variable changes- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"],- [data-color-mode="light"] {
+ [data-color-mode="light"],- [data-color-mode="dark"] {
+ [data-color-mode="dark"], |
🟢 Unit test coverage changes foundUnit test coverage has been updated through this PR. Changes: 0 new tests, 0 removed tests, 2 improved, 0 decreased
|
🟢 No visual differences foundOur visual comparison tests did not find any differences in the UI. |
There was a problem hiding this comment.
Pull request overview
Adds first-class support for “derived” color modes (e.g. dark_dimmed, light_high_contrast) by having ThemeProvider expose both the requested data-color-mode and a derived data-color-scheme (light/dark) so existing dark/light-specific styling and token application continue to work.
Changes:
- Extend
ThemeProviderto accept arbitrary color mode strings, compute a base scheme viagetColorScheme, and renderdata-color-scheme. - Update component/recipe CSS selectors to treat
data-color-schemethe same asdata-color-modefor light/dark conditional styles. - Update design-token CSS output to apply light/dark token sets via
[data-color-scheme="light"|"dark"], plus add Storybook and visual coverage for derived modes.
Show a summary per file
| File | Description |
|---|---|
| packages/react/src/Token/Token.module.css | Minor diff; still contains dark-only selector for Token styling. |
| packages/react/src/ThemeProvider/ThemeProvider.visual.spec.ts | Adds a visual regression test for the derived color modes story. |
| packages/react/src/ThemeProvider/ThemeProvider.tsx | Adds getColorScheme, widens ColorMode, and renders data-color-scheme. |
| packages/react/src/ThemeProvider/ThemeProvider.test.tsx | Adds assertions and unit tests for derived modes and getColorScheme. |
| packages/react/src/ThemeProvider/ThemeProvider.stories.tsx | Adds a “DerivedColorModes” story and control options for derived modes. |
| packages/react/src/Text/Text.module.css | Makes antialiasing conditional on dark scheme as well as dark mode. |
| packages/react/src/SubNav/SubNav.module.css | Makes antialiasing conditional on dark scheme as well as dark mode. |
| packages/react/src/recipes/SolutionTemplates/SolutionPage/SolutionPage.module.css | Extends light/dark hero glow styling to also respond to scheme. |
| packages/react/src/recipes/SolutionTemplates/CategoryPage/CategoryPage.module.css | Extends light/dark hero styling to also respond to scheme. |
| packages/react/src/recipes/seo/Category/CategoryPage.module.css | Updates dark-only hover/selected styling to also respond to scheme. |
| packages/react/src/recipes/FlexTemplate/FlexSection/FlexSection.tsx | Uses getColorScheme for dark/light background image selection. |
| packages/react/src/recipes/FlexTemplate/FlexSection/components/FlexSectionTestimonials.tsx | Uses getColorScheme to select correct light/dark decorative assets. |
| packages/react/src/recipes/Flexsuite/Overview/FlexSuiteAIOverview.tsx | Uses getColorScheme for light/dark image selection and simplifies logic. |
| packages/react/src/recipes/Flexsuite/Overview/FlexSuiteAIOverview.module.css | Extends dark hover styling to also respond to scheme. |
| packages/react/src/recipes/FeaturePreviewLPs/FeaturePreviewLevelTwo/FeaturePreviewLevelTwo.module.css | Extends dark trailing-section styling to also respond to scheme. |
| packages/react/src/recipes/FeaturePreviewLPs/FeaturePreviewLevelOne/FeaturePreviewLevelOne.module.css | Extends dark trailing-section and form styling to also respond to scheme. |
| packages/react/src/MinimalFooter/MinimalFooter.tsx | Uses getColorScheme to choose correct logo fill for derived modes. |
| packages/react/src/MinimalFooter/MinimalFooter.test.tsx | Adds unit test to ensure derived dark modes use the dark logo fill. |
| packages/react/src/MinimalFooter/MinimalFooter.module.css | Extends light-only social icon filter styling to also respond to scheme. |
| packages/react/src/Label/Label.module.css | Extends dark antialiasing styling to also respond to scheme. |
| packages/react/src/InlineLink/InlineLink.module.css | Extends dark font-smoothing styling to also respond to scheme. |
| packages/react/src/Hero/Hero.stories.module.css | Extends story-only light/dark vars to also respond to scheme. |
| packages/react/src/Heading/Heading.module.css | Extends dark font-smoothing styling to also respond to scheme. |
| packages/react/src/Card/CardSkewEffect.tsx | Uses getColorScheme so skew behavior is consistent for derived modes. |
| packages/react/src/Card/Card.stories.shared.module.css | Extends dark hover styling to also respond to scheme. |
| packages/react/src/Accordion/Accordion.module.css | Extends dark content font-smoothing styling to also respond to scheme. |
| packages/design-tokens/src/formats/color-mode-attributes.test.js | Updates expected CSS output to include data-color-scheme selectors. |
| packages/design-tokens/src/formats/color-mode-attributes.js | Outputs token selectors for `[data-color-scheme="light" |
| .changeset/lazy-wolves-win.md | Documents the release impact for react-brand and brand-primitives. |
Copilot's findings
Comments suppressed due to low confidence (2)
packages/react/src/Token/Token.module.css:32
Tokenstill gates its dark-mode font-smoothing on[data-color-mode='dark']only, so derived dark modes (e.g.dark_dimmed) will not get the intended dark styling even though the provider now setsdata-color-scheme="dark". Update this selector to also match[data-color-scheme='dark'](consistent with the other modules changed in this PR).
[data-color-mode='dark'] .Token {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: auto;
}
packages/react/src/ThemeProvider/ThemeProvider.tsx:62
- When
colorModeisauto,activeModeis initialized to'auto'on the first render, sodata-color-schemewill initially render as"light"(viagetColorScheme('auto')) even if the system preference is dark. With the new[data-color-scheme]token selectors, this can cause a visible flash/incorrect tokens (especially for nested providers) until the effect runs. Consider initializing state with the resolved auto mode on the client (guardingwindowfor SSR), e.g.useState(() => (colorMode === AUTO && typeof window !== 'undefined' ? getActiveAutoMode() : colorMode)).
export function ThemeProvider({colorMode = defaultMode, children, ...rest}: PropsWithChildren<ThemeProviderProps>) {
const [activeMode, setActiveMode] = useState(colorMode)
const availableColorModes = useMemo(() => Object.values(ColorModesEnum), [])
useEffect(() => {
if (colorMode === ColorModesEnum.AUTO) {
setActiveMode(getActiveAutoMode())
} else if (activeMode !== colorMode) {
setActiveMode(colorMode)
}
return handleSystemPreferenceChange(setActiveMode)
}, [colorMode, activeMode, setActiveMode])
return (
<ThemeContext.Provider value={{colorMode: activeMode, availableColorModes}}>
<div data-color-mode={activeMode} data-color-scheme={getColorScheme(activeMode)} {...rest}>
{children}
- Files reviewed: 29/30 changed files
- Comments generated: 2
🟢 Bundle size report
|
Summary
Towards https://github.com/github/brand-experience/issues/80
ThemeProvideronly recognizeslight,dark, andautoas color modes. Consumers that need derived modes (e.g.dark_dimmed,light_high_contrast) can't pass them through without breaking dark/light-specific component styling.Adds a
data-color-schemeattribute next to the existingdata-color-modeand determines if a given derived theme is"light"or"dark"automatically.List of notable changes:
ThemeProvidermaps color modes to"light"or"dark"(adding support for derived themes without explicitly including them).data-color-schemeto set light or dark for the derived themes.What should reviewers focus on?
autoand/or passing a derived mode likedark_dimmedtoThemeProvidershould resolve to the correct scheme styling.Steps to test:
autoSupporting resources (related issues, external links, etc):
Contributor checklist:
update snapshotslabel to the PR)Reviewer checklist:
Screenshots:
screenshot-HiYRhVnZ-001022.mp4