Skip to content

Let CodeBlock resolve its theme from a scope - #120

Open
makew0rld wants to merge 2 commits into
mainfrom
feat/code-block-theme-scope
Open

Let CodeBlock resolve its theme from a scope#120
makew0rld wants to merge 2 commits into
mainfrom
feat/code-block-theme-scope

Conversation

@makew0rld

Copy link
Copy Markdown
Contributor

CodeBlock is the only component that branches on the theme in JavaScript and it reads the page-wide theme state to do it. That value is a single global resolved by walking the document, so it is wrong in two situations:

  • More than one theme on screen. The first root to mount decides, so a dark subtree renders the light palette. Concretely: the light palette is stock Prism, which sets text-shadow: 0 1px white, and a white halo on a dark surface renders every glyph doubled and close to unreadable.
  • Shadow DOM. The fallback resolves the theme with document.querySelector, which cannot see into a shadow tree.

Resolve it as prop, then context, then the existing page-wide state:

<CodeBlock theme="dark" … />                       // explicit
<ThemeProvider theme="dark">…</ThemeProvider>       // via ThemeProvider
<ThemeScopeProvider theme="dark">…</…>              // theming a subtree yourself

Backwards compatible: with no prop and no scope, behaviour is unchanged, so existing consumers are unaffected. ThemeProvider's new theme prop is optional and only supplies the scope — it does not change what it renders.

Longer term this component should not read the theme in JavaScript at all. Every other component themes through CSS custom properties on the nearest [data-equality-theme] ancestor, which is already correct per-instance, inside shadow roots, and on first paint. Expressing the two syntax palettes as tokens and passing var(--…) references to the highlighter would remove this branch, the flash before the theme resolves, and the side effect where reading the page-wide state writes data-equality-theme onto document.documentElement.

PR text written by Claude Code, reviewed by me

makew0rld and others added 2 commits July 30, 2026 15:10
CodeBlock is the only component that branches on the theme in JavaScript — it
picks a syntax palette — and it reads the page-wide theme state to do it. That
value is a single global resolved by walking the document, so it is wrong in
two situations:

- More than one theme on screen. The first root to mount decides, so a dark
  subtree renders the light palette. Concretely: the light palette is stock
  Prism, which sets `text-shadow: 0 1px white`, and a white halo on a dark
  surface renders every glyph doubled and close to unreadable.
- Shadow DOM. The fallback resolves the theme with `document.querySelector`,
  which cannot see into a shadow tree.

Resolve it as prop, then context, then the existing page-wide state:

    <CodeBlock theme="dark" … />                       // explicit
    <ThemeProvider theme="dark">…</ThemeProvider>       // via ThemeProvider
    <ThemeScopeProvider theme="dark">…</…>              // theming a subtree yourself

Backwards compatible: with no prop and no scope, behaviour is unchanged, so
existing consumers are unaffected. `ThemeProvider`'s new `theme` prop is
optional and only supplies the scope — it does not change what it renders.

Longer term this component should not read the theme in JavaScript at all.
Every other component themes through CSS custom properties on the nearest
`[data-equality-theme]` ancestor, which is already correct per-instance, inside
shadow roots, and on first paint. Expressing the two syntax palettes as tokens
and passing `var(--…)` references to the highlighter would remove this branch,
the flash before the theme resolves, and the side effect where reading the
page-wide state writes `data-equality-theme` onto `document.documentElement`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mirrors the "Portalled surfaces" section added for the portal container: the
two failure cases are the same, so they read as a pair.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@makew0rld
makew0rld requested a review from Shrinks99 July 30, 2026 19:29

@Shrinks99 Shrinks99 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No other elements other than themeprovider have the option to set dark and light on them. I really don't want to set this precedent, all elements should inherit the dark and light mode from the theme provider's setting.

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