Homepage implementation

The homepage presents one content tree through two layouts. Its scene, dock, preferences, and content remain mounted when the presentation changes.

The homepage presents one content tree through two layouts. Its scene, dock, preferences, and content remain mounted when the presentation changes.

Layout responsibilities

src/components/home/HomePage.tsx owns the section order: introduction, projects, client work, notes, photos, and contact. The CSS module applies the selected layout through data-home-layout on the page root.

LayoutDesktop presentationSmall screens
Two columnsSticky introduction on the left, sections on the right, scene behind bothStacked arrangement
HeroIntroduction inside a centered scene card, sections beneathCentered card with responsive spacing

Two columns is the default when no saved preference exists. A valid saved Hero preference still takes precedence. The top navigation and bottom dock remain centered. Layout variants reuse the content tree so switching does not duplicate sections, restart the artwork, or create independent copies of page state.

Preference storage

src/hooks/use-home-layout.ts exposes the current layout and whether the picker is open through useSyncExternalStore. It restores a validated layout from the home-layout storage key and synchronizes changes from other browser tabs. Only the layout choice is persisted; panel visibility is temporary UI state.

If storage is unavailable, the selection continues to work in memory and the store reports the failure. Invalid or removed values fall back to the default.

Keyboard and panel behavior

LayoutDockButton uses native radio inputs with explicit arrow-key handling. Arrow keys move focus between options; Enter applies the focused option. Mouse and touch selection apply immediately. Selecting the current option also closes the panel. Escape closes without changing the preference.

The scene and layout panels coordinate their open state so opening one dismisses the other. DockPanel retains an exiting panel long enough to fade and move it toward the dock, while making that surface inert and hidden from assistive technology. restoreFocusWithoutTooltip returns keyboard focus without leaving an unrelated tooltip over the page.

The command palette exposes the picker and direct layout actions. The desktop shortcut is Cmd/Ctrl+Shift+L. The visible picker button is specific to the homepage; commands can navigate home before applying a layout.

Scene rendering

SceneBackground positions the scene and applies the background fades needed for readable text. In Two columns, the artwork fills the viewport behind the columns. The eased gradient protects the text areas while leaving the open center visible in both themes.

SceneSurface loads the selected native scene and waits until it has produced a frame before crossfading. Keeping the previous surface during loading avoids flashing an empty background. A layout change keeps the existing canvas and changes its presentation instead of remounting the renderer.

Scene settings live in src/lib/home/scene-settings.ts and are managed by use-scene-store.ts. Defaults include 24 FPS, normal speed, automatic rotation, and a visible glyph trail. The controls expose character density, frame rate, color treatment, positioning, zoom, pause, and visibility.

Glyph trail

GlyphTrail adapts the native pointer-driven effect. It uses accent and shadow colors selected for the active scene, with deeper pigment on light backgrounds. The trail is enabled only for a fine desktop pointer without a reduced-motion preference. It does not add an idle animation or intercept pointer input.

Motion timing

When entering Two columns, both content columns use the same fade-and-rise timing. The scene fades in as it expands, while the navigation and dock enter in their established directions. Motion applies to the content surface rather than the scene's containing layout, preserving the full-viewport artwork.

Reduced-motion preferences disable entrance animation and smooth scrolling. Do not use motion as the only indication of a selected layout: checked state, labels, keyboard focus, and the visible content arrangement remain available.

Source map

FileResponsibility
src/lib/home/home-layouts.tsTyped layout options and descriptions
src/hooks/use-home-layout.tsPreference validation, persistence and panel state
src/components/home/HomePage.module.cssLayout widths, stacking and column entrances
src/components/home/SceneBackground.module.cssScene placement and theme-aware edge blending
src/components/home/SceneSurface.tsxReady-frame scene loading and crossfades
src/components/ui/DockPanel.tsxShared panel entrance and exit motion
src/lib/home/scene-trail-colors.tsScene-specific glyph-trail palettes

Verify a change

Test a fresh browser and one with a saved preference. Check Hero and Two columns, a narrow mobile viewport, a wide desktop viewport, and reduced motion. Use the keyboard to move focus without selecting, apply with Enter, dismiss with Escape, and reopen from the dock and command palette. Confirm the scene stays visible and the canvas is not recreated merely because the layout changed.