Skip to main content

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.

The MissionLogTimeline component presents your work history as a mission log — a vertical axis represents the passage of time, and a luminous gradient line grows along that axis as the user scrolls down the page. Each role appears as a card that animates into view, alternating left and right on desktop layouts for visual rhythm. The effect mirrors a mission debrief being printed out in real time.
The scrolling line animation uses Framer Motion’s useScroll and useTransform hooks tied to the component’s own section ref — not the window scroll position. This means the animation is scoped to when the timeline section is within the viewport, not the overall page scroll depth.

Work History Data Shape

All entries are stored in a static array (We in the compiled source). Each entry must conform to this object shape:
{
  year: string,     // Date range, e.g. "2023 - Present"
  title: string,    // Job title, e.g. "Senior Frontend Architect"
  company: string,  // Employer name, e.g. "Stellar Dynamics"
  desc: string      // One-to-two sentence role description
}

Pre-configured Entries

The timeline ships with three work history entries:

Senior Frontend Architect — Stellar Dynamics

Period: 2023 – PresentLeading a crew of 5 developers building the next-generation orbital tracking interface. Reduced load times by 40% through strategic code-splitting and WebGL optimizations.

UI Engineer — Nebula Corp

Period: 2020 – 2023Developed the core component library used across 12 different internal tools. Implemented complex data visualization dashboards for telemetry data.

Web Developer — Void Technologies

Period: 2018 – 2020Maintained and upgraded legacy systems. Migrated monolithic architecture to micro-frontends, improving deployment velocity.

Scroll-Driven Line Animation

The vertical timeline line sits in an absolute container pinned at left-1/2 on desktop (and left-[27px] on mobile, aligned with the node dots). It consists of two layers:
  1. Background track — a static w-full h-full bg-space-800 div that fills the full height of the section.
  2. Animated fill — a Framer Motion div whose height CSS property is driven by scroll position.
The scroll progress is computed using Framer Motion’s useScroll hook with a scoped target ref:
const sectionRef = useRef(null);

const { scrollYProgress } = useScroll({
  target: sectionRef,
  offset: ["start end", "end start"],
});
The raw scrollYProgress value (0 → 1) is then remapped with useTransform to compress the active range to the middle portion of the scroll:
// Smooth progress: only reacts between 20% and 80% of the section's scroll range
const lineProgress = useTransform(scrollYProgress, [0.2, 0.8], [0, 1]);
This lineProgress value is then applied to the fill bar’s height style as a percentage string:
<motion.div
  className="absolute top-0 left-0 w-full bg-gradient-to-b from-aurora-teal via-aurora-pink to-transparent"
  style={{ height: useTransform(lineProgress, [0, 1], ["0%", "100%"]) }}
/>
The gradient from-aurora-teal via-aurora-pink to-transparent is intentional — the transparent tail means the “leading edge” of the growing line always has a soft fade, reinforcing the sense that the line is being drawn live rather than revealed with a hard cutoff.

Card Entrance Animations

Each <motion.div> card uses whileInView to animate in from below as it enters the viewport:
<motion.div
  initial={{ opacity: 0, y: 50 }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, margin: "-100px" }}
  transition={{ duration: 0.6 }}
>
The margin: "-100px" on the viewport option means the animation triggers 100px before the card’s top edge fully enters the viewport — cards begin fading in slightly before they are fully visible, creating a smoother reveal. once: true ensures each card only animates once per page load.

Alternating Layout

On md and larger screens, entries alternate between left-aligned and right-aligned to create a classic timeline chevron layout. The alternation is determined by the array index:
const isEven = index % 2 === 0;

// Even entries: row is reversed so the card appears on the RIGHT
// Odd entries: card appears on the LEFT
className={`flex flex-col md:flex-row items-start gap-8 ${isEven ? "md:flex-row-reverse" : ""}`}
Each row uses flex-row with three children:
  • The content card (flex-1) — floats left or right depending on flex-row-reverse.
  • The timeline node (flex-shrink-0 w-14 h-14) — always at the centre of the row, aligned with the vertical line.
  • A spacer div (hidden md:block flex-1) — occupies the opposite half, keeping the node centred.
On mobile (flex-col), all cards stack in a single column and the node appears below each card.

Timeline Node

Each node is a circular rounded-full bg-space-950 border-2 border-aurora-teal div. At its centre, a smaller pulsing dot creates a beacon effect:
<div className="w-4 h-4 rounded-full bg-aurora-light shadow-[0_0_10px_rgba(204,251,241,0.8)] animate-pulse" />
The shadow uses the aurora-light color (rgba(204,251,241,0.8)) as a glow cast, and Tailwind’s built-in animate-pulse class provides the breathing animation without any Framer Motion configuration.

Card Anatomy

Each glass-panel card contains four pieces of information:
ElementClassesContent
Year rangefont-mono text-aurora-mint text-sme.g. “2023 - Present”
Job titlefont-sans text-2xl font-bold text-slate-100e.g. “Senior Frontend Architect”
Company namefont-serif italic text-slate-400e.g. “Stellar Dynamics”
Descriptiontext-slate-300 text-sm leading-relaxedFree-text role summary
The card’s text alignment switches with the alternating layout: md:text-right on even (right-side) cards, md:text-left on odd (left-side) cards.

Adding New Timeline Entries

1

Open the data array

Find the We array in components/MissionLogTimeline.js. Entries are ordered newest-first (top of the array = top of the timeline).
2

Prepend a new role

Insert a new object at the beginning of the array for a current or most recent position:
{
  year: "2025 - Present",
  title: "Principal Engineer",
  company: "Aurora Systems",
  desc: "Architecting the next-generation distributed rendering pipeline for real-time 3D web experiences."
}
3

Check the alternating layout

Adding an entry at the front shifts the even/odd index of all subsequent entries, flipping their left/right alignment. Review the rendered timeline to ensure the visual rhythm still looks balanced.
4

Adjust scroll offsets if needed

With more entries, the section becomes taller. The useTransform input range [0.2, 0.8] may need to be adjusted (e.g. [0.1, 0.9]) to ensure the line fills fully by the time the last card is in view.
The fill bar uses the Tailwind class bg-gradient-to-b from-aurora-teal via-aurora-pink to-transparent. To change it, edit the className on the motion.div fill bar inside MissionLogTimeline.js.Some alternatives:
from-aurora-mint via-aurora-teal to-transparent   — cooler, all-teal tone
from-aurora-pink via-indigo-500 to-transparent    — pink-to-indigo gradient
from-white via-aurora-light to-transparent        — high-contrast white beam
Always end with to-transparent to preserve the soft leading-edge fade effect.
Do not remove the ref prop on the outer section element. The useScroll hook’s target option depends on this ref to scope its measurements to the timeline section. Without it, scrollYProgress will fall back to window-level scroll, causing the line to animate at the wrong time relative to the section’s position on the page.

Build docs developers (and LLMs) love