Skip to content

Conversation

@mattcosta7
Copy link
Contributor

@mattcosta7 mattcosta7 commented Dec 12, 2025

Reverts #7305
Fixes a css issue with arbitrarily deep css class:has() selectors by moving data-dragging to the page layout - in

related to https://github.com/github/pull-requests/issues/21237

I merged separate updates into this:

This pull request reapplies and improves the PageLayout resizable enhancements in @primer/react, focusing on performance optimizations during pane resizing while avoiding previous issues with INP (Interaction to Next Paint) caused by expensive CSS selectors. The changes introduce new CSS containment strategies, export key CSS variables for use in JavaScript, and add new tests to ensure correct drag state handling. It also updates test snapshots and skips new performance tests in the E2E suite.

PageLayout Resizable Enhancements and Performance Optimizations:

  • Added aggressive CSS containment rules (contain: layout style paint) to .ContentWrapper, .Content, and .Pane elements during drag operations, reducing layout and paint costs and improving performance for large content areas. [1] [2] [3]
  • Introduced and tested a data-dragging attribute on PageLayoutContent to efficiently toggle drag state, replacing reliance on global selectors and making it easier to apply performance optimizations only when needed.
  • Updated the DraggableHandle to prevent touch scrolling and text selection during drag, and ensured the correct cursor is shown. [1] [2]

Code and Documentation Updates:

  • Exported CSS custom properties for pane width calculations to JavaScript, improving maintainability and clarity in how breakpoints and max-widths are managed. [1] [2] [3]
  • Updated E2E and snapshot tests to reflect the new drag behavior and style changes. [1] [2] [3] [4] [5]

Meta/Process:

  • Reapplied the resizable enhancements after reverting a previous PR, ensuring no input performance drop from expensive selectors. [1] [2]

Changelog

No api changes

New

n/a

Changed

Improved drag to resize performance for PageLayout resizable

Removed

n/a

Rollout strategy

  • Patch release
  • Minor release
  • Major release; if selected, include a written rollout or migration plan
  • None; if selected, include a brief description as to why

Testing & Reviewing

Merge checklist

@changeset-bot
Copy link

changeset-bot bot commented Dec 12, 2025

🦋 Changeset detected

Latest commit: b2e840f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

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

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Dec 12, 2025
@github-actions
Copy link
Contributor

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the integration-tests: skipped manually label to skip these checks.

