Skip to content

fix(webapp): share page dark mode no longer shows white slabs (BEA-71) - #122

Merged
ssowonny merged 1 commit into
mainfrom
bea-71-ph-scan-bug-public-share-page-renders-light-while-the-hub
Aug 5, 2026
Merged

fix(webapp): share page dark mode no longer shows white slabs (BEA-71)#122
ssowonny merged 1 commit into
mainfrom
bea-71-ph-scan-bug-public-share-page-renders-light-while-the-hub

Conversation

@ssowonny

@ssowonny ssowonny commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

TL;DR

  • A shared file opened on a dark-mode machine showed a white frontmatter slab, a white code block and light-grey table borders on a near-black page — the first thing a stranger sees, looking like a different product.
  • The page already followed the system. Its dark @media block just sat above the light rules at equal specificity, so every light rule after it won.
  • Fix is source order: move the dark block to the end of the stylesheet and finish it there. That repairs pre/code for free.
  • Dark values now come from the hub's tw.css tokens instead of hand-picked greys (#c6cbd3, #3a3a44, #888) — that ad-hoc palette was the other half of the complaint.
  • Known gap, already agreed (Snow, 2026-08-03): a light-system visitor still gets a light share page next to a permanently dark hub. That ends when the hub gets a light theme, deferred to its own issue.

What was actually broken

The spec listed four unthemed rules. It was six — pre and code had a dark override that never took effect, because it was declared before the light rules it was meant to beat.

Rule Dark override before After
body bg + text yes, but #c6cbd3 text #0a0b0d / #eef0f3
a yes unchanged (#ffcf85)
pre, code background declared, but lost the cascade #15171b
blockquote border + text none rgba(255,255,255,.07) / #9aa0a9
td, th borders none rgba(255,255,255,.07)
table.frontmatter background none — a white slab #15171b
table.frontmatter borders + label none rgba(255,255,255,.07) / #868b93
footer.bdrive, .updated yes, but #3a3a44 / #888 real tokens

Values are the @theme tokens in internal/webapp/frontend/src/tw.css, not picked by eye, which is what makes the two surfaces agree.

The overrides use border-left-color / border-color / border-bottom-color rather than the border shorthand, so the light rules keep owning width and style and only the colour is themed.

Dark mode, before and after

Before After
desktop dark before desktop dark after
mobile dark before mobile dark after

Same document both sides: frontmatter table, blockquote, table, inline code, code block.

The one thing to actually check

That the diff contains no light-mode literal and no response header. Light mode has to be untouched, and /s/* keeps its sandbox CSP / nosniff / referrer policy (CLAUDE.md invariant — those live on the handler, not in this const, and nothing here goes near them).

Evidence rather than assertion:

  • Light-mode screenshots rendered from the pre- and post-fix pages are byte-identical PNGs (same SHA-256). Computed styles for body, pre, code, blockquote, td, table.frontmatter, footer match exactly on both sides.
  • TestShareLastUpdatedStamp already asserts the three headers, and still passes.

Test

TestShareDarkThemeIsLast asserts placement, not presence — deliberately. "A dark rule exists" passed while the page was still wrong, so the test checks the dark block comes after the last light literal (#f6f8fa, #d0d7de, #d8dee4, #6e7781, #57606a), covers each previously-light selector, and that no ad-hoc grey survived. It renders through the real handler, so the fmt.Sprintf format string still has to work — a % that wasn't doubled would surface as %!, which the test also catches.

Confirmed regression-shaped: reverted shares.go and re-ran it — 6 failures.

What was run

Check Result
go build ./... && go vet ./... clean
go test ./... all packages pass
npm run e2e 150 passed, 1 skipped
UI eval, desktop + mobile, dark + light real rendered page, screenshots above
Contrast on #0a0b0d body #eef0f3 17.25:1, dim 7.48:1, faint 5.75:1, link 13.61:1 — all past 4.5:1

No syncer test: this touches no sync path. No frontend build: the CSS is a Go const, internal/webapp/static is untouched.

Architecture

No diagram change — no types, seams or relationships moved. One CSS const and one test.

Deviation from the plan

None. The plan's own correction to the spec (six rules, not four; move the block rather than patch it in place) held up against the code.

Build session

cd $(git worktree list | grep bea-71-ph-scan-bug | awk '{print $1}') && claude --resume ad9bedc5-d0a5-495c-b672-c6d3b9313f24

Only works on the machine that ran the build.

The share page already followed the system, but its dark @media block sat
ABOVE the light pre/code/blockquote/table rules at equal specificity, so
every light rule after it won. A dark-system visitor got a white frontmatter
slab, a white code block and light-grey table borders on a near-black page —
the surface strangers see first reading as a different product.

Source order is the whole fix: the block moves to the end of the stylesheet
and gets completed there, which repairs pre/code for free. Values are the
hub's @theme tokens from frontend/src/tw.css instead of the hand-picked greys
(#c6cbd3 body text, #3a3a44/#888 footer) that were the "different product"
half of the complaint.

Light mode is pixel-identical — only the dark block moved. Response headers
(sandbox CSP, nosniff, referrer) are untouched.

The test asserts placement, not presence: "a dark rule exists" passed while
the page was still wrong, so it checks the dark block comes after the last
light literal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ssowonny
ssowonny requested a review from thefron August 5, 2026 05:13
@ssowonny
ssowonny merged commit 5d399c3 into main Aug 5, 2026
2 checks passed
@ssowonny
ssowonny deleted the bea-71-ph-scan-bug-public-share-page-renders-light-while-the-hub branch August 5, 2026 08:06
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.

1 participant