nextjs - ✅(Solved) Fix Activity breaks CSS `:has()` rules [1 pull requests, 8 comments, 5 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#91359Fetched 2026-04-08 02:02:14
View on GitHub
Comments
8
Participants
5
Timeline
17
Reactions
0
Timeline (top)
commented ×7mentioned ×4subscribed ×4cross-referenced ×1

Root Cause

This happens because on navigation Activity hides the previous page content with display: none but :has() rule still targets it because it does not care about display: none style. I know of one workaround where the CSS rule is configured to skip elements with display: none but that seems hacky to me and may break in the future.

Fix Action

Fix / Workaround

This happens because on navigation Activity hides the previous page content with display: none but :has() rule still targets it because it does not care about display: none style. I know of one workaround where the CSS rule is configured to skip elements with display: none but that seems hacky to me and may break in the future.

Do you have any other recommendations or the current workaround is actually good enough?

PR fix notes

PR #91365: fix: add hidden attribute to Activity wrapper for :has() CSS compatibility

Description (problem / solution / changelog)

fix : #91359

Summary

  • Add hidden attribute to the Activity wrapper when the segment is inactive so :has() can ignore hidden bfcache content.

Changes

  • Wrap Activity in a div with hidden when mode !== 'visible'
  • Add e2e test for :has() behavior with cacheComponents

Why

  • Activity hides inactive bfcache content with display: none, but :has() still matches it. The hidden attribute lets users exclude hidden content via :not(:has([hidden] .selector)).

Usage

  • body:has([data-page='a']):not(:has([hidden] [data-page='a'])) .status-banner { ... }

Changed files

  • packages/next/src/client/components/layout-router.tsx (modified, +9/-7)
  • test/e2e/app-dir/activity-has-css/activity-has-css.test.ts (added, +33/-0)
  • test/e2e/app-dir/activity-has-css/app/globals.css (added, +10/-0)
  • test/e2e/app-dir/activity-has-css/app/layout.tsx (added, +25/-0)
  • test/e2e/app-dir/activity-has-css/app/page-a/page.tsx (added, +7/-0)
  • test/e2e/app-dir/activity-has-css/app/page-b/page.tsx (added, +7/-0)
  • test/e2e/app-dir/activity-has-css/app/page.tsx (added, +17/-0)
  • test/e2e/app-dir/activity-has-css/next.config.js (added, +6/-0)

Code Example

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:53:27 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6041
  Available memory (MB): 24576
  Available CPU cores: 12
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: 1.22.22
  pnpm: 9.15.4
Relevant Packages:
  next: 16.1.6 // Latest available version is detected (16.1.6).
  eslint-config-next: N/A
  react: 19.2.3
  react-dom: 19.2.3
  typescript: 5.9.3
Next.js Config:
  output: N/A
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/binarystride/next-activity-has-repro

To Reproduce

  1. Enabled Cache Components
  2. Add a :has() CSS rule to your layout that targets page A content
  3. Go to page A and then navigate to page B

Current vs. Expected behavior

Expected: page B does not apply :has() rule

Current: page B applies :has() rule

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 24.5.0: Tue Apr 22 19:53:27 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6041
  Available memory (MB): 24576
  Available CPU cores: 12
Binaries:
  Node: 22.21.1
  npm: 10.9.4
  Yarn: 1.22.22
  pnpm: 9.15.4
Relevant Packages:
  next: 16.1.6 // Latest available version is detected (16.1.6).
  eslint-config-next: N/A
  react: 19.2.3
  react-dom: 19.2.3
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

cacheComponents

Which stage(s) are affected? (Select all that apply)

next dev (local), next start (local), Vercel (Deployed), Other (Deployed)

Additional context

This happens because on navigation Activity hides the previous page content with display: none but :has() rule still targets it because it does not care about display: none style. I know of one workaround where the CSS rule is configured to skip elements with display: none but that seems hacky to me and may break in the future.

If Next.js could add a stable attribute like hidden or something similar to the hidden element, the :has() rule would be easier to write and less likely to break.

Do you have any other recommendations or the current workaround is actually good enough?

To be honest this is not exactly a bug, so feel free to move to discussion if that's more appropriate.

extent analysis

TL;DR

Add a stable attribute like hidden to the hidden element to make the :has() rule easier to write and less likely to break.

Guidance

  • Consider adding a CSS rule that targets elements with display: none to exclude them from the :has() rule, despite the potential for future breakage.
  • Next.js could potentially add a stable attribute like hidden to the hidden element, making it easier to write reliable :has() rules.
  • Verify that any workaround or solution does not introduce unintended side effects or break existing functionality.
  • Evaluate the trade-offs between using a potentially fragile CSS workaround versus advocating for a change in Next.js to support a more robust solution.

Example

No specific code example is provided, as the issue revolves around the interaction between CSS rules and Next.js behavior, rather than a specific code snippet that can be modified.

Notes

The issue highlights a limitation in how :has() rules interact with elements hidden using display: none, which is not a bug per se but rather a consequence of how these technologies work together. Any solution will need to balance between workarounds that might have future compatibility issues and advocating for changes in the underlying frameworks.

Recommendation

Apply workaround: Adding a stable attribute like hidden is not currently an option without changes to Next.js, so using a CSS rule that skips elements with display: none might be the most immediate, albeit imperfect, solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING