Skip to content

Commit 12e12f5

Browse files
mattcosta7CopilotCopilot
authored
Improve PageLayout pane drag performance with pointer capture and GPU-accelerated transforms (#7251)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 8421808 commit 12e12f5

File tree

6 files changed

+881
-160
lines changed

6 files changed

+881
-160
lines changed

.changeset/olive-heads-enter.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
Improve drag performance for PageLayout

e2e/components/Axe.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const SKIPPED_TESTS = [
1414
'components-flash-features--with-icon-action-dismiss', // TODO: Remove once color-contrast issues have been resolved
1515
'components-flash-features--with-icon-and-action', // TODO: Remove once color-contrast issues have been resolved
1616
'components-filteredactionlist--default',
17+
'components-pagelayout-performance-tests--medium-content',
18+
'components-pagelayout-performance-tests--heavy-content',
1719
]
1820

1921
type Component = {

packages/react/src/PageLayout/PageLayout.module.css

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
/* Maintain resize cursor while dragging */
2-
/* stylelint-disable-next-line selector-no-qualifying-type */
3-
body[data-page-layout-dragging='true'] {
4-
cursor: col-resize;
5-
}
6-
7-
/* Disable text selection while dragging */
8-
/* stylelint-disable-next-line selector-no-qualifying-type */
9-
body[data-page-layout-dragging='true'] * {
10-
user-select: none;
11-
}
12-
131
.PageLayoutRoot {
142
/* Region Order */
153
--region-order-header: 0;
@@ -357,6 +345,12 @@ body[data-page-layout-dragging='true'] * {
357345
flex-grow: 1;
358346
flex-shrink: 1;
359347

348+
/**
349+
* OPTIMIZATION: Isolate content area from rest of page
350+
* Note: No 'paint' containment to allow overflow effects (tooltips, modals, etc.)
351+
*/
352+
contain: layout style;
353+
360354
&:where([data-is-hidden='true']) {
361355
display: none;
362356
}
@@ -383,6 +377,26 @@ body[data-page-layout-dragging='true'] * {
383377
}
384378
}
385379

380+
/**
381+
* OPTIMIZATION: Aggressive containment during drag for ContentWrapper
382+
* CSS handles most optimizations automatically via :has() selector
383+
* JavaScript only handles scroll locking (can't be done in CSS)
384+
*/
385+
.PageLayoutContent:has(.DraggableHandle[data-dragging='true']) .ContentWrapper {
386+
/* Add paint containment during drag - safe since user can't interact */
387+
contain: layout style paint;
388+
389+
/* Disable interactions */
390+
pointer-events: none;
391+
392+
/* Disable transitions to prevent expensive recalculations */
393+
transition: none;
394+
395+
/* Force compositor layer for hardware acceleration */
396+
will-change: width;
397+
transform: translateZ(0);
398+
}
399+
386400
.Content {
387401
width: 100%;
388402

@@ -392,6 +406,14 @@ body[data-page-layout-dragging='true'] * {
392406
margin-left: auto;
393407
flex-grow: 1;
394408

409+
/**
410+
* OPTIMIZATION: Skip rendering off-screen content during scrolling/resizing
411+
* This automatically helps consumers with large content by only rendering
412+
* elements that are visible in the viewport
413+
*/
414+
content-visibility: auto;
415+
contain-intrinsic-size: auto 500px;
416+
395417
&:where([data-width='medium']) {
396418
max-width: 768px;
397419
}
@@ -409,6 +431,16 @@ body[data-page-layout-dragging='true'] * {
409431
}
410432
}
411433

434+
/**
435+
* OPTIMIZATION: Freeze content layout during resize drag
436+
* This prevents expensive recalculations of large content areas
437+
* while keeping content visible (just frozen in place)
438+
*/
439+
.PageLayoutContent:has(.DraggableHandle[data-dragging='true']) .Content {
440+
/* Full containment (without size) - isolate from layout recalculations */
441+
contain: layout style paint;
442+
}
443+
412444
.PaneWrapper {
413445
display: flex;
414446
width: 100%;
@@ -585,6 +617,15 @@ body[data-page-layout-dragging='true'] * {
585617
/* stylelint-disable-next-line primer/spacing */
586618
padding: var(--spacing);
587619

620+
/**
621+
* OPTIMIZATION: Full containment for pane - isolates from rest of page
622+
*/
623+
contain: layout style paint;
624+
/**
625+
* OPTIMIZATION: For extremely tall content - skip rendering off-screen content
626+
*/
627+
content-visibility: auto;
628+
588629
@media screen and (min-width: 768px) {
589630
overflow: auto;
590631
}
@@ -598,6 +639,26 @@ body[data-page-layout-dragging='true'] * {
598639
}
599640
}
600641

642+
/**
643+
* OPTIMIZATION: Performance enhancements for Pane during drag
644+
* CSS handles all optimizations automatically - JavaScript only locks scroll
645+
*/
646+
.PaneWrapper:has(.DraggableHandle[data-dragging='true']) .Pane {
647+
/* Full containment - isolate from layout recalculations */
648+
contain: layout style paint;
649+
650+
/* Disable interactions during drag */
651+
pointer-events: none;
652+
653+
/* Disable transitions during drag */
654+
transition: none;
655+
656+
/* Force hardware acceleration */
657+
will-change: width, transform;
658+
transform: translateZ(0);
659+
backface-visibility: hidden;
660+
}
661+
601662
.PaneHorizontalDivider {
602663
&:where([data-position='start']) {
603664
/* stylelint-disable-next-line primer/spacing */
@@ -696,12 +757,22 @@ body[data-page-layout-dragging='true'] * {
696757
padding: var(--spacing);
697758
}
698759

760+
/**
761+
* DraggableHandle - Interactive resize handle
762+
*/
699763
.DraggableHandle {
700764
position: absolute;
701765
inset: 0 -2px;
702766
cursor: col-resize;
703767
background-color: transparent;
704768
transition-delay: 0.1s;
769+
770+
/**
771+
* OPTIMIZATION: Prevent touch scrolling and text selection during drag
772+
* This is done in CSS because it needs to be set before any pointer events
773+
*/
774+
touch-action: none;
775+
user-select: none;
705776
}
706777

707778
.DraggableHandle:hover {
@@ -710,6 +781,7 @@ body[data-page-layout-dragging='true'] * {
710781

711782
.DraggableHandle[data-dragging='true'] {
712783
background-color: var(--bgColor-accent-emphasis);
784+
cursor: col-resize;
713785
}
714786

715787
.DraggableHandle[data-dragging='true']:hover {

0 commit comments

Comments
 (0)