Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/windows-xp-developer/llms.txt

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

The Case Studies page transforms process documentation into a tactile, nostalgic interaction. Each case study is presented as a physical CD jewel case — closed on the front with a gradient cover and project title, and open on the back with a Tracklist of project phases. Clicking a card triggers a 3D rotateY flip animation driven by Framer Motion, exactly like opening a real CD case from the left hinge. The metaphor maps perfectly: a project’s phases are its tracks, and the case is the deliverable.

Visual overview

Two cards sit in a two-column centred grid, each 300 px tall. The front face has a bold gradient fill — blue-to-cyan for Project Aqua, purple-to-pink for E-Commerce 2.0 — with the project title and subtitle centred in white text and a CD disc icon above. The card has a thick left border (12 px, dark grey) simulating the jewel-case spine. When clicked, the card rotates −160° around its left edge (the hinge), revealing the inside back face: a frosted slate background with a “Tracklist” heading and an ordered list of project phases. A second click rotates it back.

Project Aqua

Subtitle: “Redesigning a legacy dashboard.” Tracks: Research & Discovery → Wireframing → High-Fidelity Gloss → Implementation. Gradient: from-blue-400 to-cyan-300.

E-Commerce 2.0

Subtitle: “Increasing conversion with delight.” Tracks: User Flow Audit → Checkout Redesign → Micro-interactions → A/B Testing. Gradient: from-purple-400 to-pink-300.

Component structure

The flip is implemented with a single motion.div wrapping both faces. The CSS class preserve-3d and the backface-hidden class on each face handle the depth illusion; Framer Motion only animates the rotateY value. A click on any open card closes it — only one card can be open at a time, controlled by a shared openId state.
// Framer Motion 3D card flip on click
import { motion } from 'framer-motion';
import { useState } from 'react';

const caseStudies = [
  {
    id:       1,
    title:    'Project Aqua',
    subtitle: 'Redesigning a legacy dashboard',
    tracks: [
      'Research & Discovery',
      'Wireframing',
      'High-Fidelity Gloss',
      'Implementation',
    ],
    color: 'from-blue-400 to-cyan-300',
  },
  {
    id:       2,
    title:    'E-Commerce 2.0',
    subtitle: 'Increasing conversion with delight',
    tracks: [
      'User Flow Audit',
      'Checkout Redesign',
      'Micro-interactions',
      'A/B Testing',
    ],
    color: 'from-purple-400 to-pink-300',
  },
];

export default function CaseStudies() {
  const [openId, setOpenId] = useState(null);

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-12 perspective-[1000px]">
      {caseStudies.map((project) => (
        <div
          key={project.id}
          className="relative h-[300px] cursor-pointer"
          onClick={() => setOpenId(openId === project.id ? null : project.id)}
        >
          <motion.div
            className="w-full h-full preserve-3d"
            animate={{ rotateY: openId === project.id ? -160 : 0 }}
            transition={{ duration: 0.8, type: 'spring', stiffness: 50 }}
            style={{ transformStyle: 'preserve-3d', transformOrigin: 'left center' }}
          >
            {/* Front face: gradient cover */}
            <div className="absolute inset-0 backface-hidden rounded-r-xl border-l-[12px] border-gray-800 bg-white overflow-hidden">
              <div className={`absolute inset-0 bg-gradient-to-br ${project.color} flex flex-col items-center justify-center text-center p-6`}>
                <h3>{project.title}</h3>
                <p>{project.subtitle}</p>
              </div>
            </div>

            {/* Back face: tracklist — rotated 180° so it faces the viewer when open */}
            <div
              className="absolute inset-0 backface-hidden rounded-l-xl border-r-[12px] border-gray-800 bg-slate-100 p-6"
              style={{ transform: 'rotateY(180deg)' }}
            >
              <h4>Tracklist</h4>
              <ul>
                {project.tracks.map((track) => (
                  <li key={track}>{track}</li>
                ))}
              </ul>
            </div>
          </motion.div>
        </div>
      ))}
    </div>
  );
}

The data array

Both case studies are defined in a caseStudies array in assets/main.js. Locate it by searching for "Project Aqua":
// In assets/main.js — locate the caseStudies array (search for "Project Aqua")
const caseStudies = [
  {
    id:       1,
    title:    'Project Aqua',
    subtitle: 'Redesigning a legacy dashboard',
    tracks: [
      'Research & Discovery',
      'Wireframing',
      'High-Fidelity Gloss',
      'Implementation',
    ],
    color: 'from-blue-400 to-cyan-300',   // Tailwind gradient classes for the front face
  },
  {
    id:       2,
    title:    'E-Commerce 2.0',
    subtitle: 'Increasing conversion with delight',
    tracks: [
      'User Flow Audit',
      'Checkout Redesign',
      'Micro-interactions',
      'A/B Testing',
    ],
    color: 'from-purple-400 to-pink-300',
  },
];
To add a third case study, append a new object to the array. The grid will wrap it to a second row automatically.
Keep tracks arrays to four to six items. The back face has limited space, and more than six items will overflow the visible card area at the current 300 px card height.

Key interactions

InteractionBehaviour
Click card (closed)openId is set to the card’s id; card animates rotateY: 0 → -160 over 0.8 s with spring stiffness 50
Click card (open)openId is set to null; card animates rotateY: -160 → 0 — closes back to cover
Click other cardAny open card closes as openId changes to the new card’s id
Spring stiffnessLow stiffness (50) gives a slow, weighty flip that feels like a physical object
transformOrigin: 'left center'Anchors rotation to the left edge — the “hinge” — so the card opens like a real jewel case
Backface hiddenBoth faces use the backface-hidden CSS class so neither face bleeds through mid-rotation
The perspective-[1000px] Tailwind class is on the parent grid container, not on each individual card wrapper. This means the 3D depth is evaluated from a shared vanishing point, which makes the two cards appear to exist in the same physical space rather than each having an independent perspective.

Flip mechanics — step by step

1

Initial state

Card mounts with openId !== project.id. rotateY is 0, so the front face is fully visible and the back face is hidden (rotated 180° away from the viewer).
2

Click fires

onClick calls setOpenId(project.id). Framer Motion begins animating rotateY from 0 toward -160.
3

Mid-rotation (around -90°)

Both faces are edge-on to the viewer. The backface-hidden class means neither face is visible — the card appears as a thin edge, like a CD case caught in the act of opening.
4

Open state (-160°)

The back face is now facing the viewer. The spring settles and the card holds at -160°, displaying the Tracklist.
5

Close

A second click sets openId to null, reversing the animation back to , hiding the tracklist and showing the cover again.

Build docs developers (and LLMs) love