Graduate primer_react_css_has_selector_perf feature flag#7633
Graduate primer_react_css_has_selector_perf feature flag#7633
primer_react_css_has_selector_perf feature flag#7633Conversation
🦋 Changeset detectedLatest commit: 94a5e2f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com>
primer_react_css_has_selector_perf feature flag
|
There was a problem hiding this comment.
Pull request overview
Graduates the primer_react_css_has_selector_perf feature flag by making the Dialog scroll-disable mechanism always use body[data-dialog-scroll-disabled], and removes the temporary feature-flag scaffolding across FeatureFlags, Dialog, CSS, and tests.
Changes:
- Removed
primer_react_css_has_selector_perffrom default feature flags and deleted all feature-flag gating infrastructure. - Updated
Dialogto always set/removedata-dialog-scroll-disabledondocument.bodyusing a module-level ref counter. - Removed legacy
:has()/.DisableScrollCSS path and simplified test coverage to validate the always-on behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react/src/FeatureFlags/tests/FeatureFlags.test.tsx | Removes tests and reset hooks related to the now-removed scroll optimization feature flag. |
| packages/react/src/FeatureFlags/FeatureFlags.tsx | Simplifies FeatureFlags back to a pure context provider by removing temporary body-attribute management. |
| packages/react/src/FeatureFlags/DefaultFeatureFlags.ts | Drops the graduated primer_react_css_has_selector_perf flag from defaults. |
| packages/react/src/Dialog/Dialog.tsx | Makes data-dialog-scroll-disabled body attribute management unconditional and ref-counted at the Dialog level. |
| packages/react/src/Dialog/Dialog.test.tsx | Replaces flag-conditional tests with a scroll disable behavior suite for the new always-on behavior. |
| packages/react/src/Dialog/Dialog.module.css | Removes .DisableScroll and the legacy :has() selector rule, leaving only body[data-dialog-scroll-disabled]. |
| React.useEffect(() => { | ||
| const scrollbarWidth = window.innerWidth - document.body.clientWidth | ||
| const dialog = dialogRef.current | ||
| const usePerfOptimization = document.body.hasAttribute('data-dialog-scroll-optimized') | ||
|
|
||
| // Add DisableScroll class to this dialog (for legacy :has() selector path) | ||
| dialog?.classList.add(classes.DisableScroll) | ||
| dialogScrollDisabledCount++ | ||
| document.body.style.setProperty('--prc-dialog-scrollgutter', `${scrollbarWidth}px`) | ||
|
|
||
| if (usePerfOptimization) { | ||
| // Optimized path: set attribute on body for direct CSS targeting | ||
| document.body.setAttribute('data-dialog-scroll-disabled', '') | ||
| } | ||
| // Legacy path: no action needed - CSS :has(.Dialog.DisableScroll) handles it | ||
| document.body.setAttribute('data-dialog-scroll-disabled', '') | ||
|
|
There was a problem hiding this comment.
--prc-dialog-scrollgutter is recomputed and set on every dialog mount. When a second dialog mounts after scroll has already been disabled, document.body.clientWidth will typically equal window.innerWidth (no scrollbar), making scrollbarWidth become 0 and overwriting the previously-correct gutter value. This can reintroduce layout shift while dialogs are still open. Consider only computing/setting the gutter (and the data-dialog-scroll-disabled attribute) when the ref count transitions from 0 → 1, and only removing them when it transitions back to 0.
See below for a potential fix:
dialogScrollDisabledCount++
if (dialogScrollDisabledCount === 1) {
const scrollbarWidth = window.innerWidth - document.body.clientWidth
document.body.style.setProperty('--prc-dialog-scrollgutter', `${scrollbarWidth}px`)
document.body.setAttribute('data-dialog-scroll-disabled', '')
}
| for (const footerButton of footerButtons) { | ||
| if (footerButton.autoFocus) { | ||
| // eslint-disable-next-line react-hooks/immutability | ||
| footerButton.ref = autoFocusedFooterButtonRef | ||
| } |
There was a problem hiding this comment.
The footerButtons loop still mutates footerButton.ref, but the eslint-disable-next-line react-hooks/immutability was removed. If the repo’s immutability lint rule still applies here (it’s used elsewhere), this will fail linting. Either restore the targeted eslint-disable for this assignment or refactor to avoid mutating the footerButtons objects (e.g., derive a new array/object for rendering).
…f flag Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com>
primer_react_css_has_selector_perffeature flagOriginal prompt
This pull request was created from Copilot chat.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.