fix: honor system theme on homepage first paint - #30
Draft
wustep wants to merge 2 commits into
Draft
Conversation
The about page hardcoded dark on SSR and only flipped after hydration, so light-mode visitors (and the noflash body class used everywhere else) got a dark flash. Drive tokens from body.dark-mode and use the shared useDarkMode hook so the toggle stays in sync with journal pages. Co-authored-by: Stephen Wu <wustep@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
The homepage (~60% of weekly visitors) keeps its own
isDarkstate, defaults it totrue, and writes that tolocalStorageon mount. Light-mode visitors therefore:<main class="…dark">in the SSR HTML)trueJournal / philosophy pages already use
useDarkMode+ the inline noflash script onbody.dark-mode. The homepage ignored both.What changed
body.dark-mode(set by the existing noflash script before first paint)useDarkModehook for the theme toggle so homepage and Notion pages stay in sync--about-lenses-*,.iconOnDark/.iconOnLight) so they don’t wait on React stateNo brand redesign — same light and dark palettes, just applied at the right time.
Why
Light-preferring visitors (and anyone with
darkMode=falsesaved) were paying a dark flash on the most-viewed page. Toggling theme on the homepage also failed to updatebody.dark-mode, so a client navigation to a journal page could disagree with the homepage.Before / after
Before (production first paint): SSR hardcodes the dark class. Mid-load the dark page is fading in over a light body, which reads as washed-out low-contrast chrome:
Production homepage first paint — washed out dark-on-light flash
Before (production after hydration, light preference): the page does land on light — the bug is the flash getting there, plus the independent theme state.
Production homepage after JS hydration in light mode
After (this branch, light preference — first paint is already light):
Local homepage light mode desktop
Local homepage light mode mobile
After (this branch, dark preference — first paint is already dark):
Local homepage dark mode desktop
Local homepage dark mode mobile
The circular “N” in the corner of the local shots is the Next.js 16 dev indicator, not site chrome.
Test plan
prefers-color-scheme: light: homepage is light on first paint (no dark flash)prefers-color-scheme: dark: homepage is dark on first paint (no light flash)/, then open/philosophy: both pages match/philosophy, then go home: homepage matches/after choosing light: stays light (localStorage not overwritten totrue)prefers-reduced-motion: no entrance animation; theme still correctTo show artifacts inline, enable in settings.