From ae8f6782a7846e9d388ffb70530d577a75c7b234 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Wed, 29 Apr 2026 23:50:53 -0400 Subject: [PATCH 1/9] chore: forward @primer/react theming from @primer/styled-react under FF --- .../src/FeatureFlags/DefaultFeatureFlags.ts | 1 + .../FeatureFlaggedTheming.browser.test.tsx | 127 ++++++++++++++++++ .../src/components/FeatureFlaggedTheming.tsx | 47 +++++++ packages/styled-react/src/index.tsx | 4 +- 4 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 packages/styled-react/src/__tests__/FeatureFlaggedTheming.browser.test.tsx create mode 100644 packages/styled-react/src/components/FeatureFlaggedTheming.tsx diff --git a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts index 979363503f2..3331554c772 100644 --- a/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts +++ b/packages/react/src/FeatureFlags/DefaultFeatureFlags.ts @@ -4,4 +4,5 @@ export const DefaultFeatureFlags = FeatureFlagScope.create({ primer_react_css_anchor_positioning: false, primer_react_select_panel_fullscreen_on_narrow: false, primer_react_select_panel_order_selected_at_top: false, + primer_react_styled_react_use_primer_theme_providers: false, }) diff --git a/packages/styled-react/src/__tests__/FeatureFlaggedTheming.browser.test.tsx b/packages/styled-react/src/__tests__/FeatureFlaggedTheming.browser.test.tsx new file mode 100644 index 00000000000..9a90901e6a2 --- /dev/null +++ b/packages/styled-react/src/__tests__/FeatureFlaggedTheming.browser.test.tsx @@ -0,0 +1,127 @@ +import {render, screen} from '@testing-library/react' +import {describe, expect, it, vi} from 'vitest' +import React from 'react' +import {ThemeProvider, useTheme, BaseStyles} from '../' +import {FeatureFlags} from '@primer/react/experimental' + +// window.matchMedia() is not implemented by JSDOM so we have to create a mock: +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: vi.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })), +}) + +describe('FeatureFlaggedTheming', () => { + describe('when primer_react_styled_react_use_primer_theme_providers is disabled', () => { + it('ThemeProvider does not render a wrapper div with data-color-mode', () => { + render( + + +
Hello
+
+
, + ) + + // The styled ThemeProvider uses styled-components SCThemeProvider which + // does not inject a wrapper div. The child should not have a parent with data-color-mode. + const child = screen.getByTestId('child') + expect(child.parentElement).not.toHaveAttribute('data-color-mode') + }) + + it('useTheme returns styled theme context values', () => { + function ThemeConsumer() { + const theme = useTheme() + return
{theme.colorMode ?? 'day'}
+ } + + render( + + + + + , + ) + + expect(screen.getByTestId('theme-consumer')).toHaveTextContent('night') + }) + + it('BaseStyles renders with data-color-mode and without data-component', () => { + render( + + + +
Hello
+
+
+
, + ) + + const baseStyles = screen.getByTestId('base-styles') + expect(baseStyles).toHaveAttribute('data-color-mode') + expect(baseStyles).toHaveAttribute('data-light-theme') + expect(baseStyles).toHaveAttribute('data-dark-theme') + expect(baseStyles).not.toHaveAttribute('data-component') + }) + }) + + describe('when primer_react_styled_react_use_primer_theme_providers is enabled', () => { + it('ThemeProvider renders a wrapper div with data-color-mode', () => { + render( + + +
Hello
+
+
, + ) + + // The @primer/react ThemeProvider renders a
with data-color-mode + const child = screen.getByTestId('child') + expect(child.parentElement).toHaveAttribute('data-color-mode') + expect(child.parentElement).toHaveAttribute('data-light-theme') + expect(child.parentElement).toHaveAttribute('data-dark-theme') + }) + + it('useTheme returns primer theme context values', () => { + function ThemeConsumer() { + const theme = useTheme() + return
{theme.colorMode ?? 'day'}
+ } + + render( + + + + + , + ) + + expect(screen.getByTestId('theme-consumer')).toHaveTextContent('night') + }) + + it('BaseStyles renders with data-component and without data-color-mode', () => { + render( + + + +
Hello
+
+
+
, + ) + + const baseStyles = screen.getByTestId('base-styles') + expect(baseStyles).toHaveAttribute('data-component', 'BaseStyles') + expect(baseStyles).not.toHaveAttribute('data-color-mode') + expect(baseStyles).not.toHaveAttribute('data-light-theme') + expect(baseStyles).not.toHaveAttribute('data-dark-theme') + }) + }) +}) diff --git a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx new file mode 100644 index 00000000000..f36c0e8ef7b --- /dev/null +++ b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import { + ThemeProvider as PrimerThemeProvider, + useTheme as primerUseTheme, + BaseStyles as PrimerBaseStyles, +} from '@primer/react' +import type { + ThemeProviderProps as PrimerThemeProviderProps, + BaseStylesProps as PrimerBaseStylesProps, +} from '@primer/react' +import {useFeatureFlag} from '@primer/react/experimental' +import {ThemeProvider as StyledThemeProvider, useTheme as styledUseTheme, useColorSchemeVar} from './ThemeProvider' +import type {ThemeProviderProps as StyledThemeProviderProps} from './ThemeProvider' +import {BaseStyles as StyledBaseStyles} from './BaseStyles' +import type {BaseStylesProps as StyledBaseStylesProps} from './BaseStyles' + +export type ThemeProviderProps = StyledThemeProviderProps + +export type BaseStylesProps = StyledBaseStylesProps + +export const ThemeProvider: React.FC> = ({children, ...props}) => { + const enabled = useFeatureFlag('primer_react_styled_react_use_primer_theme_providers') + if (enabled) { + return {children} + } + return {children} +} + +export function useTheme() { + const enabled = useFeatureFlag('primer_react_styled_react_use_primer_theme_providers') + const styledTheme = styledUseTheme() + const primerTheme = primerUseTheme() + if (enabled) { + return primerTheme + } + return styledTheme +} + +export {useColorSchemeVar} + +export function BaseStyles(props: BaseStylesProps) { + const enabled = useFeatureFlag('primer_react_styled_react_use_primer_theme_providers') + if (enabled) { + return + } + return +} diff --git a/packages/styled-react/src/index.tsx b/packages/styled-react/src/index.tsx index 7b2aa90ed8b..a627c18748c 100644 --- a/packages/styled-react/src/index.tsx +++ b/packages/styled-react/src/index.tsx @@ -39,7 +39,7 @@ export { * `@primer/primitives` and CSS Modules instead. */ type ThemeProviderProps, -} from './components/ThemeProvider' +} from './components/FeatureFlaggedTheming' export { /** @@ -53,7 +53,7 @@ export { * supported. Use the component from `@primer/react` with CSS Modules instead. */ type BaseStylesProps, -} from './components/BaseStyles' +} from './components/FeatureFlaggedTheming' export { /** From ddb7f7e3627a560c23abd231848bb7c3545aced6 Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:52:49 -0400 Subject: [PATCH 2/9] Update theming for @primer/react Forward theming from @primer/styled-react to @primer/react under FF. --- .changeset/fuzzy-kangaroos-cover.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fuzzy-kangaroos-cover.md diff --git a/.changeset/fuzzy-kangaroos-cover.md b/.changeset/fuzzy-kangaroos-cover.md new file mode 100644 index 00000000000..698176a7ee6 --- /dev/null +++ b/.changeset/fuzzy-kangaroos-cover.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +chore: forward @primer/react theming from @primer/styled-react under FF From 09833e6347894f286be9e7f24a71bd58f6c7d2ca Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:53:35 -0400 Subject: [PATCH 3/9] Forward theming from @primer/styled-react --- .changeset/fuzzy-kangaroos-cover.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/fuzzy-kangaroos-cover.md b/.changeset/fuzzy-kangaroos-cover.md index 698176a7ee6..31f2ec8bf08 100644 --- a/.changeset/fuzzy-kangaroos-cover.md +++ b/.changeset/fuzzy-kangaroos-cover.md @@ -1,5 +1,6 @@ --- "@primer/react": patch +"@primer/styled-react": patch --- chore: forward @primer/react theming from @primer/styled-react under FF From c4f07998aba575324b3454d8edd4a1c6a9307664 Mon Sep 17 00:00:00 2001 From: francinelucca <40550942+francinelucca@users.noreply.github.com> Date: Thu, 30 Apr 2026 04:00:53 +0000 Subject: [PATCH 4/9] chore: auto-fix lint and formatting issues --- packages/styled-react/src/components/FeatureFlaggedTheming.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx index f36c0e8ef7b..39b9b5f388f 100644 --- a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx +++ b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import type React from 'react' import { ThemeProvider as PrimerThemeProvider, useTheme as primerUseTheme, From da45ff7ea2c605f6e49a82f1a64596890d961c58 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Thu, 30 Apr 2026 00:01:05 -0400 Subject: [PATCH 5/9] lint --- packages/styled-react/src/components/FeatureFlaggedTheming.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx index f36c0e8ef7b..39b9b5f388f 100644 --- a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx +++ b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import type React from 'react' import { ThemeProvider as PrimerThemeProvider, useTheme as primerUseTheme, From 62f9056acfe48b06252c4706aeae251bbede3447 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Thu, 7 May 2026 16:08:31 -0400 Subject: [PATCH 6/9] fix changesets --- .changeset/fuzzy-kangaroos-cover.md | 3 +-- .changeset/styled-react-forward-theming.md | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/styled-react-forward-theming.md diff --git a/.changeset/fuzzy-kangaroos-cover.md b/.changeset/fuzzy-kangaroos-cover.md index 31f2ec8bf08..32c705359a9 100644 --- a/.changeset/fuzzy-kangaroos-cover.md +++ b/.changeset/fuzzy-kangaroos-cover.md @@ -1,6 +1,5 @@ --- "@primer/react": patch -"@primer/styled-react": patch --- -chore: forward @primer/react theming from @primer/styled-react under FF +chore: add `primer_react_css_modules_theming` feature flag to DefaultFeatureFlags diff --git a/.changeset/styled-react-forward-theming.md b/.changeset/styled-react-forward-theming.md new file mode 100644 index 00000000000..77a1f57f4df --- /dev/null +++ b/.changeset/styled-react-forward-theming.md @@ -0,0 +1,5 @@ +--- +"@primer/styled-react": patch +--- + +chore: forward @primer/react theming from @primer/styled-react under feature flag From 8894da70be3e27533dbc4e021ff7adf29b21cd3f Mon Sep 17 00:00:00 2001 From: Marie Lucca <40550942+francinelucca@users.noreply.github.com> Date: Thu, 7 May 2026 16:09:11 -0400 Subject: [PATCH 7/9] Update feature flag in DefaultFeatureFlags --- .changeset/fuzzy-kangaroos-cover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/fuzzy-kangaroos-cover.md b/.changeset/fuzzy-kangaroos-cover.md index 32c705359a9..3c1827f0e40 100644 --- a/.changeset/fuzzy-kangaroos-cover.md +++ b/.changeset/fuzzy-kangaroos-cover.md @@ -2,4 +2,4 @@ "@primer/react": patch --- -chore: add `primer_react_css_modules_theming` feature flag to DefaultFeatureFlags +chore: add `primer_react_styled_react_use_primer_theme_providers` feature flag to DefaultFeatureFlags From aa3cfda237d2640fbcbf03e1a93d8901091e52c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 21:11:28 +0000 Subject: [PATCH 8/9] fix: add explicit return type annotation to useTheme in FeatureFlaggedTheming to resolve TS2883 error Agent-Logs-Url: https://github.com/primer/react/sessions/9d9495e8-f002-47e8-aa62-f6df92cf2d9b Co-authored-by: francinelucca <40550942+francinelucca@users.noreply.github.com> --- .../styled-react/src/components/FeatureFlaggedTheming.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx index 39b9b5f388f..6728a76b447 100644 --- a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx +++ b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx @@ -26,12 +26,12 @@ export const ThemeProvider: React.FC return {children} } -export function useTheme() { +export function useTheme(): ReturnType { const enabled = useFeatureFlag('primer_react_styled_react_use_primer_theme_providers') const styledTheme = styledUseTheme() const primerTheme = primerUseTheme() if (enabled) { - return primerTheme + return primerTheme as ReturnType } return styledTheme } From 3f56c49f3e1f19a9e274b258686c0272f1aee6ec Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Thu, 7 May 2026 18:49:12 -0400 Subject: [PATCH 9/9] change types --- .../styled-react/src/components/FeatureFlaggedTheming.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx index 6728a76b447..3fa68d7927d 100644 --- a/packages/styled-react/src/components/FeatureFlaggedTheming.tsx +++ b/packages/styled-react/src/components/FeatureFlaggedTheming.tsx @@ -26,12 +26,12 @@ export const ThemeProvider: React.FC return {children} } -export function useTheme(): ReturnType { +export function useTheme(): ReturnType { const enabled = useFeatureFlag('primer_react_styled_react_use_primer_theme_providers') const styledTheme = styledUseTheme() const primerTheme = primerUseTheme() if (enabled) { - return primerTheme as ReturnType + return primerTheme as ReturnType } return styledTheme }