The dev.void portfolio pairs three carefully chosen typefaces with a cohesive Framer Motion animation language to reinforce its space-exploration narrative. Space Grotesk gives the site its crisp, geometric backbone; Cormorant Garamond adds editorial grace to quoted passages; and JetBrains Mono grounds all technical labels in authentic monospace authority. On top of typography, Framer Motion drives six distinct animation patterns — from page-level blur transitions to spring-based cursor tracking — each chosen to make interactions feel weightless and cinematic without sacrificing performance.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev.void/llms.txt
Use this file to discover all available pages before exploring further.
Typography System
Font Families
All three families are loaded via a single Google Fonts@import at the very top of assets/main.css:
Space Grotesk
Tailwind class:
font-sansWeights loaded: 300, 400, 500, 600, 700Applied globally to html, body, and all h1–h6 elements. This is the dominant typeface for every heading, paragraph, nav item, and button label. Headings additionally receive letter-spacing: -0.025em (Tailwind tracking-tight) to tighten the naturally wide geometric letterforms at large sizes.Cormorant Garamond
Tailwind class:
font-serifWeights loaded: 400, 600 (normal + italic)Used sparingly for font-serif italic flourishes — specifically the testimonial quote bodies in AsteroidQuotes (font-serif italic text-lg text-slate-300). The contrast between the spindly serifs and the surrounding sans-serif text creates a deliberate editorial tension, like a mission log entry written by hand.JetBrains Mono
Tailwind class:
font-monoWeights loaded: 100–800 (variable)Applied globally to code, kbd, samp, and pre elements, and explicitly via font-mono on UI labels, badges, role tags, tech-stack chips, and section identifiers. Its even, spaced-out letterforms read well at very small sizes (e.g., text-[10px] uppercase tracking-widest on role labels).Base Styles from main.css
How to Change Fonts
Choose replacement families on Google Fonts
Visit fonts.google.com and select your alternatives. Construct a new
@import URL that includes all the weight and style variants you need. For example, to swap Space Grotesk for Inter:Replace the @import at the top of main.css
Open
assets/main.css and replace only the first line (the @import statement). Do not touch anything else in this line — it is the only external network dependency the CSS file has.Update font-family references in main.css
Search for every
Space Grotesk occurrence in main.css and replace with your new family name. There are four occurrences: the html rule, the body rule, the headings rule, and the .font-sans utility class.Cormorant Garamond and JetBrains Mono are intentionally narrow in scope. If you swap them out, audit
font-serif italic usages in AsteroidQuotes.js and font-mono usages on badge/label elements to ensure the replacement typefaces look correct at those specific sizes and weights.Reduced-Motion Accessibility
main.css includes a prefers-reduced-motion media query that respects the operating system’s accessibility setting. When a user has enabled “Reduce Motion” in their OS preferences, all CSS animation and transition durations are forced to near-zero, and the aurora SVG filter is removed:
CometCursor component already handles a related case — it returns null on coarse-pointer (touch) devices, avoiding the cursor entirely where it would be meaningless.
Framer Motion Animation Patterns
All six patterns below are used in production components. Each is documented with the exact prop values taken from the compiled component source.1. Page Transition — PageWrapper
The outermost route wrapper fades, lifts, and blurs every page in and out. The custom cubic-bezier easing [0.22, 1, 0.36, 1] is an “expo out” curve that produces an energetic snap-in feel.
2. Scroll-Triggered Entrance — MissionLogTimeline, FlightRecorderCase
Section content enters from below as the user scrolls it into the viewport. The once: true option means the animation only fires on the first reveal — elements do not re-animate when the user scrolls back up. The margin: '-100px' offset triggers the animation slightly before the element fully enters the visible area, so content is already mid-transition when the eye reaches it.
3. Spring Cursor — CometCursor
The custom cursor uses useMotionValue to track raw mouse coordinates and useSpring to interpolate the cursor element’s position with physical spring damping. The result is a cursor that lags slightly behind the pointer and overshoots on fast movements — like a comet trailing its nucleus.
x/y spring values drive both the inner dot and the outer ring simultaneously, keeping them physically linked while the scale transforms diverge on click.
4. Continuous Rotation — OrbitingSatellites
Orbital rings and satellites use an infinite, perfectly linear rotation driven by Framer Motion. The ease: 'linear' setting is critical here — any easing curve would cause the ring to visibly speed up and slow down each revolution.
duration values per orbital ring (20 s, 35 s, 50 s from inner to outer) to simulate realistic orbital velocity differences. Note that TelemetryRadar drives its sweep via requestAnimationFrame and React state rather than Framer Motion, so it is not covered by this pattern.
5. Float Animation — AsteroidQuotes
Each testimonial card hovers in a slow, looping float. The y and rotate keyframes are defined as three-point arrays (start → peak → return), which Framer Motion automatically interpolates as a continuous cycle. Each card receives a different yOffset and delay so all three never align — the staggered phase creates organic, living movement.
The
y and rotate sub-transitions have intentionally different durations (6 s vs. 8 s). Because they drift out of phase with each other, the combined motion never feels repetitive even though both are simple three-keyframe loops.6. Shared Layout Expansion — MissionPatchWall
Project patch cards expand into a full modal using Framer Motion’s shared layout system. The layoutId prop tells Framer Motion that the small card and the large modal are the same element at different sizes — it automatically animates the border-radius, position, and dimensions between the two states. AnimatePresence wraps the modal so it can animate out when dismissed.
opacity: 0 → 1) and the card expansion (layoutId morph) run simultaneously but are independently controlled, so the background dims at its own pace while the card reshapes itself.
Animation Quick-Reference
| Pattern | Component(s) | Key Props |
|---|---|---|
| Page blur transition | PageWrapper | filter: blur, ease [0.22,1,0.36,1], 0.6 s |
| Scroll entrance | MissionLogTimeline, FlightRecorderCase | whileInView, viewport.once, 0.6 s |
| Spring cursor | CometCursor | useSpring, stiffness 300, damping 25 |
| Continuous rotation | OrbitingSatellites | rotate: 360, repeat: Infinity, linear, 20/35/50 s |
| Floating idle | AsteroidQuotes | Three-keyframe y/rotate array, staggered delay |
| Shared layout modal | MissionPatchWall | layoutId, AnimatePresence |