@mattcosta7 mattcosta7 self-assigned this Dec 12, 2025
Comment on lines +1 to 7
/* Exported values for JavaScript consumption */
:export {
/* Breakpoint where --pane-max-width-diff changes (used in usePaneWidth.ts) */
paneMaxWidthDiffBreakpoint: 1280;
/* Default value for --pane-max-width-diff below the breakpoint */
paneMaxWidthDiffDefault: 511;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if we have prefernces for using these across JS/CSS or just duplicating them?

Copy link
Member

@siddharthkp siddharthkp Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL about :export

Sorry, trying to understand the goal here. Why are these exported from PageLayout.module.css? We don't use them in this file

Nevermind, I realise you can't use the exports within the file because they will be removed during compilation

* Handles initialization from storage, clamping on viewport resize, and provides
* functions to save and reset width.
*/
export function usePaneWidth({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extracted from PageLayout because that file is huge and getting harder to navigate

@mattcosta7 mattcosta7 marked this pull request as ready for review December 12, 2025 15:04
@mattcosta7 mattcosta7 requested a review from a team as a code owner December 12, 2025 15:04
@github-actions github-actions bot temporarily deployed to storybook-preview-7307 December 12, 2025 15:05 Inactive
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request re-applies performance improvements to the PageLayout component's resizable pane functionality, fixing CSS selector performance issues that caused the previous implementation to be reverted. The core optimization replaces expensive :has() selectors with simple descendant selectors by moving the data-dragging attribute from the drag handle to the PageLayoutContent parent element.

Key Changes:

  • Introduces a new usePaneWidth custom hook to manage pane width state, localStorage persistence, and viewport-responsive constraints
  • Refactors drag handling to use modern pointer events with pointer capture for better cross-device compatibility
  • Applies aggressive CSS containment and GPU acceleration during drag operations to minimize layout thrashing
  • Synchronizes CSS breakpoint values with JavaScript using CSS :export to ensure consistency

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/react/src/PageLayout/usePaneWidth.ts New custom hook encapsulating pane width state management, localStorage persistence, viewport constraint calculations, and ARIA attribute updates with optimized resize handling
packages/react/src/PageLayout/usePaneWidth.test.ts Comprehensive unit tests for the new hook covering initialization, localStorage persistence, constraint calculations, and resize listener behavior
packages/react/src/PageLayout/PageLayout.tsx Refactored to use the new hook, replaced mouse events with pointer events, extracted DragHandle component, and moved data-dragging attribute management to parent for performance
packages/react/src/PageLayout/PageLayout.module.css Adds CSS :export for JavaScript consumption, replaces :has() selectors with descendant selectors, and adds performance optimizations (containment, GPU acceleration) during drag
packages/react/src/PageLayout/PageLayout.test.tsx New tests verifying data-dragging attribute is correctly set/removed during both pointer and keyboard resize operations
packages/react/src/PageLayout/PageLayout.performance.stories.tsx New Storybook stories for manual performance testing with varying DOM complexity (light/medium/heavy content) and ARIA attribute monitoring
packages/react/src/PageLayout/__snapshots__/PageLayout.test.tsx.snap Updated snapshots reflecting removal of inline --pane-width style for non-resizable panes
e2e/components/Axe.test.ts Skips performance test stories from accessibility tests to avoid timeouts on large DOM structures
.changeset/shiny-buckets-study.md Documents the patch fix for ResizeObserver performance regression
.changeset/healthy-poets-act.md Removes changeset for the reverted PR

// suppressHydrationWarning: We intentionally read from localStorage during
// useState init to avoid resize flicker, which causes a hydration mismatch
// for --pane-width. This only affects this element, not children.
suppressHydrationWarning
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this in a later PR where I removed a layouteffect that reads localStorage.

we should probably provide alternatives to this - but for now, just keeping it and suppressing the warning since we're accepting this by having width stored in local state and not having a good mechanism for setting it on the server

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5817e16

this commit flips between the options here - I'm open to either - or keeping as is for now (inline with today's behavior) and moving that to a followup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed an issue for this also #7311 since today's implementation is not safe

Copy link
Contributor

@Lukeghenco Lukeghenco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval from PR eng who uses it.

Comment on lines +157 to +178
// CSS variable, not DOM structure or children.
const [currentWidth, setCurrentWidth] = React.useState(() => {
const defaultWidth = getDefaultPaneWidth(width)

if (!resizable || !canUseDOM) {
return defaultWidth
}

try {
const storedWidth = localStorage.getItem(widthStorageKey)
if (storedWidth !== null) {
const parsed = Number(storedWidth)
if (!isNaN(parsed) && parsed > 0) {
return parsed
}
}
} catch {
// localStorage unavailable - keep default
}

return defaultWidth
})
Copy link
Contributor Author

@mattcosta7 mattcosta7 Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this here for now, but I think we should move to a layout effect or some other pattern.
do we have a way to preseed this into SSR to avoid hydration mismatches - or do we always want to accept a CLS flash or hydration error/supression requirement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related issue (for today's impl too) #7311

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

@mattcosta7 mattcosta7 enabled auto-merge December 15, 2025 18:34
@francinelucca francinelucca changed the title Revert "Revert "Revert "Revert "Improve PageLayout pane drag performance with pointer capture and GPU-accelerated transforms"""" Improve PageLayout pane drag performance with pointer capture and GPU-accelerated transforms Dec 15, 2025
@primer-integration
Copy link

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/8861

@primer-integration
Copy link

🔬 github-ui Integration Test Results

Check Status Details
CI ✅ Passed View run
Projects (Memex) ✅ Passed View run
VRT ✅ Passed View run

All checks passed! Your integration PR is ready for review.

@mattcosta7 mattcosta7 added this pull request to the merge queue Dec 15, 2025
Merged via the queue into main with commit 5dcc87c Dec 15, 2025
53 checks passed
@mattcosta7 mattcosta7 deleted the revert-7305-revert-7275-revert-7274-revert-7251-mc/copilot/sub-pr-7248 branch December 15, 2025 19:31
@primer primer bot mentioned this pull request Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants