Skip to content

perf(TreeView): make rows paint-safe and reduce hover/focus invalidation - #7899

Merged
mattcosta7 merged 6 commits into
mainfrom
treeview-paint-safe-perf
Jun 1, 2026
Merged

perf(TreeView): make rows paint-safe and reduce hover/focus invalidation#7899
mattcosta7 merged 6 commits into
mainfrom
treeview-paint-safe-perf

Conversation

@mattcosta7

@mattcosta7mattcosta7 commented May 29, 2026

Copy link
Copy Markdown
Contributor

Closes #

Five targeted improvements to `TreeView` style + structure aimed at making rows cheaper to render at scale and removing a latent bug in the documented `containIntrinsicSize` API. No visual, layout, or public-API changes at the default rendering — all changes are either invisible at the default state or behind a CSS containment property the consumer opts into.

Changelog

Changed

  • Current-item indicator is now paint-safe via `overflow-clip-margin`. The indicator pseudo-element is positioned at `left: -8px` of `.TreeViewItemContainer`. That meant any consumer applying `contain: paint` to the row, or the documented `containIntrinsicSize` prop on `TreeView.Item` (which sets `content-visibility: auto` — implying `contain: paint` — on the same container), would clip the indicator on `current` items. Both `.TreeViewItem` and `.TreeViewItemContainer` now declare `overflow-clip-margin: var(--base-size-8)`. The property is a no-op when no paint containment is active, so default rendering is byte-identical.
  • Skeleton-row hover suppression no longer uses `:has()`. `:has(.TreeViewItemSkeleton):hover` matched on every row container in every tree, forcing subtree invalidation tracking on every row. `LoadingItem` now communicates with the placeholder `Item` via a module-private `LoadingPlaceholderContext` that emits a positive `data-loading` attribute on the `
  • `, and the CSS selector targets that directly. No new public prop.
  • Nesting indicator lines no longer rely on a root-scope `:hover`/`:focus-within` descendant selector. Color is driven by an inherited `--tree-line-color` custom property toggled on the root `
      `. Same UX (lines fade in while the tree is interacted with on hover-capable devices, always visible on coarse pointers); the engine updates one property on one element instead of re-matching the descendant selector against every `.TreeViewItemLevelLine` in the tree.
    • Fixed unitless `outline-offset: -2` in the forced-colors focus-ring fallback. Browsers were silently dropping the declaration, so forced-colors users were getting no focus indicator on tree items.
    • Made `grid-template-columns` declare the `trailingAction` track explicitly (`auto`) so it matches the 5-area `grid-template-areas` declaration on the same element. Clarity, no behavior change.

    New

    • Storybook story `TreeView/Features/CurrentItemWithContainIntrinsicSize` exercising the previously-broken `current` + `containIntrinsicSize` combination so VRT locks in the contract going forward.

    Removed

    Nothing public.

    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

    • All 94 `packages/react/src/TreeView/**` unit tests pass.
    • `tsc --noEmit` on `packages/react` is clean.

    VRT expectations: No change in default rendering. The one place a change could surface is forced-colors focus rings on tree items, which go from "nothing" (the silent `outline-offset: -2` drop) to a 2px inset `HighlightText` outline. That's an accessibility fix.

    Internal-only note: this lets the github-ui escape hatch in `packages/diff-file-tree/DiffFileTree.module.css` (`content-visibility: visible; contain: none;` for `[aria-current='true']` and `:focus-visible`) be removed once this lands.

    Merge checklist

@changeset-bot

changeset-botBot commented May 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bf5953f

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

This PR includes changesets to release 1 package
NameType
@primer/reactPatch

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-actionsgithub-actionsBot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label May 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Action required

👋 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. Check the integration testing docs for step-by-step instructions. Or, apply the integration-tests: skipped manually label to skip these checks.

To publish a canary release for integration testing, apply the Canary Release label to this PR.

@mattcosta7
mattcosta7force-pushed the treeview-paint-safe-perf branch from a12402c to ddb1910CompareMay 29, 2026 11:52
@github-actions
github-actionsBot requested a deployment to storybook-preview-7899 May 29, 2026 11:55 Abandoned
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7899 May 29, 2026 12:06 Inactive
…ver/focus invalidation
- Add 'overflow-clip-margin: var(--base-size-8)' to '.TreeViewItem' and
'.TreeViewItemContainer' so the current-item indicator (positioned at
'left: -8px' of the row container) remains visible when a consumer applies
'contain: paint' to the row, or when the documented 'containIntrinsicSize'
prop triggers 'content-visibility: auto' (which implies paint containment)
on the container. The property is a no-op when no paint containment is
active, so default rendering is unchanged. Safari 17 silently ignores the
property and keeps the pre-existing clipped-indicator behavior under
consumer-side 'contain: paint' (no regression from prior versions); Safari
18+ supports it, as does 'content-visibility: auto'.
- Replace ':has(.TreeViewItemSkeleton):hover' with a positive '[data-loading]'
marker emitted on the placeholder row's <li> by 'LoadingItem' via a
module-private 'LoadingPlaceholderContext'. No public-API change; avoids the
broad ':has()' invalidation cost across every row in large trees.
- Replace the root-scope ':hover'/':focus-within' descendant selectors on
level lines with an inherited '--tree-line-color' custom property toggled on
the root <ul>. Same UX; one property toggle on one element instead of
selector re-matching against every '.TreeViewItemLevelLine' descendant.
- Fix unitless 'outline-offset: -2' in the forced-colors focus-ring fallback
that browsers were silently dropping (so forced-colors users now actually
get a focus indicator on tree items).
- Make 'grid-template-columns' declare the 'trailingAction' track explicitly
('auto') so it matches the 5-area 'grid-template-areas' declaration.
@mattcosta7
mattcosta7force-pushed the treeview-paint-safe-perf branch from ddb1910 to 59c4714CompareMay 29, 2026 12:21
@primer

