Brutalist Design Philosophy
Off Grid uses a terminal-inspired brutalist design system with full light/dark theme support. The system emphasizes:- Information density over decoration
- Functional minimalism with zero ornamental UI
- Monochromatic palette with emerald accent
- Monospace typography (Menlo) throughout
- Crisp borders and geometric layouts
Theme System
Dynamic light/dark theming viasrc/theme/:
Core Files
palettes.ts—COLORS_LIGHT,COLORS_DARK,SHADOWS_LIGHT,SHADOWS_DARK,createElevation()index.ts—useTheme()hook,getTheme(mode),ThemetypeuseThemedStyles.ts—useThemedStyles(createStyles)memoized style factory
Usage Pattern
Every screen and component follows this pattern:Theme Toggle
Theme mode is stored inappStore.themeMode (persisted) and toggled via the Settings screen Dark Mode switch.
Token Organization
- Theme-independent tokens stay in
src/constants/:TYPOGRAPHY,SPACING,FONTS - Dynamic tokens come from hooks:
colors.*,shadows.*
Color Palettes
Light Palette
Dark Palette
Shadows & Elevation
Light Shadows
Uses CSS boxShadow (RN 0.76+ with New Architecture) for cross-platform shadow rendering:Dark Shadows
Crisp white glow for depth in dark mode:Elevation Factory
ThecreateElevation() function generates 5 elevation levels with appropriate backgrounds, borders, and blur effects:
Typography Scale
10-level scale, all Menlo monospace:Usage Guidelines
- h1 — Hero text only (24px, light weight)
- h2 — Screen titles (16px)
- h3 — Section headers (13px)
- body — Primary content (14px)
- bodySmall — Descriptions (13px)
- label — Uppercase labels (10px)
- meta — Timestamps, metadata (10px, lighter weight)
Spacing Scale
6-step scale for consistent whitespace:Animation System
Powered by react-native-reanimated with spring-based physics and react-native-haptic-feedback.AnimatedEntry
Staggered fade + slide entrance for lists and grids. RespectsuseReducedMotion().
Source: src/components/AnimatedEntry.tsx
index— Item index in list (for stagger calculation)delay— Manual delay override (ms)staggerMs— Delay per item (default: 30ms)maxItems— Max items to animate (default: 10)from— Initial style (default:{ opacity: 0, translateY: 12 })animate— Target style (default:{ opacity: 1, translateY: 0 })transition— Timing config (default:{ duration: 300 })trigger— Change this value to replay animation
Automatically skips animation if
index >= maxItems to prevent performance issues in long lists.AnimatedPressable
Spring scale-down on press with haptic feedback. Source:src/components/AnimatedPressable.tsx
scaleValue— Scale on press (default: 0.97)hapticType—'selection'|'impactLight'|'impactMedium'|'impactHeavy'|'notificationSuccess'|'notificationWarning'|'notificationError'disabled— Disable interaction- Standard TouchableOpacity props
AppSheet
Custom swipe-to-dismiss bottom sheet with spring animation, replacing React Native modals. Source:src/components/AppSheet.tsx
useFocusTrigger
Hook that replays stagger animations on every tab focus.Global Animation Behavior
- Tab transitions — Fade animations + haptic feedback on bottom tab switches
- Modal screens —
slide_from_bottomanimation for all modal presentations - Reduced motion — All animations automatically disabled when user has system-level reduced motion enabled
Design Tokens Reference
Quick Import
File Locations
| Token | File |
|---|---|
| Colors | src/theme/palettes.ts |
| Shadows | src/theme/palettes.ts |
| Elevation | src/theme/palettes.ts |
| Typography | src/constants/index.ts |
| Spacing | src/constants/index.ts |
| Fonts | src/constants/index.ts |