Skip to content

fix(mobile): dark mode error screen readability and splash flash#85

Open
GSTJ wants to merge 2 commits into
mainfrom
GSTJ/dark-mode-polish
Open

fix(mobile): dark mode error screen readability and splash flash#85
GSTJ wants to merge 2 commits into
mainfrom
GSTJ/dark-mode-polish

Conversation

@GSTJ

@GSTJ GSTJ commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

The generic error screen is readable in dark mode now, and cold starts no longer flash a white splash before going dark.

Details

  • Error screen: the container never had a background, so it rendered straight over the native window; with the in-app theme forced dark while the system was light, you got navy text on a black window. It now paints the theme background, and the ThemeProvider derives everything from the stored choice first so text and backdrop always agree.
  • Splash flash: the theme override only applied at JS runtime, so every cold start showed the system-appearance splash then flipped (~2s white slam in dark mode). A small config plugin injects the stored theme into the AppDelegate before the splash renders; the splash asset already had a dark variant, so the first in-process frame comes out dark. The only remainder is the OS-drawn pre-process frame (~0.4s, platform limit) which crossfades instead of slamming.
  • Screens also stop defaulting to white containers via React Navigation's theme, and the window background follows the theme.
  • Version bumped (native change).
  • Before/after evidence: screen recordings frame-analyzed (before: brightness 98 to 1 slam; after: zero bright frames on dark boots, zero dark frames on light boots) and error screen screenshots in both themes plus the mismatch combo.
  • Follow-up worth a separate pass: the Android status bar color is still hardcoded light.

Testing steps

  1. In the app, force dark theme in the profile settings while your phone is in light mode. Force-close and reopen: the splash should come up dark right away, no white flash.
  2. Turn on airplane mode and navigate somewhere that needs the network until the "Oops" error screen appears, in dark mode: title and text should be clearly readable (white on dark), with the retry button visible.
  3. Same screen in light mode: unchanged from before (dark text on white).
  4. Boot the app a few times in plain light mode: no dark flash either.

GSTJ added 2 commits July 9, 2026 03:46
The NetworkBoundary error screen (fire/flask illustration) had no
background of its own, so it rendered directly over the native window.
When the styled-components theme and the window appearance disagreed
(forced theme vs system scheme), the light theme's navy text landed on
a black backdrop and was unreadable in dark mode.

Give the container an explicit theme background so text and backdrop
always come from the same theme object: readable in every combination,
light mode visually unchanged.
…s forced

The in-app theme override only lived in AsyncStorage and was applied at
JS runtime via Appearance.setColorScheme, so every cold start rendered
the native splash and the first JS frames with the SYSTEM appearance and
then flipped: users who forced dark mode on a light-mode device booted
into ~2s of white splash slamming to black.

Fixes, layered so every boot phase agrees on the theme:
- New withInitialThemeOverride config plugin injects a snippet into
  AppDelegate.swift that reads the forced theme from UserDefaults
  (mirrored by ThemeProvider via RN Settings) and sets the window's
  overrideUserInterfaceStyle before the splash renders. The splash
  storyboard's color asset already has a dark variant, so the splash now
  resolves dark from the first in-process frame - and RN's Appearance
  reports the forced scheme immediately too.
- ThemeProvider now derives the theme from the user's stored choice
  first (system scheme as fallback) instead of relying on the
  setColorScheme side effect, wraps children in React Navigation's
  ThemeProvider so screen containers stop defaulting to white, and syncs
  the window background via expo-system-ui on theme change.
- The stored-theme read starts at module scope and _layout keeps the
  native splash visible until it has been applied, so the first visible
  JS frame is already themed.

Version bumped to 1.4.1 (native change, per the app.config convention).

Verified on the iPhone 17 Pro Max sim: forced-dark + system-light boots
into a dark splash (only the OS-drawn pre-process launch image still
follows the system, an iOS limit), system-dark boots fully dark, light
mode boots clean with no dark flashes.
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