Skip to main content

App Architecture

This document covers the application shell, provider hierarchy, route structure, and page composition model.

Provider nesting

Providers are ordered from outermost (root) to innermost (content):

Provider responsibilities

ProviderStatePersistenceHook
ThemeProviderPalette mode (light/dark), appearance preset (6 options), motion intensity (4 levels)Three independent localStorage keysuseAppTheme()
WelcomeOnboardingProviderPause-motion hint, dark-mode hintSession-scoped (no persistence)useWelcomeOnboarding()
WelcomeAudioProviderAudio consent (unknown/granted/declined), playback state, widget lifecyclelocalStorage consent keyuseWelcomeAudio()
CommandPaletteProviderOpen/close state, search queryNoneuseCommandPalette()

Key constraint: ThemeProvider must remain outermost because all downstream providers and components depend on MUI's ThemeProvider for useTheme() access. CommandPaletteProvider wraps only AppContent, not the root — it doesn't need theme context consumers above it.

Application shell

Inside App, the layout shell contains global chrome that persists across route transitions:

Shell components

ComponentScopePurpose
ScrollProgressBarGlobalThin progress indicator at viewport top, driven by scroll position
HeaderGlobalSticky app bar with primary navigation, theme/appearance toggles, motion intensity dial, audio controls, and mobile drawer
PageTransitionWraps RoutesCrossfade + slide-up on route change using AnimatePresence mode="wait" — respects motion intensity scaling
FooterGlobalSite footer below all route content
CommonLinkTooltipGlobalPopper-based tooltip for external links across all pages
GlobalCommandPaletteGlobalCmd+K modal for site-wide search and navigation

Route definitions

Route metadata

Route definitions live in src/constants/siteRoutes.ts as siteRouteMap. Each route carries:

  • path, label, title, description — used by navigation, SEO head tags, and command palette
  • image — OG image reference
  • keywords — SEO and command palette search targets
  • showInPrimaryNav — whether to show in header navigation
  • action — command palette entry with recoveryPriority for not-found suggestions
  • status — data source kind (static, remote with fallback)

Derived exports:

  • siteRoutes — all route definitions
  • primaryNavigationRoutes — routes with showInPrimaryNav: true

Page composition model

Every route page follows one of two scaffold patterns:

PageFrame (standard scaffold)

Used by: Blog, BlogPost, CV, Climbing, Photography, PhotographyCategory

Provides:

  • Full-viewport BackgroundPaper with configurable background image
  • Responsive Container (MUI) with route-specific max-width and padding
  • Automatic useDocumentMetadata() call for SEO

BackgroundPaper (full-bleed scaffold)

Used by: Home, NotFound

Provides:

  • Full-height scenic image backdrop with ::before overlay
  • Optional shellWrapper render prop for placing content panels over the image
  • Content alignment (centered or custom)

Page-level statefulness

Pages own orchestration state. Shared components are intentionally stateless or accept controlled props:

ConcernOwned byNot owned by
Section visibility sequencingPage (via layout metadata + delayMs)Shared components
Tab/accordion expanded stateFeature component (e.g., ExperienceList)Page
Story mode navigationCVStoryViewerCV.tsx (delegates)
IDE window stateHome.tsx (drag/resize/expand)TerminalHeroContent (receives props)
Audio prompt flowuseHomeWelcomeSequence() hookPage (consumes)
Search/filter statePage-local stateData hooks (return full datasets)

Runtime environment helpers

src/constants/runtimeEnvironment.ts resolves the app runtime environment from REACT_APP_RUNTIME_ENV and NODE_ENV.

  • resolveAppRuntimeEnvironment() returns development, test, or production
  • appRuntimeEnvironment exposes the resolved value for modules that need it
  • Current route exposure is public; the runtime helper is used for environment-sensitive client behavior such as development-only welcome-sequence reset logic

Cross-cutting concerns

ConcernImplementationLocation
SPA routingHost must rewrite unknown paths to index.htmlBrowserRouter with PUBLIC_URL basename
SEO metadatauseDocumentMetadata() sets title, description, OG tags per routesrc/hooks/useDocumentMetadata.ts
AccessibilitySkip links, keyboard-navigable tab panels, ARIA rolesApp shell + TabPanel + components
Error recoveryRouteRecoveryPanel with contextual route suggestionsNotFound page + blog/photography not-found states
GitHub degradationuseGithubProfile() falls back to bundled data on API failuresrc/hooks/useGithubProfile.ts
Static assetsPUBLIC_URL-compatible paths via src/utils/assets.tsAll image/PDF references

Where new features should go

Further reading