Dev Nexus layered its motion system into four distinct tiers, each served by a different Framer Motion primitive. Understanding which tier handles what makes it straightforward to adjust timing, add new animations, or strip motion for accessibility.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev-nexus/llms.txt
Use this file to discover all available pages before exploring further.
| Layer | Mechanism | Scope |
|---|---|---|
| Page-level | PageTransition wrapper + AnimatePresence | Entire route entering / exiting |
| Element-level | motion.div with initial / animate | Individual cards, headings, sections |
| Continuous | animate={{ rotate: 360 }} loop | Sigil rings, ambient background elements |
| Interactive | layout prop + AnimatePresence | VialCard expand/collapse |
Page Transitions
Every route is wrapped in a sharedPageTransition component. It lives at the router level so the entrance animation fires once per navigation, not once per component mount:
AnimatePresence in the router must use mode="wait" so the exiting page fully fades out before the entering page begins its animation. Without it, both pages are visible simultaneously during the crossfade:
Scroll-Triggered Reveals
Sections and cards usewhileInView to animate in as the user scrolls down. The viewport config controls when the trigger fires:
once: true— the animation fires exactly once. Scrolling back up and then down again does not re-trigger it. Remove this flag only if you want the element to animate every time it enters the viewport.margin: '-100px'— the element starts animating when it is 100px inside the viewport boundary, giving the reveal a comfortable lead so it never appears to pop in at the bottom edge of the screen.
Staggered Entrance Delays
Page sections that contain multiple sibling elements — skill bars, project cards, nav links — stagger theirtransition.delay values to create a cascading entrance rather than everything appearing at once:
0.5, 0.8, 1.0, 1.1, 1.2 to give the designer precise control over the cascade rhythm rather than an even mechanical step.
Continuous Rotation
The hero sigil’s outer decorative ring spins indefinitely at a slow, meditative pace. Theease: 'linear' value is critical — any other easing would produce a visible acceleration/deceleration on every revolution:
duration to change the rotation speed. A value of 20 is noticeably brisk; 60 is almost imperceptibly slow. Inner rings or counter-rotating elements use negative rotate targets:
VialCard Expand / Collapse
The Projects page VialCard uses two Framer Motion features together: thelayout prop for smooth height changes on the card container, and AnimatePresence to animate the expanded content in and out of the DOM:
height: 0 → height: 'auto' tween is handled natively by Framer Motion. In vanilla CSS, animating to height: auto is not possible — this is one of the primary reasons the component reaches for Framer Motion rather than a CSS transition.
CSS Float Animation
Bubble particles inside VialCards use a pure-CSS keyframe defined inmain.css rather than Framer Motion, because they run continuously and independently of any React state:
animation-delay values in JSX to prevent all bubbles from moving in perfect unison:
Tips and Accessibility
viewport={{ once: true }} prevents scroll-triggered animations from re-firing when the user scrolls back up through already-seen content. This is the default preference throughout Dev Nexus because re-triggering feels unpolished in a portfolio context.
mode="wait" on AnimatePresence at the router level ensures the exit animation of the departing page completes before the entering page begins. Without it, two pages render simultaneously during the transition and the layout shifts unpredictably.