Skip to content

Conversation

@mattcosta7
Copy link
Contributor

@mattcosta7 mattcosta7 commented Dec 11, 2025

This pull request addresses a ResizeObserver performance regression in @primer/react's PageLayout component and introduces several optimizations and improvements. The main changes focus on improving drag performance by replacing expensive CSS selectors with a more efficient JavaScript-driven approach, updating the drag handle logic, and enhancing performance testing stories.

Performance and architectural optimizations:

  • Replaces expensive :has() CSS selectors with a JavaScript-managed data-dragging attribute on .PageLayoutContent, improving drag performance by avoiding costly style recalculations. [1] [2] [3]
  • Refactors the drag handle logic: splits the vertical divider and drag handle into separate components, manages ARIA attributes more efficiently, and synchronizes drag state between handle and content for both pointer and keyboard interactions. [1] [2] [3] [4] [5] [6]
  • Removes the shared ResizeObserver subscription logic for viewport width tracking, simplifying code and relying on more direct CSS variable reads for pane sizing.

Accessibility and usability improvements:

  • Adds ARIA attributes (aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext) to the drag handle for improved accessibility, updating them efficiently during drag operations. [1] [2] [3]
  • Keyboard resizing step is now a constant (ARROW_KEY_STEP), making adjustments easier and more consistent. [1] [2]

Performance testing enhancements:

  • Updates the performance stories to include a realistic, stateful SearchInput (with Autocomplete), making the test scenarios more representative of real-world usage. [1] [2] [3] [4] [5] [6]

Bug fixes:

  • Fixes a performance regression related to ResizeObserver by simplifying how pane sizing and drag state are managed.

Context and maintainability:

  • Introduces a contentRef in the PageLayoutContext to allow coordinated updates and more maintainable code when managing drag state. [1] [2] [3] [4]

These changes collectively result in smoother drag interactions, better accessibility, and more maintainable code in the PageLayout component.

Changelog

New

Changed

Removed

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 11, 2025

🦋 Changeset detected

Latest commit: 8c5edd0

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 11, 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.

Comment on lines -840 to -888
handleRef={handleRef}
onDrag={(delta, isKeyboard = false) => {
const deltaWithDirection = isKeyboard ? delta : position === 'end' ? -delta : delta
const maxWidth = maxPaneWidthRef.current

if (isKeyboard) {
// Clamp keyboard delta to stay within bounds
const newWidth = Math.max(minPaneWidth, Math.min(maxWidth, currentWidthRef.current! + deltaWithDirection))
if (newWidth !== currentWidthRef.current) {
currentWidthRef.current = newWidth
paneRef.current?.style.setProperty('--pane-width', `${newWidth}px`)
updateAriaValues(handleRef.current, {current: newWidth})
}
} else {
// Apply delta directly via CSS variable for immediate visual feedback
if (paneRef.current) {
const newWidth = currentWidthRef.current! + deltaWithDirection
const clampedWidth = Math.max(minPaneWidth, Math.min(maxWidth, newWidth))

// Only update if the clamped width actually changed
// This prevents drift when dragging against min/max constraints
if (clampedWidth !== currentWidthRef.current) {
paneRef.current.style.setProperty('--pane-width', `${clampedWidth}px`)
currentWidthRef.current = clampedWidth
updateAriaValues(handleRef.current, {current: clampedWidth})
}
}
}
}}
// Save final width to localStorage (skip React state update to avoid reconciliation)
onDragEnd={() => {
// For mouse drag: The CSS variable is already set and currentWidthRef is in sync.
// We intentionally skip setPaneWidth() to avoid triggering expensive React
// reconciliation with large DOM trees. The ref is the source of truth for
// subsequent drag operations.
setWidthInLocalStorage(currentWidthRef.current!)
}}
position={positionProp}
// Reset pane width on double click
onDoubleClick={() => {
const defaultWidth = getDefaultPaneWidth(width)
// Update CSS variable and ref directly - skip React state to avoid reconciliation
if (paneRef.current) {
paneRef.current.style.setProperty('--pane-width', `${defaultWidth}px`)
currentWidthRef.current = defaultWidth
updateAriaValues(handleRef.current, {current: defaultWidth})
}
setWidthInLocalStorage(defaultWidth)
}}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved into the drag handle

}
}
})
viewportWidthObserver.observe(document.documentElement)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this was causing observation on any change inside the body, not just page resizing

* ARIA values are set in JSX for SSR accessibility,
* then updated via DOM manipulation during drag for performance
*/
const DragHandle: React.FC<DragHandleProps> = ({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

pulls this out to a a child, and avoid passing through props to the divider

}
// If pane is resizable, the divider should be draggable
draggable={resizable}
handleRef={handleRef}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moves the draghandle to a child - so we can keep VerticalDividerslimmer

// ResizeObserver on document.documentElement fires on any content change (typing, etc),
// causing INP regressions. Window resize only fires on viewport changes.
// eslint-disable-next-line github/prefer-observers
window.addEventListener('resize', throttledUpdateMax)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

replaces the resize observer with a more granular raf

@github-actions github-actions bot requested a deployment to storybook-preview-7302 December 11, 2025 16:15 Abandoned
@github-actions github-actions bot temporarily deployed to storybook-preview-7302 December 11, 2025 16:27 Inactive
@mattcosta7 mattcosta7 force-pushed the fix-resize-obsever-subscripotion branch from c7abafa to 70f8c48 Compare December 12, 2025 04:42
@github-actions github-actions bot temporarily deployed to storybook-preview-7302 December 12, 2025 04:52 Inactive
@mattcosta7 mattcosta7 changed the base branch from main to revert-7305-revert-7275-revert-7274-revert-7251-mc/copilot/sub-pr-7248 December 12, 2025 05:01
@primer-integration
Copy link

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

@primer-integration
Copy link

🔬 github-ui Integration Test Results

Check Status Details
CI ✅ Passed View run
Projects (Memex) ⏳ Pending Waiting for workflow to complete
VRT ✅ Passed View run

@mattcosta7 mattcosta7 changed the title Removes a resize subscription from pagelayout Removes a resize subscription and expensive :has selectors from pagelayout Dec 12, 2025
@mattcosta7 mattcosta7 self-assigned this Dec 12, 2025
@mattcosta7 mattcosta7 marked this pull request as ready for review December 12, 2025 14:54
@mattcosta7 mattcosta7 requested a review from a team as a code owner December 12, 2025 14:54
@mattcosta7 mattcosta7 requested a review from jonrohan December 12, 2025 14:54
@mattcosta7 mattcosta7 merged commit 8b08d12 into revert-7305-revert-7275-revert-7274-revert-7251-mc/copilot/sub-pr-7248 Dec 12, 2025
46 checks passed
@mattcosta7 mattcosta7 deleted the fix-resize-obsever-subscripotion branch December 12, 2025 14:54
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.

2 participants