Skip to content
TextCodingAny LLM

Animated portfolio website

Build a single-page dark portfolio landing page using React + Vite + Tailwind CSS + TypeScript + GSAP + Framer Motion + hls.js.

760

Prompt

Build a single-page dark portfolio landing page using React + Vite + Tailwind CSS + TypeScript + GSAP + Framer Motion + hls.js.


Global Design System

Fonts

Google Fonts import: Inter (300–700) and Instrument Serif (italic, 400).

  • --font-body: 'Inter', sans-serif → Tailwind font-body
  • --font-display: 'Instrument Serif', serif → Tailwind font-display

CSS Custom Properties (HSL, no hsl() wrapper — Tailwind adds it)

--bg: 0 0% 4%;          /* #0a0a0a */
--surface: 0 0% 8%;     /* #141414 */
--text: 0 0% 96%;       /* #f5f5f5 */
--muted: 0 0% 53%;      /* #888888 */
--stroke: 0 0% 12%;     /* #1f1f1f */
--accent: 0 0% 96%;

Tailwind Custom Colors

bg: "hsl(var(--bg))",
surface: "hsl(var(--surface))",
"text-primary": "hsl(var(--text))",
muted: "hsl(var(--muted))",
stroke: "hsl(var(--stroke))",

Accent Gradient

linear-gradient(90deg, #89AACC 0%, #4E85BF 100%) — used on logo ring, hover borders, progress bars. CSS utility class .accent-gradient.

Custom Animations (in index.css)

  • @keyframes scroll-down — translateY(-100%) → translateY(200%), 1.5s ease-in-out infinite
  • @keyframes role-fade-in — opacity 0 + translateY(8px) → opacity 1 + translateY(0), 0.4s ease-out
  • @keyframes gradient-shift — background-position 0% 50% → 100% 50% → 0% 50%, 6s ease infinite (for animated gradient borders)

Forced dark theme — no light mode toggle. body gets bg-bg text-text-primary.


Page Structure (Index.tsx)


  {isLoading &&  setIsLoading(false)} />}


Section 1: Loading Screen

Full-screen overlay (fixed inset-0 z-[9999] bg-bg). Uses requestAnimationFrame counter from 000→100 over 2700ms.

  • Top-left: "Portfolio" label — text-xs text-muted uppercase tracking-[0.3em]. Animates y:-20→0, opacity 0→1.
  • Center: Rotating words ["Design", "Create", "Inspire"] cycling every 900ms. AnimatePresence mode="wait" with y:20→0→-20 transitions. text-4xl md:text-6xl lg:text-7xl font-display italic text-text-primary/80.
  • Bottom-right: Counter display — text-6xl md:text-8xl lg:text-9xl font-display text-text-primary tabular-nums. Shows String(count).padStart(3, "0").
  • Bottom progress bar: h-[3px] bg-stroke/50, inner div with .accent-gradient, scaleX(count/100) transform, box-shadow: 0 0 8px rgba(137, 170, 204, 0.35).
  • On complete (count reaches 100): 400ms delay then calls onComplete.

Section 2: Hero

Full-viewport section with background HLS video and centered content.

Background Video

  • HLS source: https://stream.mux.com/Aa02T7oM1wH5Mk 5EEVDYhbZ1ChcdhRsS2m1NYyx4Ua1g.m3u8
  • Uses hls.js — if Hls.isSupported(), create HLS instance; else if native HLS support, set video.src directly.
  • Video: autoPlay muted loop playsInline, absolutely positioned and centered with min-w-full min-h-full object-cover -translate-x-1/2 -translate-y-1/2.
  • Dark overlay: bg-black/20
  • Bottom fade: h-48 bg-gradient-to-t from-bg to-transparent

Navbar (fixed, floats at top center)

fixed top-0 left-0 right-0 z-50 flex justify-center pt-4 md:pt-6 px-4.

Inner pill: inline-flex items-center rounded-full backdrop-blur-md border border-white/10 bg-surface px-2 py-2. Gets shadow-md shadow-black/10 when scrollY > 100.

Contents (left to right):

  1. Logo: 9×9 circle with accent gradient border (reverses direction on hover). Inner bg-bg circle with "JA" in font-display italic text-[13px]. Scales 110% on hover.
  2. Divider: w-px h-5 bg-stroke mx-1 (hidden on mobile)
  3. Nav links: ["Home", "Work", "Resume"] — text-xs sm:text-sm rounded-full px-3 sm:px-4 py-1.5 sm:py-2. Active: text-text-primary bg-stroke/50. Inactive: text-muted hover:text-text-primary hover:bg-stroke/50.
  4. Divider
  5. "Say hi" button: Same size as nav links. On hover, shows accent gradient border behind (using absolute span with inset: -2px). Inner content wrapped in bg-surface rounded-full backdrop-blur-md. Includes "↗" arrow.

