From 80f633ccc59e5a58890aa79a064ffb38912bacfe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 05:56:32 +0000 Subject: [PATCH 1/4] Initial plan From 746b5eda2a9473099bd72851a24d8a29d32c9b9e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 06:06:05 +0000 Subject: [PATCH 2/4] feat: Add feature flag for BaseStyles performance optimization - Modified BaseStyles.module.css to use negative data attribute selector - Added comprehensive comments explaining the feature flag behavior - Created changeset for the patch release - All tests passing, linting and type checking successful Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com> --- ...rf-basestyles-has-selector-feature-flag.md | 11 +++++++++ packages/react/src/BaseStyles.module.css | 23 +++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 .changeset/perf-basestyles-has-selector-feature-flag.md diff --git a/.changeset/perf-basestyles-has-selector-feature-flag.md b/.changeset/perf-basestyles-has-selector-feature-flag.md new file mode 100644 index 00000000000..1c22757a811 --- /dev/null +++ b/.changeset/perf-basestyles-has-selector-feature-flag.md @@ -0,0 +1,11 @@ +--- +'@primer/react': patch +--- + +perf(BaseStyles): Feature-flag expensive :has([data-color-mode]) selectors + +Add a feature flag (`data-primer-css-perf-has-selector` data attribute) to opt-in to skipping expensive `:has([data-color-mode])` selectors that scan the entire DOM on every style recalculation. + +To enable the optimization, add `data-primer-css-perf-has-selector` to an ancestor element (e.g., `` or `
`). Input color-scheme is already handled by global selectors in the codebase. + +See #7325 and #7312 for context on this performance optimization. diff --git a/packages/react/src/BaseStyles.module.css b/packages/react/src/BaseStyles.module.css index 76259ac05dd..0b35a465a3b 100644 --- a/packages/react/src/BaseStyles.module.css +++ b/packages/react/src/BaseStyles.module.css @@ -60,15 +60,30 @@ details-dialog:focus:not(:focus-visible):not(:global(.focus-visible)) { /* stylelint-disable-next-line primer/colors */ color: var(--BaseStyles-fgColor, var(--fgColor-default)); - /* Global styles for light mode */ - &:has([data-color-mode='light']) { + /* + * PERFORMANCE: The :has([data-color-mode]) selectors below are expensive + * as they scan the entire DOM on every style recalculation. + * Input color-scheme is already handled by global selectors above: + * [data-color-mode='light'] input { color-scheme: light; } + * [data-color-mode='dark'] input { color-scheme: dark; } + * + * Feature flag: When [data-primer-css-perf-has-selector] is NOT present on + * an ancestor element, the old (expensive) behavior is preserved. + * Add [data-primer-css-perf-has-selector] to an ancestor to opt-in to the + * optimized behavior (which skips these expensive selectors). + * + * See #7325 and #7312 for context on this performance optimization. + */ + + /* Global styles for light mode - only apply when feature flag is NOT present */ + &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='light']) { input & { color-scheme: light; } } - /* Global styles for dark mode */ - &:has([data-color-mode='dark']) { + /* Global styles for dark mode - only apply when feature flag is NOT present */ + &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='dark']) { input & { color-scheme: dark; } From 730672608bba5e342fcd0cfacf5a6eb19cf2f991 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 14:43:48 +0000 Subject: [PATCH 3/4] refactor: Avoid universal selector by placing flag on BaseStyles element Changed selector from `&:not([data-primer-css-perf-has-selector] *)` to `&:not([data-primer-css-perf-has-selector])` to avoid the universal selector. The attribute should now be placed directly on the BaseStyles element instead of an ancestor. Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com> --- .../perf-basestyles-has-selector-feature-flag.md | 2 +- packages/react/src/BaseStyles.module.css | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.changeset/perf-basestyles-has-selector-feature-flag.md b/.changeset/perf-basestyles-has-selector-feature-flag.md index 1c22757a811..51667543762 100644 --- a/.changeset/perf-basestyles-has-selector-feature-flag.md +++ b/.changeset/perf-basestyles-has-selector-feature-flag.md @@ -6,6 +6,6 @@ perf(BaseStyles): Feature-flag expensive :has([data-color-mode]) selectors Add a feature flag (`data-primer-css-perf-has-selector` data attribute) to opt-in to skipping expensive `:has([data-color-mode])` selectors that scan the entire DOM on every style recalculation. -To enable the optimization, add `data-primer-css-perf-has-selector` to an ancestor element (e.g., `` or ``). Input color-scheme is already handled by global selectors in the codebase. +To enable the optimization, add `data-primer-css-perf-has-selector` to the BaseStyles element. Input color-scheme is already handled by global selectors in the codebase. See #7325 and #7312 for context on this performance optimization. diff --git a/packages/react/src/BaseStyles.module.css b/packages/react/src/BaseStyles.module.css index 0b35a465a3b..19dd3e61389 100644 --- a/packages/react/src/BaseStyles.module.css +++ b/packages/react/src/BaseStyles.module.css @@ -68,22 +68,22 @@ details-dialog:focus:not(:focus-visible):not(:global(.focus-visible)) { * [data-color-mode='dark'] input { color-scheme: dark; } * * Feature flag: When [data-primer-css-perf-has-selector] is NOT present on - * an ancestor element, the old (expensive) behavior is preserved. - * Add [data-primer-css-perf-has-selector] to an ancestor to opt-in to the - * optimized behavior (which skips these expensive selectors). + * the BaseStyles element, the old (expensive) behavior is preserved. + * Add [data-primer-css-perf-has-selector] to the BaseStyles element to opt-in + * to the optimized behavior (which skips these expensive selectors). * * See #7325 and #7312 for context on this performance optimization. */ /* Global styles for light mode - only apply when feature flag is NOT present */ - &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='light']) { + &:not([data-primer-css-perf-has-selector]):has([data-color-mode='light']) { input & { color-scheme: light; } } /* Global styles for dark mode - only apply when feature flag is NOT present */ - &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='dark']) { + &:not([data-primer-css-perf-has-selector]):has([data-color-mode='dark']) { input & { color-scheme: dark; } From 268138016e3973c11121ee413f67d2d59735c35a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 25 Dec 2025 16:14:15 +0000 Subject: [PATCH 4/4] feat: Use feature flag system to automatically add perf attribute - Added primer_react_css_perf_has_selector to DefaultFeatureFlags - BaseStyles component now uses useFeatureFlag hook - Automatically adds data-primer-css-perf-has-selector when flag is enabled - Added tests to verify feature flag behavior - Updated documentation to reference feature flag usage Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com> --- .../perf-basestyles-has-selector-feature-flag.md | 4 ++-- packages/react/src/BaseStyles.module.css | 8 ++++---- packages/react/src/BaseStyles.tsx | 3 +++ .../react/src/FeatureFlags/DefaultFeatureFlags.ts | 1 + packages/react/src/__tests__/BaseStyles.test.tsx | 15 +++++++++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/.changeset/perf-basestyles-has-selector-feature-flag.md b/.changeset/perf-basestyles-has-selector-feature-flag.md index 51667543762..53b4dd93fd5 100644 --- a/.changeset/perf-basestyles-has-selector-feature-flag.md +++ b/.changeset/perf-basestyles-has-selector-feature-flag.md @@ -4,8 +4,8 @@ perf(BaseStyles): Feature-flag expensive :has([data-color-mode]) selectors -Add a feature flag (`data-primer-css-perf-has-selector` data attribute) to opt-in to skipping expensive `:has([data-color-mode])` selectors that scan the entire DOM on every style recalculation. +Add a feature flag (`primer_react_css_perf_has_selector`) to opt-in to skipping expensive `:has([data-color-mode])` selectors that scan the entire DOM on every style recalculation. -To enable the optimization, add `data-primer-css-perf-has-selector` to the BaseStyles element. Input color-scheme is already handled by global selectors in the codebase. +To enable the optimization, set the `primer_react_css_perf_has_selector` feature flag to `true` via the `FeatureFlags` component. The BaseStyles component will automatically add the `data-primer-css-perf-has-selector` attribute when the flag is enabled. Input color-scheme is already handled by global selectors in the codebase. See #7325 and #7312 for context on this performance optimization. diff --git a/packages/react/src/BaseStyles.module.css b/packages/react/src/BaseStyles.module.css index 19dd3e61389..8eb23fffcba 100644 --- a/packages/react/src/BaseStyles.module.css +++ b/packages/react/src/BaseStyles.module.css @@ -67,10 +67,10 @@ details-dialog:focus:not(:focus-visible):not(:global(.focus-visible)) { * [data-color-mode='light'] input { color-scheme: light; } * [data-color-mode='dark'] input { color-scheme: dark; } * - * Feature flag: When [data-primer-css-perf-has-selector] is NOT present on - * the BaseStyles element, the old (expensive) behavior is preserved. - * Add [data-primer-css-perf-has-selector] to the BaseStyles element to opt-in - * to the optimized behavior (which skips these expensive selectors). + * Feature flag: When the primer_react_css_perf_has_selector feature flag + * is disabled (default), the old (expensive) behavior is preserved. + * Enable the feature flag via FeatureFlags to opt-in to the optimized + * behavior (which skips these expensive selectors). * * See #7325 and #7312 for context on this performance optimization. */ diff --git a/packages/react/src/BaseStyles.tsx b/packages/react/src/BaseStyles.tsx index 4631ede237a..780adf6dbe5 100644 --- a/packages/react/src/BaseStyles.tsx +++ b/packages/react/src/BaseStyles.tsx @@ -3,6 +3,7 @@ import {type CSSProperties, type PropsWithChildren, type JSX} from 'react' import {clsx} from 'clsx' import classes from './BaseStyles.module.css' +import {useFeatureFlag} from './FeatureFlags' import 'focus-visible' @@ -14,6 +15,7 @@ export type BaseStylesProps = PropsWithChildren & { color?: string // Fixes `color` ts-error } function BaseStyles({children, color, className, as: Component = 'div', style, ...rest}: BaseStylesProps) { + const cssPerfHasSelector = useFeatureFlag('primer_react_css_perf_has_selector') const newClassName = clsx(classes.BaseStyles, className) const baseStyles = { ['--BaseStyles-fgColor']: color, @@ -23,6 +25,7 @@ function BaseStyles({children, color, className, as: Component = 'div', style, .