Engineering Decisions Behind a Modern Marketing Site Build
Architecture choices and implementation trade-offs from building a scroll-driven marketing site with Next.js 15, GSAP, and Lenis.
Building marketing sites that balance performance with rich motion requires deliberate technical choices. Our recent site rebuild taught us several lessons about scroll-driven animation architecture, smooth scrolling integration, and automation tooling.
Scroll Animation Architecture: GSAP + Lenis Integration
The core technical decision was pairing GSAP ScrollTrigger with Lenis smooth scrolling. According to our lib/use-lenis.ts implementation, we sync Lenis with the GSAP ticker rather than using requestAnimationFrame directly. This eliminates frame desync issues between smooth scroll position and animation triggers.
The integration mounts through a custom useLenis hook that handles the bidirectional sync. When GSAP updates, it drives Lenis. When Lenis scrolls, it updates ScrollTrigger. This architecture ensures pinned sections in components/sections/Manifesto.tsx and components/sections/Services.tsx remain perfectly synchronized with scroll position.
- GSAP ticker drives Lenis updates at consistent 60fps
- ScrollTrigger.update() called on every Lenis scroll event
- getLenis() utility provides global access for anchor navigation in components/Navbar.tsx
Component Architecture for Scroll-Driven Sections
Each section component encapsulates its own ScrollTrigger logic. The components/sections/Process.tsx file demonstrates a pinned 4-stage morphing SVG implementation. Rather than managing all animations centrally, each section registers its own triggers on mount and cleans up on unmount.
The components/sections/Services.tsx horizontal scroll section shows another pattern: pinning the container while translating inner content based on scroll progress. The parallax card internals use data-speed attributes processed by lib/parallax.ts to create depth without complex calculations.
- Self-contained ScrollTrigger instances per section component
- Data attributes for declarative parallax speeds
- Cleanup on unmount prevents memory leaks and orphaned triggers
Blog Automation Pipeline
The scripts/blog/ directory contains our content automation system. Rather than manual content creation, we built tooling that generates drafts from repository context. The npm run blog:generate-draft command pulls recent commits, changed files, and README documentation to create contextual drafts.
The pipeline validates schema before publishing to content/blog/posts.json. This approach ensures consistent structure while maintaining editorial control. Drafts live in content/blog/drafts/ for review before publication. The OPENROUTER_MODEL environment variable configures the AI model used for generation.
Our app/api/contact/route.ts handler demonstrates a similar automation opportunity. Currently a stub, it's structured to integrate with email services or CRM systems. The components/ContactForm.tsx implementation includes floating labels and client-side validation before POST submission.
- Architecture
- Performance
- Animation