Hero Content (centered, z-10)

  • Eyebrow: text-xs text-muted uppercase tracking-[0.3em] mb-8 — "COLLECTION '26". Class blur-in.
  • Name: text-6xl md:text-8xl lg:text-9xl font-display italic leading-[0.9] tracking-tight text-text-primary mb-6 — "Michael Smith". Class name-reveal.
  • Role line: "A {role} lives in Chicago." — roles cycle every 2s through ["Creative", "Fullstack", "Founder", "Scholar"]. Role word uses font-display italic text-text-primary animate-role-fade-in inline-block with key={roleIndex} for re-triggering animation.
  • Description: text-sm md:text-base text-muted max-w-md mb-12 — "Designing seamless digital interactions by focusing on the unique nuances which bring systems to life."
  • CTA Buttons (inline-flex gap-4):
    • "See Works": Solid button. Default: bg-text-primary text-bg. Hover: bg-bg text-text-primary with accent gradient border ring.
    • "Reach out...": Outlined button. Default: border-2 border-stroke bg-bg text-text-primary. Hover: border-transparent with accent gradient border ring.
    • Both: rounded-full text-sm px-7 py-3.5 hover:scale-105.

GSAP Entrance

Timeline with ease: "power3.out":

  • .name-reveal: opacity 0→1, y 50→0, duration 1.2s, delay 0.1s
  • .blur-in: opacity 0→1, filter blur(10px)→blur(0px), y 20→0, duration 1s, stagger 0.1, delay 0.3s

Scroll Indicator

Bottom-center, text-xs text-muted uppercase tracking-[0.2em] "SCROLL" label above a w-px h-10 bg-stroke line with animated highlight using .animate-scroll-down.


Section 3: Selected Works

bg-bg py-12 md:py-16. Inner: max-w-[1200px] mx-auto px-6 md:px-10 lg:px-16.

Header

Framer Motion whileInView — opacity 0→1, y 30→0, duration 1s, ease [0.25,0.1,0.25,1], viewport once margin "-100px".

  • Eyebrow: w-8 h-px bg-stroke + "Selected Work" text-xs text-muted uppercase tracking-[0.3em]
  • Heading: "Featured projects" — italic word in font-display italic
  • Subtext: "A selection of projects I've worked on, from concept to launch."
  • "View all work" button (desktop only, hidden md:inline-flex) — rounded-full with gradient hover border ring + right arrow

Bento Grid

grid grid-cols-1 md:grid-cols-12 gap-5 md:gap-6. Column spans alternate: 7/5/5/7.

4 project cards:

[
  { slug: "automotive-motion", title: "Automotive Motion", image: "/projects/wireframe.png", gradient: "from-violet-500 via-fuchsia-400/60 via-indigo-500/60 to-transparent" },
  { slug: "urban-architecture", title: "Urban Architecture", image: "/projects/building.png", gradient: "from-sky-500 via-blue-400/60 to-transparent" },
  { slug: "human-perspective", title: "Human Perspective", image: "/projects/person.png", gradient: "from-emerald-500 via-emerald-300/60 via-teal-500/60 to-transparent" },
  { slug: "brand-identity", title: "Brand Identity", image: "/projects/branding.png", gradient: "from-amber-500 via-amber-300/60 via-orange-500/60 to-transparent" },
]

