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​