Skip to content

Conversation

@jonrohan
Copy link
Member

@jonrohan jonrohan commented Jan 22, 2026

Part of https://github.com/github/pull-requests/issues/22213

Summary

Optimizes CSS selector performance in ActionList by replacing expensive universal selectors (*) and broad :has() queries with targeted class selectors. :is(.ActionListItem:not(:has([aria-disabled], [disabled]), [data-has-subitem="true"]):where([data-loading="true"]), .ActionListItem:not(:has([aria-disabled], [disabled]), [data-has-subitem="true"]):has([data-loading="true"])) *

Problem

During LCP (Largest Contentful Paint) analysis, we identified a selector in ActionList that was attempting to match ~200k elements but matching none:

The issues were:

  1. Universal selector (& *) - Forces the browser to check every descendant element
  2. Broad :has([data-loading='true']) - Scans all descendants looking for the data-loading attribute

Solution

  1. Replaced & * with explicit class targets:

    • .ItemLabel
    • .Description
    • .LeadingVisual
    • .TrailingVisual
    • .VisualWrap
  2. Removed :has([data-loading='true']) entirely - After investigation, data-loading is set directly on the <li> element via Item.tsx, so the :has() variant was unnecessary. The separate rule at line 369 already handles TrailingAction-specific loading states.

Related Issue(s)

Before/After

Aspect Before After
Loading state selector &:where([data-loading='true']), &:has([data-loading='true']) { & * { ... } } &:where([data-loading='true']), & [data-loading='true'] { & .ItemLabel, & .Description, ... { ... } }
Elements checked ~200k (all descendants) Only elements with specific classes
:has() scope All descendants Direct child with data-loading attribute

Testing & Verification

  • Build passes (npm run build)
  • Unit tests pass (npm test - 505 tests)
  • CSS linting passes (npx stylelint)
  • Verified loading state styling in Storybook
  • Tested with ActionList items using loading prop
  • Tested with TrailingAction containing loading buttons

Merge checklist

  • Added/updated tests
  • Added/updated documentation
  • Tested in all supported browsers
  • Reviewed for accessibility impact (no changes to accessibility behavior)

Screenshots

N/A - This is a performance optimization with no visual changes.

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

🦋 Changeset detected

Latest commit: 79b2187

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 staff Author is a staff member label Jan 22, 2026
@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.

@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 Jan 22, 2026
@github-actions github-actions bot temporarily deployed to storybook-preview-7468 January 22, 2026 22:57 Inactive
}

&:where([data-loading='true']),
&:has([data-loading='true']) {
Copy link
Contributor

Choose a reason for hiding this comment

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

is the [data-loading] really at any level of depth, or is it possibly on the ListItem wrappers directly? that would probably be another nice win

Copy link
Member Author

Choose a reason for hiding this comment

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

Theres is a .TrailingAction data-loading that can happen, but we can make the has more specific here to avoid scanning all the descendants.

@github-actions github-actions bot requested a deployment to storybook-preview-7468 January 23, 2026 18:22 Abandoned
@github-actions github-actions bot temporarily deployed to storybook-preview-7468 January 23, 2026 18:34 Inactive
Optimizes CSS selector performance in ActionList by replacing expensive universal selectors (`*`) and broad `:has()` queries with targeted class selectors.
@jonrohan jonrohan marked this pull request as ready for review January 23, 2026 21:03
@jonrohan jonrohan requested a review from a team as a code owner January 23, 2026 21:03
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

Improves ActionList loading-state CSS performance by replacing expensive universal/:has() selectors with more targeted class-based selectors.

Changes:

  • Replaced & * loading-state styling with a curated set of ActionList child classes.
  • Removed the broad :has([data-loading='true']) loading selector path.
  • Added a changeset documenting the performance optimization.

Reviewed changes

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

File Description
packages/react/src/ActionList/ActionList.module.css Updates loading-state selectors to reduce matching work and avoid broad descendant scans.
.changeset/orange-walls-buy.md Publishes the change as a patch with a note about selector performance optimization.

Comment on lines 279 to +280
&:where([data-loading='true']),
&:has([data-loading='true']) {
& * {
& > [data-loading='true'] {
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

& > [data-loading='true'] likely never matches with the current ActionList.Item markup. data-loading is set on the <li> itself (Item.tsx:210), and when TrailingAction is loading the attribute is on the nested button inside .TrailingAction, not on a direct child of the <li>. Consider removing this selector (keeping only &:where([data-loading='true'])) to avoid dead/incorrect CSS, or update it to match an actual DOM structure that needs support.

See below for a potential fix:

    &:where([data-loading='true']) {

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@primer-integration
Copy link

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

@primer-integration
Copy link

Integration test results from github/github-ui:

Passed  CI   Passed
Passed  VRT   Passed
Passed  Projects   Passed

All checks passed!

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 staff Author is a staff member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants