From 5d40a53cec1b5816f130650edcb5d6fde94399c9 Mon Sep 17 00:00:00 2001 From: Matthew Costabile Date: Mon, 15 Dec 2025 14:52:16 +0000 Subject: [PATCH] perf(Breadcrumbs): Optimize CSS :has() selector and batch offsetWidth reads - Scope :has(.MenuOverlay) to shallow path > .MenuDetails .MenuOverlay for O(1) lookup - Batch all offsetWidth reads in a single pass to avoid layout thrashing Part of #7312 --- .changeset/perf-breadcrumbs-cls-fix.md | 8 ++++++++ packages/react/src/Breadcrumbs/Breadcrumbs.module.css | 3 ++- packages/react/src/Breadcrumbs/Breadcrumbs.tsx | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .changeset/perf-breadcrumbs-cls-fix.md diff --git a/.changeset/perf-breadcrumbs-cls-fix.md b/.changeset/perf-breadcrumbs-cls-fix.md new file mode 100644 index 00000000000..2b39c31e3d6 --- /dev/null +++ b/.changeset/perf-breadcrumbs-cls-fix.md @@ -0,0 +1,8 @@ +--- +'@primer/react': patch +--- + +perf(Breadcrumbs): Optimize CSS :has() selector and batch offsetWidth reads + +- Scope `:has(.MenuOverlay)` to shallow path `> .MenuDetails .MenuOverlay` for O(1) lookup +- Batch all offsetWidth reads in a single pass to avoid layout thrashing diff --git a/packages/react/src/Breadcrumbs/Breadcrumbs.module.css b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css index e699fb42587..11f1633894e 100644 --- a/packages/react/src/Breadcrumbs/Breadcrumbs.module.css +++ b/packages/react/src/Breadcrumbs/Breadcrumbs.module.css @@ -117,7 +117,8 @@ list-style: none; /* allow menu items to wrap line */ - &:has(.MenuOverlay) { + /* PERFORMANCE: MenuOverlay is inside MenuDetails > summary sibling, use shallow path */ + &:has(> .MenuDetails .MenuOverlay) { white-space: normal; } diff --git a/packages/react/src/Breadcrumbs/Breadcrumbs.tsx b/packages/react/src/Breadcrumbs/Breadcrumbs.tsx index 5a59efe2f4c..e5353a285a1 100644 --- a/packages/react/src/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/react/src/Breadcrumbs/Breadcrumbs.tsx @@ -188,9 +188,11 @@ function Breadcrumbs({className, children, style, overflow = 'wrap', variant = ' listElement.children.length === childArray.length ) { const listElementArray = Array.from(listElement.children) as HTMLElement[] + // Batch all offsetWidth reads in a single pass to avoid layout thrashing const widths = listElementArray.map(child => child.offsetWidth) setChildArrayWidths(widths) - setRootItemWidth(listElementArray[0].offsetWidth) + // Use first width from the array instead of reading offsetWidth again + setRootItemWidth(widths[0]) } }, [childArray, overflowMenuEnabled])