Skip to main content
The Education component displays academic history in a visually engaging timeline format with animated cards and a flowing vertical progress line.

Overview

Key features:
  • Vertical timeline with animated gradient line
  • Alternating left/right card layout on desktop
  • Animated dots marking each milestone
  • Responsive mobile layout with all cards on one side
  • GPA badges and duration indicators
  • Smooth scroll-based animations

Component Structure

Education.jsx
import React from 'react';
import { motion } from 'framer-motion';
import { styles } from '@/styles';
import { SectionWrapper } from '@/hooks';

const EducationCard = ({ degree, institution, duration, gpa, description, index, isLeft }) => (
  <motion.div
    initial={{ opacity: 0, x: isLeft ? -50 : 50 }}
    whileInView={{ opacity: 1, x: 0 }}
    viewport={{ once: true }}
    transition={{ duration: 0.6, delay: index * 0.2 }}
    className="relative"
  >
    {/* Card content */}
  </motion.div>
);

function Education() {
  const educationData = [
    {
      degree: "Bachelor of Technology in Computer Science Engineering",
      institution: "Techno Bengal Institute of Technology",
      duration: "2023 - 2027 (Expected)",
      gpa: "7.67 GPA",
      description: "Currently in the third year..."
    },
    // More education entries...
  ];

  return (
    <div className="px-4 sm:px-6 lg:px-8 bg-white/50 
      dark:bg-zinc-950/30 backdrop-blur-sm py-16">
      {/* Timeline */}
    </div>
  );
}

export default SectionWrapper(Education, "education");

Animated Timeline Line

The vertical line features a flowing gradient animation:
{/* Desktop Timeline */}
<div className="absolute left-1/2 transform -translate-x-1/2 
  top-0 bottom-0 w-1.5 hidden md:block">
  <motion.div 
    initial={{ scaleY: 0 }}
    whileInView={{ scaleY: 1 }}
    viewport={{ once: true }}
    transition={{ duration: 1.5, ease: "easeOut" }}
    className="relative w-full h-full rounded-full origin-top overflow-hidden"
  >
    <div className="absolute inset-0 bg-gradient-to-b 
      from-navy-500 via-navy-600 to-transparent 
      dark:from-blue-500 dark:via-blue-600 dark:to-transparent" />
    <motion.div
      animate={{ y: ['100%', '-100%'] }}
      transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
      className="absolute inset-0 bg-gradient-to-b 
        from-transparent via-white/30 to-transparent"
    />
  </motion.div>
</div>
The flowing gradient creates a sense of progress and movement through academic history.

Education Card Component

const EducationCard = ({ degree, institution, duration, gpa, description, index, isLeft }) => (
  <motion.div
    initial={{ opacity: 0, x: isLeft ? -50 : 50 }}
    whileInView={{ opacity: 1, x: 0 }}
    viewport={{ once: true }}
    transition={{ duration: 0.6, delay: index * 0.2 }}
    className="relative"
  >
    <div className="bg-white/95 dark:bg-zinc-900/20 backdrop-blur-sm 
      p-6 rounded-xl border border-navy-200 dark:border-zinc-600 
      hover:border-navy-400 dark:hover:border-zinc-500 
      transition-all duration-300 shadow-lg hover:shadow-xl group">
      
      {/* Timeline Dot */}
      <motion.div
        initial={{ scale: 0 }}
        whileInView={{ scale: 1 }}
        viewport={{ once: true }}
        transition={{ delay: index * 0.2, duration: 0.4 }}
        className={`absolute top-8 ${isLeft ? '-right-3' : '-left-3'} 
          md:block hidden`}
      >
        <div className="relative">
          <div className="w-6 h-6 bg-navy-600 dark:bg-blue-500 
            rounded-full border-4 border-slate-50 dark:border-slate-900 
            shadow-lg" />
          <div className="absolute inset-0 bg-navy-600 dark:bg-blue-500 
            rounded-full animate-ping opacity-20" />
        </div>
      </motion.div>

      <h3 className="text-navy-900 dark:text-slate-100 
        text-xl font-bold mb-2">
        {degree}
      </h3>
      <h4 className="text-navy-700 dark:text-slate-300 
        text-lg font-semibold mb-3">
        {institution}
      </h4>
      
      <div className="flex items-center gap-4 mb-3">
        <p className="text-navy-600 dark:text-slate-400 
          text-sm font-medium">
          {duration}
        </p>
        {gpa && (
          <span className="bg-navy-100 dark:bg-slate-700 
            text-navy-800 dark:text-slate-200 
            px-3 py-1 rounded-full text-sm font-semibold">
            {gpa}
          </span>
        )}
      </div>
      
      <p className="text-navy-700 dark:text-slate-300 
        text-base leading-relaxed">
        {description}
      </p>
    </div>
  </motion.div>
);

