Skip to content

Read the settings before the first render, so a custom theme is painted from the app's first render - #651

Merged
zaerl merged 1 commit into
trunkfrom
feat/theme-before-first-render
Oct 7, 2026
Merged

zaerl merged 1 commit into
trunkfrom
feat/theme-before-first-render

Conversation

@zaerl

@zaerl zaerl commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Why

The follow-up recorded in #650: a custom theme was painted after a first render in the standard theme of its scheme, a flash of the wrong colours at every launch, because the settings, of which the theme is one, were read in an effect once the app had mounted. Part of #560.

What changes

The settings are read before the first render, beside the locale, which the window already waited for; the two reads run together, so the first paint waits for neither longer than before. What was read is handed to the hook that holds the settings, which asks main again only where that read failed, as it did before. No new bridge surface: the same ask, made earlier.

What remains: before the page's script runs at all, the page paints the stylesheet's light surface, or under the dark scheme the dark seed index.html paints, over a window frame that is already the custom colour; a custom theme shows that for that moment, then its own colours. The review's idea of a transparent body until the provider mounts, so the frame shows through, was tried and measured: in the light scheme the window's colour did show through, but under the dark scheme Chromium paints its own canvas colour, #121212, over the frame, so a custom dark theme would still flash, and the standard dark theme would flash a grey that is not its seed. Not taken. The page cannot know its colour before its script runs without being handed it, which is new bridge surface.

Mail rendering is unchanged here and tracked separately.

How to test this

Platforms: any. The journeys drive this on macOS and Windows. From the repository root, after npm ci and npm run build:once:

npx playwright test --project=journeys settings.spec
  • settings.spec.js, the custom theme journey, at its end: the page is reloaded with a script in it before any of its own, which notes the document's own style at the moment the app mounts, before anything is painted; the custom colours are in it, and not the standard theme's, and not none. On the code before this change the journey receives an empty style there, since a custom light theme's first render was the standard light theme, which puts nothing on the document.

Nothing else is new to test: the settings dialog's journeys and the language relaunch cover the hook as it is handed the settings.

Worth a look by hand (any platform, the current head): Settings → General → Theme → Custom, a background such as fff8e1, quit, open the app: the window is cream from the app's first render, with no moment of the standard light theme after the page has mounted. With 102030: navy from the app's first render; what the page paints before its script runs is the dark seed.

What must not have happened: the app failing to open its window when the settings cannot be read (the read's failure is logged and the hook asks again); a slower first paint; the language relaunch offer appearing on a fresh launch.

What I could not test: the packaged smoke on this head, since my own copy of the app held the single-instance lock; the change is the renderer's and the bridge is unchanged.

Risks and limitations

  • Review: 2 passes, 1 finding fixed here, the follow-up tried and not taken, every style note applied. Details below.
  • A custom dark theme still shows the dark seed for the moment before the page's script runs. See above.

Related

Follow-up of #650, part of #560 and #542.


Review outcome (required — see AGENTS.md)

Pass 1: 1 [fix here] · 1 [follow-up] · 0 style notes. Pass 2 (on the fix-up): 0 [fix here] · 0 [follow-up] · 2 style notes. The finding fixed here, the follow-up tried and not taken, both style notes applied.

  • Review: completed — a separate agent context, read-only; head c3801dd / base e0ed3ee; the hook's seeding against the language relaunch's loaded, the two reads' failure paths and timing, the reload against the deep-link queue and the provider's root count, the observer's timing against React's commit, the journey red on the old code; ESLint and the unit files run; 1 [fix here], 1 [follow-up].
    • Fixed: the comment, the journey and the commit said "first frame" and "before anything is painted", where the page paints before its script runs; they say the app's first render.
    • Follow-up, tried and not taken: a transparent body until the provider mounts. Measured above.
  • Review: completed — a second separate agent context, read-only; head d434c55 / base e0ed3ee; the wording checked place by place, the observer's timing against the provider's layout effect; ESLint and the unit files run; 0 findings, 2 style notes.
    • Style notes, applied: the first commit's message still said "first frame", so the fix-up is folded into it under the corrected words; the branch was named for the first paint and is named for the first render.
  • Since review: the fold and the rename, no change to the tree since d434c55. On the tree: npm run lint, npm test, npm run test:electron (2045), all 133 journeys and the docs build pass; the packaged smoke could not run, since the maintainer's own app holds the single-instance lock, and the bridge is unchanged.
Implementation notes
  • src/renderer/index.jsx: loadSettings, Root({ initialSettings }), the two reads before the first render. src/renderer/hooks/use-settings.jsx: useSettings(initial). src/renderer/components/app-theme.jsx: the comment.
  • tests/e2e/journeys/settings.spec.js: the reload with the mount observer.
Screenshots or recording

Not attached: what changed is the first frame, which a picture taken later does not show. The journey reads it.

🤖 Generated with Claude Code

…ed from the app's first render

The follow-up of #650. The settings were read once the app had mounted, and the theme is one of them, so a custom theme was painted after a first render in the standard theme of its scheme: a flash of the wrong colours at every launch. The window reads the settings beside the locale now, before its first render, and hands them to the hook that holds them; a read that fails leaves the hook to ask again once mounted, as it did. No new bridge surface: the same ask, made earlier. What the page paints before its script runs, the stylesheet's light surface or the dark seed, is as it was.

The custom theme journey reloads the page with a script in it before any of its own, which notes the document's own style at the moment the app mounts, before the app's first frame is painted: the custom colours, not the standard theme's and not none.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: WordPress/contributor-toolkit/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f1f542e8-c324-4425-a4fb-dc39114e6719

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@zaerl
zaerl merged commit 0cd70e4 into trunk Oct 7, 2026
8 checks passed
@zaerl
zaerl deleted the feat/theme-before-first-render branch October 7, 2026 10:41
@zaerl zaerl added this to the v2.0.0 milestone Oct 7, 2026
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