Each card: bg-surface border border-stroke rounded-3xl with aspect ratios [4/3, 4/3 md:h-full, 4/3 md:h-full, 4/3]. Contains:

  • Background image with object-cover group-hover:scale-105
  • Halftone overlay: radial-gradient(circle, #000 1px, transparent 1px) at 4×4px, opacity-20 mix-blend-multiply
  • Hover: bg-bg/70 opacity-0→1 + backdrop-blur-lg
  • Hover label: pill with animated gradient border, white bg, "View — Title" (title in font-display italic)

Framer Motion: each card staggers with delay: i * 0.1.


Section 4: Journal

bg-bg py-16 md:py-24. Same header pattern (eyebrow + "Recent thoughts" + subtext + "View all" button).

4 journal entries displayed as horizontal pills (rounded-[40px] sm:rounded-full):

[
  { title: "The Future of Generative Art in 2026", image: "/explorations/planet.jpeg", readTime: "6 min read", date: "Feb 13, 2026" },
  { title: "Designing for the Next Billion Users", image: "/explorations/cubes.jpeg", readTime: "5 min read", date: "Feb 06, 2026" },
  { title: "The Psychology of Minimalist Motion", image: "/explorations/ascii.jpeg", readTime: "6 min read", date: "Feb 03, 2026" },
  { title: "The Importance of Mobile-First Design", image: "/explorations/smoke.jpeg", readTime: "5 min read", date: "Jan 31, 2026" },
]

Each entry: flex items-center gap-6 p-4 bg-surface/30 hover:bg-surface border border-stroke. Contains:

  • Circular image (100×100px, rounded-full overflow-hidden, hover: scale-110, border transitions)
  • Title: text-lg md:text-2xl font-medium, group-hover:translate-x-1
  • Dotted line separator (desktop): flex-grow h-px bg-stroke/30
  • Read time + dot + date
  • Arrow circle: w-10 h-10 rounded-full border border-stroke, hover: fills bg-text-primary text-bg

Framer Motion: alternating x:-20/+20 entrance per entry.


Section 5: Explorations (Parallax Gallery)

min-h-[300vh] section for scroll-driven parallax.

Layer 1: Pinned Center (z-10)

h-screen div pinned with GSAP ScrollTrigger.create({ pin: contentRef, pinSpacing: false }).

  • Eyebrow: "Explorations"
  • Heading: "Visual playground"
  • Subtext: "A space for creative experiments, motion studies, and visual explorations."
  • Dribbble button: pink icon (#ea4c89) + "View on Dribbble" + NE arrow. Gradient hover border ring.

Layer 2: Parallax Columns (z-20, absolute)

grid grid-cols-2 gap-12 md:gap-40 inside max-w-[1400px].

6 items split into 2 columns (even→left, odd→right):

[
  { id: 1, title: "Celestial Planets", category: "3D Visualization", image: "/explorations/planet.jpeg" },
  { id: 2, title: "ASCII Art Study", category: "Generative Art", image: "/explorations/ascii.jpeg" },
  { id: 3, title: "Atmospheric Smoke", category: "Visual Effects", image: "/explorations/smoke.jpeg" },
  { id: 4, title: "Abstract Cylinder", category: "3D Rendering", image: "/explorations/cylinder.jpeg" },
  { id: 5, title: "Organic Waves", category: "Motion Design", image: "/explorations/wave.jpeg" },
  { id: 6, title: "Geometric Cubes", category: "3D Composition", image: "/explorations/cubes.jpeg" },
]
  • Left column: y: "10vh" → "-120vh", scrub 1, spacer h-[20vh]
  • Right column: y: "40vh" → "-100vh", scrub 1.5, spacer h-[40vh]
  • Cards: aspect-square max-w-[320px], rotation (id%2===0 ? 1 : -1) * (1.5 + id%3) degrees
  • Each card: outer border frame at -inset-4 rounded-[40px], blue tint bg-blue-500/10 mix-blend-overlay, halftone texture, hover gradient + content reveal
  • Clicking opens lightbox: fixed z-[100] bg-black/95, image at aspect-[16/10], Framer Motion scale 0.9→1, close on backdrop click or Escape key

Section 6: Stats

bg-bg py-16 md:py-24. Same header pattern (eyebrow "Stats & Facts" + "Making an impact" + long subtext).

3-column grid (sm:grid-cols-2 lg:grid-cols-3, 3rd card gets sm:col-span-2 lg:col-span-1):

[
  { number: "20+", label: "Years Experience", sublabel: "In the web design industry field." },
  { number: "95+", label: "Projects Done", sublabel: "Around worldwide in last five years." },
  { number: "200%", label: "Satisfied Clients", sublabel: "With a great experience and results." },
]

Each: large number text-6xl sm:text-7xl md:text-8xl lg:text-9xl font-medium tracking-tighterh-px bg-stroke divider → label (text-xl md:text-2xl font-bold) + sublabel (text-sm text-muted). Staggered Framer Motion entrance.


Section 7: Contact / Footer

bg-bg pt-16 md:pt-20 pb-8 md:pb-12 overflow-hidden.

Background Video

Same HLS source as hero, but flipped vertically (scale-y-[-1]). Heavier overlay: bg-black/60. Top fade h-32 + bottom fade h-24.

GSAP Marquee

"BUILDING THE FUTURE • " repeated 10×. text-5xl md:text-7xl lg:text-8xl font-display italic text-text-primary/10. GSAP xPercent: -50, duration 40, ease "none", repeat -1.

CTA

  • Subtext: "Have a project in mind? I'm always open to new ideas and collaborations."
  • Email button: mailto:[email protected]px-8 py-4 bg-bg border-2 border-stroke rounded-full with gradient hover border ring. whileTap={{ scale: 0.97 }}. NE arrow icon.

Footer Bar

border-t border-stroke pt-8 flex flex-col md:flex-row items-center justify-between.

  • Left: Social links [Twitter, LinkedIn, Dribbble, GitHub] — text-sm text-muted hover:text-text-primary hover:-translate-y-0.5
  • Right: Green pulsing dot (animate-ping bg-green-400 + solid bg-green-500) + "Available for projects"

Required Assets (in /public/)

  • /projects/wireframe.png, /projects/building.png, /projects/person.png, /projects/branding.png
  • /explorations/planet.jpeg, /explorations/ascii.jpeg, /explorations/smoke.jpeg, /explorations/cylinder.jpeg, /explorations/wave.jpeg, /explorations/cubes.jpeg

Dependencies

gsap, framer-motion, hls.js, react-router-dom, tailwindcss-animate

Add smooth scroll nav Add page transitions

Model Outputs

Comments