Alternating Layout (Desktop)

<div className="space-y-12 relative">
  {educationData.map((edu, index) => {
    const isLeft = index % 2 === 0;
    return (
      <div key={index} className="relative">
        {/* Desktop Layout - Alternating */}
        <div className="hidden md:grid md:grid-cols-2 md:gap-16 items-center">
          {isLeft ? (
            <>
              <EducationCard index={index} {...edu} isLeft={true} />
              <div></div>
            </>
          ) : (
            <>
              <div></div>
              <EducationCard index={index} {...edu} isLeft={false} />
            </>
          )}
        </div>

        {/* Mobile Layout - All on right */}
        <div className="md:hidden pl-12">
          <EducationCard index={index} {...edu} isLeft={false} />
          {/* Mobile Dot */}
          <motion.div
            initial={{ scale: 0 }}
            whileInView={{ scale: 1 }}
            viewport={{ once: true }}
            transition={{ delay: index * 0.2, duration: 0.4 }}
            className="absolute left-4 top-8 transform -translate-x-1/2"
          >
            <div className="w-4 h-4 bg-navy-600 dark:bg-blue-500 
              rounded-full border-2 border-slate-50 dark:border-slate-900" />
          </motion.div>
        </div>
      </div>
    );
  })}
</div>
Cards alternate between left and right sides with center timeline

Timeline Dots

Animated dots mark each milestone:
<motion.div
  initial={{ scale: 0 }}
  whileInView={{ scale: 1 }}
  viewport={{ once: true }}
  transition={{ delay: index * 0.2, duration: 0.4 }}
  className={`absolute top-8 ${isLeft ? '-right-3' : '-left-3'}`}
>
  <div className="relative">
    {/* Main dot */}
    <div className="w-6 h-6 bg-navy-600 dark:bg-blue-500 
      rounded-full border-4 border-slate-50 dark:border-slate-900 
      shadow-lg" />
    {/* Pulse animation */}
    <div className="absolute inset-0 bg-navy-600 dark:bg-blue-500 
      rounded-full animate-ping opacity-20" />
  </div>
</motion.div>
The animate-ping class creates a subtle pulsing effect to draw attention.

Education Data Structure

const educationData = [
  {
    degree: "Bachelor of Technology in Computer Science Engineering",
    institution: "Techno Bengal Institute of Technology",
    duration: "2023 - 2027 (Expected)",
    gpa: "7.67 GPA",
    year: "2023",
    description: "Currently in the third year of B.Tech..."
  },
  {
    degree: "Higher Secondary (10+2 Grades)",
    institution: "Bidhannagar Govt. High School",
    duration: "2021 - 2023",
    gpa: "84.2%",
    year: "2021",
    description: "Completed Higher Secondary examination..."
  },
  {
    degree: "Secondary (10th Grade)",
    institution: "Bidhannagar Municipal School",
    duration: "2021",
    gpa: "91.8%",
    year: "2021",
    description: "Completed Madhyamik examination..."
  }
];

Animation Timing

Timeline Line

1.5s duration Scales from top scaleY: 0 → 1

Cards

0.6s duration 0.2s delay per card Slide from sides

Dots

0.4s duration 0.2s delay per dot Scale from center

GPA Badge

Optional GPA display with pill styling:
{gpa && (
  <span className="bg-navy-100 dark:bg-slate-700 
    text-navy-800 dark:text-slate-200 
    px-3 py-1 rounded-full text-sm font-semibold">
    {gpa}
  </span>
)}

Mobile Timeline

Simplified for mobile devices:
{/* Mobile timeline line */}
<div className="absolute left-4 top-0 bottom-0 w-0.5 md:hidden 
  bg-gradient-to-b from-navy-500 to-navy-400 
  dark:from-blue-500 dark:to-blue-400" />

Responsive Behavior

- Two-column grid
- Cards alternate sides
- Center timeline (1.5px)
- Dots positioned on card edges

Hover Effects

hover:border-navy-400 dark:hover:border-zinc-500
hover:shadow-xl
The entire card responds to hover with:
  • Border color change
  • Shadow enhancement
  • Smooth 300ms transition

Build docs developers (and LLMs) love