Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 25, 2025

Adds data-primer-css-perf-has-selector feature flag to opt-in to skipping expensive :has([data-color-mode]) selectors that scan the entire DOM on every style recalculation.

Changes

Modified BaseStyles.module.css to wrap expensive selectors with :not([data-primer-css-perf-has-selector] *):

/* Before: Always active */
&:has([data-color-mode='light']) {
  input & {
    color-scheme: light;
  }
}

/* After: Only active when flag NOT present */
&:not([data-primer-css-perf-has-selector] *):has([data-color-mode='light']) {
  input & {
    color-scheme: light;
  }
}

Default behavior: Expensive selectors remain active (unchanged)
Opt-in behavior: Add data-primer-css-perf-has-selector to <html> or <body> to skip expensive selectors

Input color-scheme is already handled by global selectors at lines 20-26, making these :has() checks redundant for consumers who opt-in.

Changelog

Changed

  • Modified .BaseStyles selector to support performance feature flag via negative data attribute check
  • Added documentation comments explaining the performance optimization and feature flag behavior

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

Consumers can test the optimization in staging/canary by adding the data attribute before production rollout. Once validated broadly, the feature flag can be removed in a future release.

Testing & Reviewing

Existing BaseStyles tests validate backward compatibility. To test the optimization:

  1. Add data-primer-css-perf-has-selector to <html> or <body>
  2. Verify input color-scheme still works correctly (handled by global selectors)
  3. Use Chrome DevTools Performance tab to validate :has() selectors are no longer recalculating styles

Merge checklist

Original prompt

Summary

Create a feature-flagged version of the performance optimization from #7325. Instead of removing the expensive :has([data-color-mode]) selectors outright, use a negative data attribute check to allow consumers to opt-in to the optimization.

This allows us to ship the change safely by giving consumers control over when to enable the optimization.

Implementation

In packages/react/src/BaseStyles.module.css, modify the .BaseStyles class to use a negative data attribute selector as a feature flag:

.BaseStyles {
  font-family: var(--BaseStyles-fontFamily, var(--fontStack-system));
  /* stylelint-disable-next-line primer/typography */
  line-height: var(--BaseStyles-lineHeight, 1.5);
  /* stylelint-disable-next-line primer/colors */
  color: var(--BaseStyles-fgColor, var(--fgColor-default));

  /*
   * PERFORMANCE: The :has([data-color-mode]) selectors below are expensive
   * as they scan the entire DOM on every style recalculation.
   * Input color-scheme is already handled by global selectors above:
   *   [data-color-mode='light'] input { color-scheme: light; }
   *   [data-color-mode='dark'] input { color-scheme: dark; }
   *
   * Feature flag: When [data-primer-css-perf-has-selector] is NOT present on
   * an ancestor element, the old (expensive) behavior is preserved.
   * Add [data-primer-css-perf-has-selector] to an ancestor to opt-in to the
   * optimized behavior (which skips these expensive selectors).
   *
   * See #7325 and #7312 for context on this performance optimization.
   */

  /* Global styles for light mode - only apply when feature flag is NOT present */
  &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='light']) {
    input & {
      color-scheme: light;
    }
  }

  /* Global styles for dark mode - only apply when feature flag is NOT present */
  &:not([data-primer-css-perf-has-selector] *):has([data-color-mode='dark']) {
    input & {
      color-scheme: dark;
    }
  }

  /* Low-specificity default link styling */
  :where(a:not([class*='prc-']):not([class*='PRC-']):not([class*='Primer_Brand__'])) {
    color: var(--fgColor-accent, var(--color-accent-fg));
    text-decoration: none;

    &:hover {
      text-decoration: underline;
    }
  }
}

How it works

  • Default (no attribute): The expensive :has([data-color-mode]) selectors remain active - preserving existing behavior.
  • Opt-in (attribute present): When data-primer-css-perf-has-selector is added to any ancestor element (e.g., <html data-primer-css-perf-has-selector> or <body data-primer-css-perf-has-selector>), the expensive selectors are bypassed.

Changeset

Add a changeset file .changeset/perf-basestyles-has-selector-feature-flag.md:

---
'@primer/react': patch
---

perf(BaseStyles): Feature-flag expensive :has([data-color-mode]) selectors

Add a feature flag (`data-primer-css-perf-has-selector` data attribute) to opt-in to skipping expensive `:has([data-color-mode])` selectors that scan the entire DOM on every style recalculation.

To enable the optimization, add `data-primer-css-perf-has-selector` to an ancestor element (e.g., `<html>` or `<body>`). Input color-scheme is already handled by global selectors in the codebase.

See #7325 and #7312 for context on this performance optimization.

Why this approach?

  1. Safe rollout: Consumers can test the optimization in staging/canary environments before enabling in production.
  2. Backward compatible: Default behavior is unchanged.
  3. Easy to enable: Just add a single data attribute to enable the optimization.
  4. Easy to remove: Once validated, we can remove the feature flag and the expensive selectors entirely (like in perf(BaseStyles): Remove expensive :has([data-color-mode]) selectors #7325).

Related

This pull request was created from Copilot chat.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@changeset-bot
Copy link

changeset-bot bot commented Dec 25, 2025

🦋 Changeset detected

Latest commit: 746b5ed

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

- Modified BaseStyles.module.css to use negative data attribute selector
- Added comprehensive comments explaining the feature flag behavior
- Created changeset for the patch release
- All tests passing, linting and type checking successful

Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com>
Copilot AI changed the title [WIP] Add feature-flagged version of performance optimization perf(BaseStyles): Add feature flag for :has([data-color-mode]) selector optimization Dec 25, 2025
Copilot AI requested a review from mattcosta7 December 25, 2025 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants