Renewal of Header Component
ARCHITECTURE •
Situation
The existing header was a conventional sticky navigation bar—functional but unremarkable. Three links (About, Blog, Playground) in a NavigationMenu with responsive drawer for mobile. It worked, but it didn't belong in a project calling itself an "Architectural Playground." The design communicated utility, not experimentation.
Wanted a header that could be three things: an immersive landing experience on home, a compact navigation bar elsewhere, and a transition mask that buys time during route changes. Not three components composed together—one component in different states. The implementation should be invisible; users shouldn't know whether they're seeing scroll-driven or button-triggered animation.
Task
- Design one component with three distinct roles driven by state transitions
- Implement scroll-as-narrative on homepage where interaction IS the experience
- Create seamless deception: identical appearance regardless of activation mechanism (scroll vs. button)
- Use animation timing strategically as loading choreography during route transitions
- Expose structural elements (grid lines as visible layout system)
- Apply minimal affordance: elements exist only when needed
Action
1. Built Working, Then Broke It
Started with intent, not knowledge. Knew the destination—didn't know the library.
Initial implementation functioned on homepage with scroll-driven animation. Direct navigation to /blog exposed the initialization bug: header assumed scroll context always existed. Scroll-vs-button race condition surfaced when rapidly toggling states.
Fixed at the right layer:
- State initialization: Properly handle undefined → collapsed → expanded transitions regardless of entry point
- Scroll lock: Prevent scroll interference during button-triggered transitions
2. Refactored After Understanding
First version worked. Second version was organized.
- Renamed by meaning:
motionStyles.link.bloginstead of generic identifiers - Consolidated animation effects into
useHeaderMotionhook - Internalized triggers: Component owns its state transitions
3. Added Choreography Depth
Single-phase animation became staged narrative:
- Navigation tiles exit fast (immediate user feedback)
- Separators linger (structural context persists)
- Scroll indicator hangs longest (teaching moment fades gradually)
- 1-second collapse duration buys time for Next.js route transitions—users watch the grid fold while the framework works underneath
4. Exposed the Grid System
Grid lines aren't decorative—they're the actual CSS Grid layout system made visible. When they animate away, you're watching the architecture disassemble. Structure as aesthetic.
- Horizontal and vertical separators positioned on grid lines
- Separators animate independently with different timing curves
- The visual system IS the layout system
5. Implemented Minimal Affordance
Elements exist only when needed:
- Chevron teaches scroll on initial load, then vanishes forever (
state === undefined) - Toggle button becomes X only when closable (
state === "expanded") - Blog/Playground areas are navigation and landing simultaneously—no duplicate elements
- Removed "About" link entirely—two destinations, not three
6. Deferred Abstraction
Recognized potential for reusable package
Result
Created a header that teaches Framer Motion through failure.
The component successfully fulfills three roles through state-driven design. On homepage, scroll IS the narrative—the user's journey through the page collapses the fullscreen grid into navigation. On other pages, direct button control provides the same visual result with different trigger. During route transitions, the 1-second animation choreography masks Next.js loading states.
The seamless deception works: users cannot distinguish scroll-driven from button-triggered states. The expanded header looks and behaves identically regardless of how it was activated. Implementation details vanish behind consistent surface presentation.
Animation serves dual purpose: aesthetic experience and functional time budget. The staged choreography (fast tile exits, lingering separators, delayed indicator fade) creates narrative depth while Next.js prefetches and hydrates routes. Loading states become part of the designed experience rather than technical necessity.
The architectural trade-off is maintenance complexity. A single component handling three distinct roles requires careful state machine design and comprehensive edge case handling. The scroll-vs-button race condition, initialization on direct navigation, and state persistence across route changes all required specific solutions. The implementation is more fragile than three separate components would be—but the user experience is more coherent.