Dark-mode the document background, not just the rail, update text fonts in dark mode#6
Dark-mode the document background, not just the rail, update text fonts in dark mode#6AnnaXWang wants to merge 6 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The light/dark toggle themed the bar and rail but left the author's document (rendered in the sandboxed iframe) untouched, so picking dark left the doc area light — visibly inconsistent. Forward the toggle mode into the iframe via a new jh:themeMode message. The overlay (the only code that can touch the opaque-origin document) forces the doc's color-scheme and, with !important, its background/text so an explicit pick wins over an authored background; "auto" removes the override and restores the doc exactly as authored. The overlay re-samples after applying, so the chrome palette and dark-highlight treatment follow the document through the existing jh:theme round-trip. Per-element authored colors still cascade (we can't invert an arbitrary design) and @media(prefers-color-scheme) can't be driven from script; both are inherent and documented at the injection site. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
a0fbf0c to
0207d85
Compare
The forced-dark override set body text to #c9d1d9 (a light gray); use #ffffff so default document text is white on the dark canvas. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Setting body color white didn't work: authored element rules (p,li
{color:#1a1a1a}, th{color:#444}) beat inheritance, so most text stayed
dark. But blanket-whitening every element breaks anything with its own
light background — code chips, pill badges, callout boxes would render
white-on-light.
Walk the DOM instead: recolor the text of every element sitting on the
page background, and skip any element with its own background (or a code
block) plus its subtree — generalizing "leave code alone" to badges and
boxes too. A first pass pins each such surface's authored text color
inline (so a whitened ancestor can't leak white into a code chip that
inherits its color). Links keep their accent.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The rail/comment-card colors are derived from the doc's sampled fg. On a light doc forced dark, that fg is the authored dark text lifted only to AA — a gray, so comment text read gray, not white. When the viewer forces a theme, report the forced fg/bg from the overlay's sample so the chrome palette matches the forced document (white comment text in dark), and set the DEFAULT_DARK fallback fg to white for the pre-sample moment. Auto mode still samples the doc so the chrome adapts to genuinely-dark docs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two review findings: forcing dark left anchored-comment (highlight) text dark-on-dark. The segment spans carry a wash background in light mode, so the surface pass pinned their authored dark color; and whitenPage skipped them, so an authored span color rule kept them dark once jh-dark turned the wash into an underline-only treatment. Treat data-jh-seg spans as prose, not surfaces: don't pin them, and DO recolor them (color:#fff !important in dark, beating any span rule). A segment inside a code block still sits inside a surface and is never reached, so highlighted code text keeps its own color. Re-run the recolor at the end of paint() so segments (re)created on reload/resize — which carry no themeMode message — are recolored too. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Addressed both highlight-segment findings (commit 5b00998). Root cause: highlight wrapper spans ( Fix: treat Verified against a real doc: the anchored "What's missing for prod" heading now renders white with its amber underline instead of near-invisible dark-on-dark. |
Three review findings on the forced-theme flow: - Theme applied after paint / omitted on ready: jh:themeMode was sent only from a useEffect keyed on overlayReady/mode. On jh:ready the shell posted anchors+reactions (→ paint) before that effect ran, briefly showing the authored doc; and on an iframe reload overlayReady stays true so the effect never re-fired, leaving the fresh overlay on the authored theme. Now the jh:ready handler sends jh:themeMode FIRST, on every ready, via a modeRef so it reads the current mode. - Focus styles strip forced text class (High): applyFocusStyles rebuilt each segment with el.className = "d"+depth, dropping jh-doc-fg, so hover/focus reverted highlighted prose to dark-on-dark. It now preserves jh-doc-fg. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Addressed all three findings (commit cc87ac0).
Verified: after forcing dark and hovering an anchored segment, its class is |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit cc87ac0. Configure here.
| // (no authored-doc flash), and on EVERY ready — including an iframe reload, | ||
| // where overlayReady stays true so the mode effect below won't re-fire and | ||
| // the freshly-loaded overlay would otherwise reset to the authored theme. | ||
| postToOverlay({ type: "jh:themeMode", mode: modeRef.current }); |
There was a problem hiding this comment.
Stale mode on iframe ready
Medium Severity
On jh:ready, the shell sends jh:themeMode using modeRef, but modeRef only tracks React mode, which stays "auto" until a mount effect reads localStorage. If the iframe becomes ready before that re-render, the overlay gets mode: "auto", paints the authored document, and only later receives the saved dark/light preference—contradicting the no-flash intent for persisted theme choice.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit cc87ac0. Configure here.
| // Mirror mode into a ref so the jh:ready handler (not in mode's deps) reads the | ||
| // current value — it must send the forced theme on every ready, including reloads. | ||
| const modeRef = useRef<ThemeMode>(mode); | ||
| modeRef.current = mode; |
There was a problem hiding this comment.
Stale theme tints auto chrome
Medium Severity
After leaving forced dark or light, auto still drives the bar and rail from the last jh:theme sample until a new one arrives. That sample reflects the forced document colors, not the authored page, so chrome can stay dark on a light doc (or light on a dark doc) while the iframe already restored the authored look.
Reviewed by Cursor Bugbot for commit cc87ac0. Configure here.


Summary
The light/dark toggle themed the bar and rail but left the author's document (rendered in the sandboxed iframe) untouched, so picking dark left the doc area light — visibly inconsistent.
This forwards the toggle mode into the iframe via a new
jh:themeModemessage. The overlay (the only code that can touch the opaque-origin document) forces the document'scolor-scheme+ background:dark/lightforce the doc canvas + default text (!important, so an explicit pick wins over an authored background);autoremoves the override and restores the doc exactly as authored.jh:themeround-trip — bar, rail, doc, and highlights all end up consistent.Small, presentation-only change: +9 lines in the shell (one effect), +39 in the overlay (a
jh:themeModehandler that setscolor-schemeand an injected bg/fg style). No API/DB/anchoring changes.Inherent limits (documented at the injection site)
colordeclarations are left alone.@media (prefers-color-scheme)can't be driven from script, so a doc that themes itself only via that media query won't respond to the toggle.Verification
tscclean,vitest108/108,next buildclean.autorestores the authored look.Note: this PR was scoped down to just the dark-mode document change. The earlier single-scrollbar work (and its iframe-height-ratchet fix) is preserved on the
hypeship/unify-scroll-archivebranch, out of this PR.Note
Low Risk
Presentation-only postMessage and DOM styling in the sandboxed overlay; no API, auth, or persistence changes.
Overview
The light/dark/auto toggle now themes the document inside the sandboxed iframe, not only the comment rail and chrome.
CommentsShell sends
jh:themeModewhen the viewer changes mode and again on everyjh:ready(viamodeRef) so iframe reloads do not flash the authored theme.DEFAULT_DARKforeground is white for forced-dark fallback chrome.Overlay handles
jh:themeModeby forcingcolor-schemeand page background, walking the DOM to recolor prose on the page canvas while skipping surfaces (code blocks, elements with their own background) and links. Forced modes report fixed bg/fg injh:themeso rail chrome matches; highlight segments keep.jh-doc-fgthrough focus/hover and repaints callmarkForcedText()when needed.Reviewed by Cursor Bugbot for commit cc87ac0. Bugbot is set up for automated code reviews on this repo. Configure here.