From ae8f6782a7846e9d388ffb70530d577a75c7b234 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Wed, 29 Apr 2026 23:50:53 -0400 Subject: [PATCH 1/5] 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/5] 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/5] 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/5] 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/5] 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,