- Category
- landing page
- Tech Stack
- Astro, Web Components, Anime.js, Locomotive Scroll, Nanostores, Tailwind CSS
Interactive product storytelling website focused on motion design and immersive user experience. The core challenge was building a seamless scroll-based narrative that combines cinematic transitions, synchronized animations, layered parallax effects, and responsive interaction patterns without sacrificing performance.
The frontend architecture was designed around smooth scroll orchestration, reusable animation systems, and GPU-friendly rendering techniques to maintain fluidity across desktop and mobile devices.
Challenges
One of the core challenges was designing a convincing sense of depth between sequential sections, particularly during the transition from the hero section into the “Philosophy” section.
Instead of treating sections as isolated vertical blocks, the scroll flow was designed as a layered spatial transition, where the next section appears as if it emerges from beneath the previous one.
To achieve this effect, the “Philosophy” section was composed with a background SVG system representing a stylized topographic map. During scroll progression, SVG paths are dynamically animated to simulate elevation changes and terrain formation, creating a pseudo-3D visual perception.
Scroll progress is normalized and exposed as a CSS variable (--progress) via Locomotive Scroll. This value is then used to drive GPU-accelerated transforms (translate3d) and path animations, ensuring smooth performance while keeping calculations on the compositor layer.
This approach allowed the system to simulate spatial depth without relying on WebGL, while maintaining high frame stability.
2. Animation Orchestration System
Another key challenge was designing a scalable and consistent animation orchestration layer for heterogeneous UI elements.
The project used Anime.js as a lightweight animation engine, chosen for its flexibility and lower overhead compared to heavier alternatives.
The system defines two primary animation targets: - text-based elements
- structural UI blocks
Instead of triggering animations imperatively, a declarative attribute-based system was introduced. Elements marked with data-tween* are automatically registered during application bootstrap and bound to predefined animation behaviors.
This created a unified animation pipeline where all motion effects are derived from DOM metadata rather than scattered imperative logic.
Additionally, a set of utility functions was introduced to handle explicit animation states (enter/exit transitions), enabling fine-grained control where needed.
3. Dynamic Animation Binding for Runtime-Added Content
A non-trivial issue emerged from dynamically injected DOM content, where newly mounted elements bypassed the initial animation registration phase.
To solve this, a mutation-aware layer AnimatedItemsObserver was introduced. It observes DOM changes and automatically registers newly added elements into the animation system, ensuring consistent motion behavior across both initial and asynchronously rendered content.
This eliminated animation desynchronization between statically rendered and dynamically injected UI components, keeping the motion system consistent throughout the entire user journey.
Architecture & Technical Decisions
1. Astro + Web Components — Island-Based Architecture for Performance-First Landing Pages
The project was built using Astro combined with native Web Components to optimize for performance and reduce client-side overhead.
Astro enables a mostly server-rendered architecture where JavaScript is shipped only for interactive islands, avoiding the cost of a full SPA runtime.
Web Components were used as an additional encapsulation layer for interactive modules, providing strong isolation of logic, styles, and lifecycle without relying on a framework-specific runtime.
This combination allowed: - minimal initial JavaScript payload
- fine-grained hydration control
- strong component isolation for complex animated sections
- predictable performance on low-end devices
The result is a hybrid architecture optimized for content-heavy, motion-driven landing experiences where full SPA hydration would be unnecessary overhead.
2. nanostores — Reactive Atomic State Model
For cross-component state management, nanostores was chosen as a lightweight reactive primitive system based on atomic stores.
Instead of a centralized global store or prop drilling, the application state is split into small reactive units (atoms), each representing a specific domain (e.g. scroll progress, active section, animation state).
This model allows independent UI parts to react only to the state slices they depend on, reducing unnecessary re-renders and improving predictability in animation-heavy contexts.
It also works naturally with scroll-driven interactions, where multiple subsystems need to react to the same underlying signal (e.g. scroll progress) without tight coupling.
3. Anime.js — Lightweight Animation Engine for Fine-Grained Control
Anime.js was used as the primary animation engine due to its lightweight footprint and flexible API for timeline-based and property-based animations.
Compared to heavier alternatives, it provides sufficient control for complex motion sequences while keeping implementation overhead low.
The decision was driven by the need for: - precise control over animation timing
- integration with custom scroll-driven progress
- reduced runtime overhead in performance-sensitive sections
This made it suitable for a system where animations are not isolated effects, but part of a continuous narrative flow.
4. Tailwind CSS + Custom Styles — Hybrid Design System Approach
Tailwind CSS was used as the base styling system to accelerate UI development and enforce consistency across layout primitives.
However, for motion-heavy and highly visual sections, custom CSS was introduced where utility classes were insufficient (e.g. complex layering, scroll-driven transforms, SVG interactions).
This hybrid approach allowed: - rapid layout iteration using utility-first styling
- controlled escape into custom styles for advanced visual effects
- consistent design tokens while preserving flexibility for experimental sections
Locomotive Scroll was used as the foundation for scroll handling, providing smooth scrolling behavior and a centralized scroll abstraction layer. On top of it, a custom extension layer was built to expose normalized scroll progress to the application.
A key enhancement was a progress-dispatching mechanism that broadcasts scroll state changes globally, enabling components to subscribe via data-* attributes and react declaratively.
This enabled: - decoupling scroll logic from individual components
- fine-grained control over animation triggers
- consistent behavior across different sections
- predictable synchronization between scroll position and animation timelines
Under the hood, scroll updates are mapped into a normalized progress value and propagated through the system as a shared reactive signal.
Trade-offs
Why Not React (or Full SPA Architecture)
A full React SPA was intentionally avoided to prevent unnecessary runtime overhead and hydration cost for a primarily content-driven, scroll-heavy experience.
In this type of product, the UI is not application-centric but narrative-centric, meaning most of the interface is static or progressively enhanced rather than constantly state-driven.
Using Astro + Web Components instead of React SPA allowed: - significantly smaller JavaScript bundle on initial load
- no full virtual DOM reconciliation layer
- reduced hydration complexity for mostly static sections
- more predictable performance on low-end devices
React was still implicitly replaced where state-driven complexity was required (via Nanostores + Web Components), but without introducing a global rendering runtime.
Trade-off: - less ergonomic state/UI composition compared to React
- more manual orchestration of reactivity and DOM interactions
Why Not GSAP
GSAP is a powerful industry-standard animation library, but it was intentionally not used as the primary animation engine to reduce abstraction overhead and keep animation control closer to the application logic.
Instead, Anime.js was chosen for its: - lighter footprint
- simpler mental model for timeline + property animations
- easier integration with custom scroll progress system
GSAP excels in complex timeline orchestration and plugin ecosystem, but in this case most animations were tightly coupled with scroll progress rather than independent timelines. Animation logic needed to be declarative and data-driven through attributes and scroll signals.
Trade-off: - GSAP would have reduced manual orchestration effort
- but introduced a heavier abstraction layer and tighter coupling to its own timeline system
Conclusion
This project demonstrates how a visually rich, scroll-driven landing page can be built without relying on a heavy SPA runtime or oversized animation framework. By combining Astro, Web Components, Locomotive Scroll, Nanostores, and Anime.js, the implementation keeps the experience modular, performant, and maintainable.
The result is a motion-focused frontend architecture that balances cinematic interaction design with practical engineering concerns: small client-side overhead, reusable animation primitives, centralized scroll orchestration, and predictable behavior across responsive layouts.