Skip to content

fix(session-ui): compute highlight styles once per subtree in Chromium - #60513

Merged
Hona merged 1 commit into
devfrom
highlight-inheritance
Oct 30, 2026
Merged

Hona merged 1 commit into
devfrom
highlight-inheritance

Conversation

@Hona

@Hona Hona commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Chromium computes a highlight style (::selection, ::highlight(...)) during style recalc for every element a highlight rule matches. Our universal and descendant highlight rules matched every timeline element, so each restyled element paid for up to four extra pseudo-element styles. Chromium 134+ inherits highlight styles, so each rule now matches one ancestor (:root, the timeline scroller, the diff host), and descendants inherit the result.

flowchart LR
  subgraph Before
    R1[":root"] --- A1["row ✱"] --- B1["p ✱"] --- C1["span ✱"]
  end
  subgraph After
    R2[":root ✱"] -.inherits.-> A2["row"] -.-> B2["p"] -.-> C2["span"]
  end
Loading

✱ = element that computes its own highlight styles

  • Blink: @supports selector(:host-context(*)) selects the inherited rules. :host-context() is Blink-only, and Gecko and WebKit have declined to implement it.
  • Gecko and WebKit: These engines do not inherit highlight styles, so they keep the original descendant rules in the @supports not branch.
  • Fallback: If Blink ever drops :host-context(), it falls back to today's rules: still correct, just slower again.
  • Rules changed:
    • timeline quote and selection colors (quote-highlight.css)
    • tool error card colors (tool-error-card.css)
    • timeline search hits (app/src/index.css)
    • diff ::selection, find, and quote highlights inside Pierre's shadow root (pierre/index.ts)
@supports selector(:host-context(*)) {
  [data-slot="session-timeline-scroll"]::selection,
  :root::highlight(timeline-quote-source) { … }
}

@supports not selector(:host-context(*)) {
  [data-slot="session-timeline-scroll"] ::selection,
  ::highlight(timeline-quote-source) { … }
}

Benchmark

Production build, 320-turn session, Chromium 148, no CPU throttle. Each figure is the median of 3 interleaved runs. "Frame busy" is main-thread time per 16.7 ms frame, taken from a Chrome trace.

Wheel-scroll 600 steps through history dev This PR
Style recalc 2,002 ms 1,087 ms (−46%)
Main-thread busy 5,028 ms 3,962 ms (−21%)
Frames busy > 5 ms 305 195
Frames busy > 8 ms 138 74
Frame busy p90 8.45 ms 6.40 ms
Longest task 32.9 ms 23.7 ms
Stream 600 deltas at 60 per second dev This PR
Style recalc 517 ms 405 ms (−22%)
Frames busy > 8 ms 11 9

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none">
  <style>
    .n{fill:currentColor;opacity:.18}
    .hot{fill:currentColor;opacity:.75}
    .l{stroke:currentColor;opacity:.3;stroke-width:1.5}
    .flow{stroke:currentColor;stroke-width:2;stroke-dasharray:4 4;opacity:.6;animation:f 1.6s linear infinite}
    @keyframes f{to{stroke-dashoffset:-16}}
    @media (prefers-reduced-motion:reduce){.flow{animation:none}}
  </style>
  <rect class="hot" x="12" y="28" width="28" height="16" rx="3"/>
  <path class="flow" d="M40 36H70M98 36H128M156 36H186"/>
  <rect class="n" x="70" y="28" width="28" height="16" rx="3"/>
  <rect class="n" x="128" y="28" width="28" height="16" rx="3"/>
  <rect class="n" x="186" y="28" width="28" height="16" rx="3"/>
  <path class="l" d="M84 44V58M142 44V58M200 44V58"/>
  <rect class="n" x="76" y="58" width="16" height="8" rx="2"/>
  <rect class="n" x="134" y="58" width="16" height="8" rx="2"/>
  <rect class="n" x="192" y="58" width="16" height="8" rx="2"/>
</svg>

Added by the opencode bot (AI-generated).

Related: #54291

Copilot AI balanced review requested due to automatic review settings October 30, 2026 06:53
@Hona
Hona requested a review from Brendonovich as a code owner October 30, 2026 06:53

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks sound. In Chromium the inherited rules compute the same colours as the old per-element rules. I checked nested elements in the timeline, tokens in Pierre's shadow root (selection and find), and the tool error card. currentColor still keeps each element's own text colour. The quote, highlight and diff regression tests in timeline-stream.spec.ts pass. I didn't re-measure the benchmark, and I didn't run the Gecko/WebKit @supports not branch in a browser.

The public API is unchanged (CSS only).

No blocking concerns.

Related: #54291

@Hona
Hona merged commit 66c278b into dev Oct 30, 2026
23 checks passed
@Hona
Hona deleted the highlight-inheritance branch October 30, 2026 06:59
@amrkmn amrkmn mentioned this pull request Oct 30, 2026
1 task
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