primerBot commented May 29, 2026

Copy link
Copy Markdown
Contributor

🤖 Lint issues have been automatically fixed and committed to this PR.

Removed comments regarding Safari 17 behavior and stylelint rules.
@mattcosta7mattcosta7 self-assigned this May 29, 2026
@mattcosta7
mattcosta7 marked this pull request as ready for review May 29, 2026 13:22
@mattcosta7
mattcosta7 requested a review from a team as a code ownerMay 29, 2026 13:22
@github-actions
github-actionsBot requested a deployment to storybook-preview-7899 May 29, 2026 13:26 Abandoned

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Targeted performance and correctness fixes to TreeView styling. Rows are made paint-safe so the current-item indicator survives contain: paint / content-visibility: auto, the skeleton hover-suppression :has() selector is replaced with a positively-marked data-loading attribute, and nesting-line hover toggling is moved from a descendant selector to a single inherited custom property. Also fixes a unitless outline-offset: -2 in the forced-colors focus fallback and makes the trailing-action grid column explicit.

Changes:

  • Add overflow-clip-margin: var(--base-size-8) on .TreeViewItem and .TreeViewItemContainer; fix unitless outline-offset; make grid-template-columns declare the trailingAction track explicitly.
  • Replace :has(.TreeViewItemSkeleton):hover with a module-private LoadingPlaceholderContext that sets data-loading on the <li>, and key the CSS off that attribute.
  • Drive nesting indicator color through an inherited --tree-line-color custom property toggled on the root <ul> instead of via :hover/:focus-within descendant selectors; add a VRT story for current + containIntrinsicSize.
Show a summary per file
FileDescription
packages/react/src/TreeView/TreeView.tsxAdds LoadingPlaceholderContext, consumes it in Item to emit data-loading, and wraps both LoadingItem return paths with the provider.
packages/react/src/TreeView/TreeView.module.cssAdds overflow-clip-margin, explicit auto trailing column, fixes outline-offset: -2px, replaces :has() skeleton selector with [data-loading], refactors level-line color to inherited --tree-line-color.
packages/react/src/TreeView/TreeView.features.stories.tsxAdds CurrentItemWithContainIntrinsicSize story for VRT coverage of the previously-clipped indicator.
.changeset/treeview-indicator-paint-safe.mdPatch changeset summarizing the five changes.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 0


&:where([data-omit-spacer='true']) .TreeViewItemContainer {
grid-template-columns: 0 0 0 1fr;
grid-template-columns: 0 0 0 1fr auto;

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

auto is the default, but it reads nicer imp when it's 5 columns defined for 5 columns

@liuliu-devliuliu-dev added the Canary Release Apply this label when you want CI to create a canary release of the current PR label May 29, 2026
@primer-integration

Copy link
Copy Markdown

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

@mattcosta7
mattcosta7 enabled auto-merge June 1, 2026 12:02
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7899 June 1, 2026 12:14 Inactive
@mattcosta7mattcosta7 added integration-tests: skipped manually Changes in this PR do not require an integration test and removed integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm labels Jun 1, 2026
@mattcosta7
mattcosta7 disabled auto-merge June 1, 2026 12:32
@mattcosta7mattcosta7 added integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm Canary Release Apply this label when you want CI to create a canary release of the current PR and removed integration-tests: skipped manually Changes in this PR do not require an integration test Canary Release Apply this label when you want CI to create a canary release of the current PR labels Jun 1, 2026
@github-actions
github-actionsBottemporarily deployed to storybook-preview-7899 June 1, 2026 18:53 Inactive
@mattcosta7mattcosta7 added Canary Release Apply this label when you want CI to create a canary release of the current PR and removed Canary Release Apply this label when you want CI to create a canary release of the current PR labels Jun 1, 2026
@mattcosta7
mattcosta7 enabled auto-merge June 1, 2026 21:12
@primer-integration

Copy link
Copy Markdown

Integration test results from github/github-ui PR:

Passed CI  Passed
Passed VRT  Passed
Passed Projects  Passed

All checks passed!

@mattcosta7
mattcosta7 added this pull request to the merge queueJun 1, 2026
Merged via the queue into main with commit 9659ce7Jun 1, 2026
61 checks passed
@mattcosta7
mattcosta7 deleted the treeview-paint-safe-perf branch June 1, 2026 21:34
@primerprimerBot mentioned this pull request Jun 1, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Canary ReleaseApply this label when you want CI to create a canary release of the current PRintegration-tests: recommendedThis 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.

3 participants

@mattcosta7@liuliu-dev