Skip to main content

Page Choreography

This document explains how each major route is assembled, what motion sequencing it uses, and where page-level orchestration versus shared component concerns live.

For the motion primitive catalog, see Motion architecture. For the component layer, see Component architecture.

Route assembly overview

Home (/)

The home page is the most orchestration-heavy route. It renders a faux-VS Code IDE as an interactive hero element.

Assembly

Entrance sequence

Page-owned state

StatePurpose
ideWindowStatenormal / minimized / expanded
ideWindowSizewidth × height for resize
Drag constraintsBound to hero container via heroBoundsRef
Typewriter playingTriggered by HeroMotionPath completion
Scroll transformsheroScale and heroOpacity from scroll progress
Welcome sequenceManaged by useHomeWelcomeSequence() hook

IDE window interactions

CV (/cv)

The CV page has two modes: a default grid layout and an immersive story viewer.

Default mode — assembly

Section layout metadata

src/pages/cvPageLayout.ts defines per-section placement and motion timing:

SectionDesktop regionDesktop delayDesktop triggerOnViewMobile order
abouttop0msfalse (immediate)0
experiencemain120mstrue1
educationmain240mstrue2
volunteeringmain360mstrue3
githubsidebar120mstrue4
certificatessidebar240mstrue5
codingmain480mstrue6

On mobile, all sections stack vertically with delayMs: 0 and most use triggerOnView: true (viewport-triggered reveal).

Within each section

Each CVSection* component wraps a CVSectionCard which contains:

  1. SectionHeading (overline + title)
  2. Feature content (list, profile card, GitHub panel, etc.)
  3. Internal AnimatedContentList for repeated entries (with per-item stagger)
  4. Optional TabPanel / AnimatedSlideList for expandable detail

Story mode (?mode=story)

Activated via query parameter. Replaces the grid layout with CVStoryViewer, a fullscreen scroll container that renders the ordered story chapters from buildCVStoryItems().

Climbing (/climbing)

Assembly

  • useFuzzySearch() provides route/location filtering across both DataGrid tables
  • initial load shows only the typed CLIMBING eyebrow; the analytics and tables stay deferred until the user scrolls far enough to hide the header
  • once unlocked, the main climbing card enters with the same spring-style card motion used by the deferred CV sections
  • Analytics panels use SectionPanel for dense data display

Photography (/photography)

Assembly

  • AlbumCard wraps in MotionTiltCard on desktop for 3D tilt parallax
  • StaggerChildren orchestrates sequential reveal across album cards
  • Image load state tracked to prevent layout shift during loading

Category detail (/photography/:slug)

  • Quilted layout uses MUI ImageList with responsive column counts
  • ImmersiveLightbox is a full-screen overlay with keyboard navigation, download, and close
  • Legacy slug redirects handled in the component for backward compatibility

Blog (/blog)

Public route rendered as part of the standard route set.

Assembly

  • BlogHero uses ContentCard with display-scale typography (editorial exception)
  • BlogPostList renders BlogPostCard items in a responsive grid
  • Tag filtering is page-local state driven by useBlogData().tags

Post detail (/blog/:slug)

  • BlogArticleBody renders typed BlogContentBlock[]: text, code, image, callout, blockquote
  • BlogCodeBlock provides syntax highlighting with copy-to-clipboard
  • Not-found slugs render RouteRecoveryPanel with contextual suggestions

NotFound (/*)

Assembly

  • No PageFrame — uses BackgroundPaper directly for full-bleed treatment
  • StaggerChildren + MotionItem animate the text reveal
  • RouteRecoveryPanel provides contextual navigation suggestions based on the attempted path

Page-level vs component-level concerns

ConcernBelongs at page levelBelongs in component
Section ordering and delay timing
delayMs for each section✓ (via layout metadata)
triggerOnView per section✓ (via layout metadata)
Tab/accordion open state✓ (feature component)
Search/filter state✓ (page-local state)
Stagger timing within a section✓ (AnimatedContentList)
Hover/tilt interactions✓ (motion primitives)
Window drag/resize (Home)
Story mode scroll progress + active chapter tracking✓ (CVStoryViewer)
Background image selection✓ (passed to PageFrame)
SEO metadata✓ (via useDocumentMetadata())

Choreography rules for new pages

  1. Use PageFrame as the scaffold unless the page needs a full-bleed background (then use BackgroundPaper)
  2. Wrap sections in MotionSection or SectionCard for viewport-triggered reveals
  3. Define section delays in a layout metadata object (not inline) if the page has multiple sections with staggered timing
  4. Let shared components handle their internal animation — don't override stagger timing from the page
  5. Use SectionHeading for section intros — don't build custom title stacks
  6. Keep page-level state minimal — filters, search, expanded mode — and pass data down as props
  7. Follow the data flow: hook → page → section component → list → card

Further reading