Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/fortune-seeker/llms.txt

Use this file to discover all available pages before exploring further.

The Case Studies page presents in-depth project write-ups using a two-act structure: first, two tarot cards are displayed at opposing angles like a partially dealt hand; then, clicking one triggers a transition where both cards blur and converge to center before the selected study’s full content panel expands into view. The experience mirrors the ritual of choosing a card for a deep reading — the moment of selection is deliberate and theatrical.

Overview

The Case Studies page lives at route /case-studies and is titled “The Deep Reading”. Its opening quote — “For those who seek the intricate details behind the magic.” — signals that this page rewards curiosity. State is managed by two useState values: selectedId (the ID of the chosen study, or null) and isTransitioning (a boolean that triggers the blur-and-converge animation before the content panel appears). The 1.5 s transition delay between click and panel reveal gives the blur animation time to complete.
The blur transition uses isTransitioning as a bridge state. When a card is clicked, isTransitioning is set to true immediately (triggering the blur), and after 1500 ms selectedId is set and isTransitioning is reset to false, revealing the content panel.

Layout & Components

Angled TarotCards (×2)

The two study cards are positioned absolutely within a 500px tall flex container. Card 0 animates to x: -150, rotate: -10 and card 1 to x: 150, rotate: 10 when in their resting state. When isTransitioning is true, both snap to x: 0, rotate: 0, scale: 0.8 with a blur filter (applied via animate on isFlipped).

Full-screen content panel

After the transition, a motion.div panel slides in with initial: { opacity: 0, scale: 0.9, y: 50 } and animate: { opacity: 1, scale: 1, y: 0 }. It contains the study title, subtitle, a drop-cap body paragraph, and a close button (X) that resets selectedId to null.

AnimatePresence

The content panel is wrapped in AnimatePresence so it exits cleanly when dismissed. The exit animation (opacity: 0, scale: 0.9, y: 50) mirrors the entrance, giving the panel a balanced open/close feel.

BookOpen icon

A BookOpen icon (from Lucide React) appears both on the card face (beneath the study title) and as a section header inside the content panel. It reinforces the “reading” metaphor and provides a consistent visual anchor.

Data Structure

Case studies are defined as an array of objects. The content field holds the full write-up displayed in the panel:
const caseStudies = [
  {
    id: "migration",
    title: "The Great Migration",
    subtitle: "From Monolith to Micro-Frontends",
    summary:
      "A tale of breaking a cursed monolith into agile, independent spirits.",
    content:
      "The legacy system was a behemoth, tangled in years of technical debt and dark patterns. The ritual required careful planning. We began by isolating domains, creating boundary wards (APIs), and slowly siphoning power to the new React-based micro-frontends. The result: 60% faster deployments and a happier coven of developers.",
  },
  {
    id: "performance",
    title: "Summoning Speed",
    subtitle: "Core Web Vitals Optimization",
    summary:
      "Banishing layout shifts and invoking lightning-fast paints.",
    content:
      "The spirits of the web (Google bots) were displeased with our sluggish performance. I led an expedition into the depths of our bundle size. By implementing aggressive code-splitting, optimizing image incantations, and deferring non-critical scripts, we achieved a perfect 100 Lighthouse score. The prophecy of page one ranking was fulfilled.",
  },
];

Animations

Each card’s wrapping motion.div uses animate with a spring transition (stiffness: undefined, type: "spring"). When isTransitioning is false, the cards hold their offset positions (x: ±150, rotate: ±10). When isTransitioning becomes true, both animate to x: 0, y: 0, rotate: 0, scale: 0.8, zIndex: 10, converging to the center like two cards being placed face-down.
The isFlipped prop of each TarotCard is set to !isTransitioning. When isTransitioning is true, isFlipped becomes false, causing the card to show its back face (a blank div). This, combined with the scale-down animation, creates the blur/fade effect without needing an explicit CSS filter: blur().
The content panel uses initial: { opacity: 0, scale: 0.9, y: 50 }, animate: { opacity: 1, scale: 1, y: 0 }, and exit: { opacity: 0, scale: 0.9, y: 50 }. The transition duration is 0.6 s. The panel also features a shine sweep on first render — a motion.div gradient that crosses from -100% to 100% once.
The first paragraph in the content panel uses CSS first-letter pseudo-element styles: font-size: 5xl, font-serif, text-gold, float-left, mr-3. This gives the write-up an illuminated manuscript feel consistent with the portfolio’s divination theme.

Customization

1

Add a third case study

Push a new object into caseStudies with a unique id, title, subtitle, summary, and content. The layout currently uses absolute positioning for two cards; for a third card, switch to a flex or grid layout with gap spacing, and remove the absolute x offsets, replacing them with rotation-only animations (rotate: ±8).
2

Write richer content

The content field renders as a single paragraph with a drop-cap. To add sections (problem, solution, results), extend the data structure with additional fields such as problem, solution, and outcome, and map them to separate p elements inside the panel.
3

Link to an external write-up

Add a url field to each study object and render a “Read the Full Chronicle” link button at the bottom of the content panel. Use target="_blank" rel="noopener noreferrer" for external links.
4

Adjust transition delay

The 1500 ms delay in setTimeout controls how long the blur animation plays before the content panel appears. Shorten to 800 ms for a snappier experience, or extend to 2000 ms for a more ceremonial pause.
The card face shows a “Click to divine details” pulsing prompt at the bottom of each card’s frontContent. If you want to remove this affordance for a more minimal look, simply delete that motion.div from the frontContent JSX. The cards remain clickable regardless.

Build docs developers (and LLMs) love