The template's visual language: design tokens (colors, spacing, radii, typography), light/dark support, and a small set of themed base components every screen builds from.
Everything lives under src/presentation/theme/ and is
re-exported from its index.ts:
import { Screen, Card, AppText, Button, useTheme } from '../theme';tokens.ts is the single source of truth:
ColorTokens— brand roles (primary/onPrimary,secondary/onSecondary,error/onError,surface/onSurface), plus the extra roles an RN surface needs (background,onSurfaceMuted,border,favorite).lightColorsanddarkColorscarry the brand values for each scheme.spacing— a 4-pt scale (xs…xxl). Use it instead of literal margins/paddings.radius— corner radii (sm/md/lg).typography— text variants (title,heading,body,subtitle,caption,button) as size + weight; color is applied separately.
theme.ts bundles the tokens into a
Theme ({ dark, colors, spacing, radius, typography }) and exports
lightTheme / darkTheme.
ThemeProvider resolves the
active theme and exposes it via context. It wraps the app in
App.tsx.
useTheme(): Theme— the active theme. It falls back tolightThemeoutside a provider (so a component rendered in isolation in a test never crashes), unlikeuseServices()which throws.useThemeMode()—{ mode, setMode }wheremodeis'system' | 'light' | 'dark'. Insystemmode the OS color scheme wins and the app re-themes automatically when the device toggles dark mode;setModeforces a variant. The user-facing control is the Theme switch in the diagnostics overlay (ThemeSection), which applies immediately; the mechanism is here. Forsystemmode to actually track the device,app.config.tssetsuserInterfaceStyle: 'automatic'(pinning it tolightwould lock the native appearance and stopuseColorScheme()from ever reporting dark).
React Navigation is themed too:
NavigationRoot bridges the
tokens into a React Navigation Theme, so headers, tab bars, and the container
background follow the same palette.
Small, themed primitives so screens hold no inline color or magic number:
| Component | Role |
|---|---|
Screen |
Root container — fills space, paints the themed background; padded / center props for the common layouts. |
Card |
Elevated surface — rounded, bordered, padded from the theme. |
AppText |
Themed Text with a variant (typography) and tone (color role). |
Button |
Primary action — primary (filled) / outline (bordered); always pass a localized label. |
- Screens and components read visual values through
useTheme()(or the base components) — never a hardcoded hex color, font size, or literal margin/padding. A color/spacing value that isn't in the tokens is a smell: add a token. - New shared visual pieces become base components under
src/presentation/theme/rather than one-off styles copied between screens.
See Architecture.md for where the theme sits in the layers, and DadJokes.md for the reference feature that renders with it.