Dark Mode Coverage Audit — Ideony Mobile
Dark Mode Coverage Audit — Ideony Mobile
Section titled “Dark Mode Coverage Audit — Ideony Mobile”Date: 2026-04-20
Scope: apps/mobile/app/ (53 files) and apps/mobile/components/ (58 files)
1. Summary
Section titled “1. Summary”| Metric | Count | % |
|---|---|---|
Screen files (app/**/*.tsx) | 53 | — |
Component files (components/**/*.tsx) | 58 | — |
Screens with any dark support (useThemeColors / darkColors / dark:) | 0 | 0% |
| Components with any dark support | 0 | 0% |
Files using useColorScheme (NativeWind) | 2 (_layout.tsx, TabBar.tsx) | 3% |
Files using dark: NativeWind class | 0 (in app/ + components/) | 0% |
Root cause: Every screen and component imports the static colors object from @/lib/colors (the light palette) and passes values into StyleSheet.create(). useThemeColors() exists at lib/hooks/use-theme-colors.ts but is called by zero screens and zero components. The NativeWind dark: variant pathway requires darkMode: "class" — the class is never toggled programmatically, so dark: would also be inert even if used.
2. Theme Infrastructure Gaps
Section titled “2. Theme Infrastructure Gaps”2a. Zustand store — no persistence
Section titled “2a. Zustand store — no persistence”apps/mobile/lib/stores/theme.ts
export const useThemeStore = create<ThemeState>((set) => ({ preference: "system", setPreference: (preference) => set({ preference }),}));No persist middleware, no AsyncStorage. Preference resets to "system" on every cold start.
2b. useThemeColors() wired to nothing
Section titled “2b. useThemeColors() wired to nothing”apps/mobile/lib/hooks/use-theme-colors.ts reads NativeWind’s useColorScheme(). The Zustand preference is never forwarded to NativeWind’s setColorScheme(). Effect: the store preference has zero runtime effect on rendered colors.
2c. darkMode: "class" — class never set
Section titled “2c. darkMode: "class" — class never set”apps/mobile/tailwind.config.ts sets darkMode: "class". No code anywhere calls NativeWind’s setColorScheme("dark") or puts a dark class on the root view. dark: variants compile but are permanently dead.
3. Hardcoded Color Literals
Section titled “3. Hardcoded Color Literals”In app/ screens (inline hex, not via colors.*):
Section titled “In app/ screens (inline hex, not via colors.*):”| File | Literal(s) |
|---|---|
app/_layout.tsx | 13 Clerk appearance values: #B35F3B, #E5484D, #10B981, #F59E0B, #737371, #2B1E10, #8C7B6A, #FAF6EE, #F0EBE0, #DDD5C8 |
app/sos/[id]/tracking.tsx | shadowColor: "#000" |
app/review/[bookingId].tsx | SUN_AMBER = "#E89059", DARK_INK = "#2B1E10", emoji colors #E5484D/#F5C842/#4A9EFF, color: "#FFFFFF" (×2) |
app/booking/[id].tsx | color: "#92400E" |
app/(professional)/credentials.tsx | color: "#FAF6EE" |
app/(professional)/calendar.tsx | selectedDayTextColor: "#FFFFFF" |
app/(professional)/onboarding/portfolio.tsx | color="#fff" (Trash icon) |
app/chat/[bookingId].tsx | color: isMe ? "#FAF6EE" : colors.text, color="#FAF6EE" (SendHorizontal icon) |
app/(professional)/earnings.tsx | color: "#FFFFFF" |
app/(consumer)/triage.tsx | AI_BUBBLE_BG = "#F5E6CB", color: "#2B1E10" |
app/book/[professionalId].tsx | color="#FFF" (CheckCircle2 icon) |
In components/:
Section titled “In components/:”| File | Literal(s) |
|---|---|
components/credentials/TrustTierBadge.tsx | bgColor: "#FFF4E6", textColor: "#B85C00", borderColor: "#E89059", iconColor: "#E89059" |
components/credentials/AddCredentialSheet.tsx | color="#fff" (ActivityIndicator), color: "#fff" |
components/WelcomeTour.tsx | shadowColor: "#000" |
components/MediaAttachments.tsx | color="#FFF" (X icon) |
components/results/ProCard.tsx | Category colors "#7C6B52"/"#8B6914"/"#7A7A7A", shadowColor: "#8B4A2B" |
components/results/CompareSheet.tsx | shadowColor: "#000" |
components/professional/HeroCarousel.tsx | backgroundColor: "#fff", color: "#fff" |
components/ui/Badge.tsx | text: "#92400E", "#065F46" (×2), bg: "#DBEAFE", text: "#1E40AF", "#991B1B" (×2) |
components/home/PromptCard.tsx | shadowColor: "#2B1E10" |
4. Light-Only colors.* Usage (Sole Palette — No Dark Equivalent)
Section titled “4. Light-Only colors.* Usage (Sole Palette — No Dark Equivalent)”All 53 app screen files and 52 of 58 component files import colors (light) from @/lib/colors. None use useThemeColors() to switch between colors / darkColors. The full darkColors object exists in lib/colors.ts but is unreachable from any screen or component at runtime.
Highest-impact files (used as primary background/surface):
app/(consumer)/index.tsx— home screenapp/(consumer)/search.tsxapp/(professional)/dashboard.tsxapp/professional/[id].tsx— professional profileapp/booking/[id].tsxapp/chat/[bookingId].tsxcomponents/chrome/TabBar.tsx— usesconst palette = colors(hardcoded; readscolorSchemefrom NativeWind but ignores it for palette selection)components/chrome/Header.tsx
5. Missing NativeWind dark: Variants
Section titled “5. Missing NativeWind dark: Variants”Zero dark: classes exist in any app/ or components/ file.
One exception within app: components/credentials/CredentialCard.tsx line 89 uses className="rounded-xl border bg-white p-4" — bg-white with no dark:bg-* override.
dark: classes only appear in three lib files:
apps/mobile/lib/gluestack-config.tsapps/mobile/lib/mapbox/style-urls.tsapps/mobile/lib/mapbox/sole-palette.ts
6. SOS Scope — Phase A Requirement
Section titled “6. SOS Scope — Phase A Requirement”Phase A spec: SOS flows must always render the dark palette regardless of system setting.
Current state: NOT enforced anywhere.
| File | Palette used | Dark enforcement |
|---|---|---|
app/sos/_layout.tsx | colors.background (light cream) | None |
app/sos/index.tsx | colors.* throughout | None |
app/sos/[id]/countdown.tsx | colors.* throughout | None |
app/sos/[id]/tracking.tsx | colors.* + shadowColor: "#000" | None |
app/sos/[id]/cancelled.tsx | colors.* | None |
app/sos/[id]/complete.tsx | colors.* | None |
components/sos/SOSButton.tsx | colors.sos, colors.textInverse, colors.sosLight (light) | None |
components/sos/DispatchStatusBar.tsx | colors.sos, colors.accent, colors.success (light) | None |
components/sos/TrackingMap.tsx | styleURL={MAPBOX_DARK_STYLE} (dark-v11) | Map style dark only |
components/sos/CandidateCard.tsx | colors.* (light) | None |
components/sos/CountdownRing.tsx | colors.* (light) | None |
TrackingMap hardcodes "mapbox://styles/mapbox/dark-v11" — uses a generic Mapbox dark style instead of the custom MAPBOX_SOLE_DARK URL from lib/mapbox/style-urls.ts.
7. Icon Theming
Section titled “7. Icon Theming”All Phosphor (phosphor-react-native) and Lucide (lucide-react-native) icon color= props consume colors.* (light palette). No icon reads from useThemeColors().
Tab bar icons in app/(consumer)/_layout.tsx receive color via the tabBarIcon callback — sourced from TabBar.tsx which computes iconColor = isFocused ? palette.primary : palette.textTertiary where palette = colors (hardcoded light).
Notable fixed-hex icon colors in screens/components (not via colors.*):
app/book/[professionalId].tsx:<CheckCircle2 color="#FFF" />app/chat/[bookingId].tsx:<SendHorizontal color="#FAF6EE" />app/(professional)/onboarding/portfolio.tsx:<Trash color="#fff" />components/MediaAttachments.tsx:<X color="#FFF" />
8. StatusBar
Section titled “8. StatusBar”Root app/_layout.tsx:
<StatusBar style={colorScheme === "dark" ? "light" : "dark"} />Correct — dynamic. No other StatusBar instances in non-layout files.
9. Design Token Gaps
Section titled “9. Design Token Gaps”packages/design-tokens/src/index.ts exports only the light Sole palette. No dark token scale.
darkColors exists only in apps/mobile/lib/colors.ts as hardcoded hex values with no token references. Moving dark tokens into @ideony/design-tokens is a prerequisite for multi-package dark mode consistency.
10. Splash Screen
Section titled “10. Splash Screen”apps/mobile/app.json:
{ "backgroundColor": "#FAF6EE" }Light cream only. No dark splash variant. iOS/Android do not support per-scheme splash background via Expo config — requires native splash screen swap workaround.
11. Mapbox
Section titled “11. Mapbox”Two dark map styles exist and are well-defined:
MAPBOX_SOLE_DARKatlib/mapbox/style-urls.ts— custom brand dark styleSOLE_DARK_CONFIGatlib/mapbox/sole-palette.ts— Standard style runtime config
Neither is wired to system color scheme in any map component. TrackingMap hardcodes a non-Sole dark style (dark-v11). MiniMap.tsx and MapAffordanceStrip.tsx (consumer home) both import colors light palette — map style URLs not checked but no colorScheme branching visible.
12. Clerk Authentication Appearance
Section titled “12. Clerk Authentication Appearance”apps/mobile/app/_layout.tsx lines 101–160: 13 hardcoded light-palette hex values passed to Clerk’s appearance prop. No dark conditional. Clerk’s appearance API accepts object-level variants — a dark theme object would need to be added.
Priority Fix Order
Section titled “Priority Fix Order”- Wire Zustand → NativeWind: call
setColorScheme(preference)inuseThemeStore.setPreference+ on hydration - Add AsyncStorage persistence to
useThemeStore - Replace
colorsimports withuseThemeColors()in all screens and components (51 app files, 52 component files) - SOS dark enforcement: use
darkColorsunconditionally in allapp/sos/**andcomponents/sos/** - Export dark tokens from
@ideony/design-tokens - Fix Clerk appearance to branch on color scheme
- Fix
TrackingMapto useMAPBOX_SOLE_DARKinstead of genericdark-v11 CredentialCardbg-white→bg-white dark:bg-surface- Splash: add dark splash workaround (native splash swap on hydration)