Let CodeBlock resolve its theme from a scope - #120
Open
makew0rld wants to merge 2 commits into
Open
Conversation
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>
Shrinks99
requested changes
Jul 31, 2026
Shrinks99
left a comment
Member
There was a problem hiding this comment.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
text-shadow: 0 1px white, and a white halo on a dark surface renders every glyph doubled and close to unreadable.document.querySelector, which cannot see into a shadow tree.Resolve it as prop, then context, then the existing page-wide state:
Backwards compatible: with no prop and no scope, behaviour is unchanged, so existing consumers are unaffected.
ThemeProvider's newthemeprop 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 passingvar(--…)references to the highlighter would remove this branch, the flash before the theme resolves, and the side effect where reading the page-wide state writesdata-equality-themeontodocument.documentElement.PR text written by Claude Code, reviewed by me