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.
| Layout | Desktop presentation | Small screens |
|---|---|---|
| Two columns | Sticky introduction on the left, sections on the right, scene behind both | Stacked arrangement |
| Hero | Introduction inside a centered scene card, sections beneath | Centered 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
| File | Responsibility |
|---|---|
src/lib/home/home-layouts.ts | Typed layout options and descriptions |
src/hooks/use-home-layout.ts | Preference validation, persistence and panel state |
src/components/home/HomePage.module.css | Layout widths, stacking and column entrances |
src/components/home/SceneBackground.module.css | Scene placement and theme-aware edge blending |
src/components/home/SceneSurface.tsx | Ready-frame scene loading and crossfades |
src/components/ui/DockPanel.tsx | Shared panel entrance and exit motion |
src/lib/home/scene-trail-colors.ts | Scene-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.
Nicholas Adamou