Skip to content

Commit e705859

Browse files
CassioMGclaude
andauthored
fix(navigation): paint scene background to remove white modal flicker (#912)
The NavigationContainer was rendered without a theme, so React Navigation fell back to its light DefaultTheme (background rgb(242,242,242)). Native Stack uses the theme's colors.background as each screen's default contentStyle background, so the light native scene container showed through during transitions — most visibly as a white flicker at the iOS modal's rounded corners while a screen slides in horizontally (e.g. the Send flow's slide_from_right push). Set a dark NAVIGATION_THEME (extends DarkTheme, overrides background/card with the app's #161616 surface token) on the NavigationContainer so the native scene background is dark everywhere. This is a central fix covering every navigator and screen rather than patching a single flow. NAVIGATION_THEME lives in its own config/navigationTheme module rather than config/theme so the design-token file stays free of the React Navigation dependency — config/theme is imported by nearly every component, and several component tests replace @react-navigation/native with an inline mock that omits DarkTheme. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9d2bcb2 commit e705859

3 files changed

Lines changed: 56 additions & 0 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { DefaultTheme } from "@react-navigation/native";
2+
import { NAVIGATION_THEME } from "config/navigationTheme";
3+
import { THEME } from "config/theme";
4+
5+
describe("NAVIGATION_THEME", () => {
6+
it("uses the app's dark surface as the scene background", () => {
7+
// React Navigation's Native Stack paints each screen's contentStyle with
8+
// the theme's `colors.background` by default. If we don't override it, the
9+
// light DefaultTheme background flashes through during transitions (most
10+
// visibly as a white flicker at the iOS modal's rounded corners). The
11+
// navigation theme must therefore match the app's dark surface.
12+
expect(NAVIGATION_THEME.colors.background).toBe(
13+
THEME.colors.background.default,
14+
);
15+
expect(NAVIGATION_THEME.colors.card).toBe(THEME.colors.background.default);
16+
});
17+
18+
it("does not fall back to the light DefaultTheme background", () => {
19+
expect(NAVIGATION_THEME.colors.background).not.toBe(
20+
DefaultTheme.colors.background,
21+
);
22+
expect(NAVIGATION_THEME.dark).toBe(true);
23+
});
24+
});

src/components/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
import * as Sentry from "@sentry/react-native";
77
import { AuthErrorToastListener } from "components/AuthErrorToastListener";
88
import { initializeSentryLogger } from "config/logger";
9+
import { NAVIGATION_THEME } from "config/navigationTheme";
910
import { RootStackParamList } from "config/routes";
1011
import { initializeSentry } from "config/sentryConfig";
1112
import { THEME } from "config/theme";
@@ -70,6 +71,7 @@ export const App = (): React.JSX.Element => {
7071
<AuthErrorToastListener />
7172
<NavigationContainer
7273
ref={navigationRef}
74+
theme={NAVIGATION_THEME}
7375
onStateChange={onStateChange}
7476
>
7577
<AuthCheckProvider>

src/config/navigationTheme.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { DarkTheme, Theme } from "@react-navigation/native";
2+
import { THEME } from "config/theme";
3+
4+
/**
5+
* React Navigation theme for the app's top-level `<NavigationContainer>`.
6+
*
7+
* Without an explicit theme, React Navigation falls back to its light
8+
* `DefaultTheme` (background `rgb(242, 242, 242)`). Native Stack uses the
9+
* theme's `colors.background` as each screen's default `contentStyle`
10+
* background, so that light native container shows through during transitions
11+
* — most visibly as a white flicker at the iOS modal's rounded corners while a
12+
* screen slides in horizontally. Painting the native scene background with the
13+
* app's dark surface removes that flash for every navigator and every screen.
14+
*
15+
* The app forces dark mode (see `App.tsx`), so this extends `DarkTheme` and
16+
* overrides the surface colors with the app's own background token.
17+
*
18+
* Kept in its own module (rather than `config/theme.ts`) so the design-token
19+
* file stays free of the React Navigation dependency — `config/theme` is
20+
* imported by nearly every component, and several component tests replace
21+
* `@react-navigation/native` with an inline mock that omits `DarkTheme`.
22+
*/
23+
export const NAVIGATION_THEME: Theme = {
24+
...DarkTheme,
25+
colors: {
26+
...DarkTheme.colors,
27+
background: THEME.colors.background.default,
28+
card: THEME.colors.background.default,
29+
},
30+
};

0 commit comments

Comments
 (0)