Every portfolio you see online started somewhere — usually a blank HTML file, a vague idea, and a lot of "I'll figure this out as I go." Mine was no different. In this post I want to break down exactly how I built Sandy's Portfolio — the design decisions, the tech choices, the parts that broke, and what I'd do differently.

1. Starting with the Brand

Before writing a single line of code, I spent time figuring out what the portfolio should feel like. I wanted something that was:

  • Dark and premium — not generic Bootstrap
  • Warm, not cold — so I chose orange (#FF6B2B) as the accent
  • Personal — hence the Sacramento cursive font for script accents
  • Fast — no heavy frameworks, just clean HTML/CSS/JS
Tip: Spend at least a day on brand before coding. Pick one accent colour, two fonts (display + body), and your background values. Everything else flows from those decisions.

2. The Folder Structure

I decided to keep things simple but scalable. Each page lives in its own folder with an index.html. Shared styles and scripts live in a /shared folder that every page imports.

/
├── index.html          ← landing page
├── portfolio/
│   ├── index.html
│   └── style.css
├── blog/
│   ├── index.html
│   └── posts/
│       └── post-1.html
├── shared/
│   ├── shared.css      ← tokens, nav, footer
│   └── shared.js
└── assets/             ← images, logos

This structure makes Vercel deployments trivial — just push to GitHub and it's live. No build step, no bundler, no config.

3. CSS Custom Properties as the Foundation

The entire site runs on CSS variables defined in shared.css. This means changing the brand colour across every page takes exactly one line:

:root {
  --orange:      #FF6B2B;
  --orange-pale: rgba(255,107,43,0.07);
  --dark:        #0E0E0F;
  --dark2:       #161618;
  --text:        #F0EDE8;
  --muted:       #9D9A96;
  --radius:      16px;
  --transition:  0.35s cubic-bezier(0.4,0,0.2,1);
}
✦

4. The Animations (Where Most Time Went)

I'll be honest — the animations took longer than everything else combined. The cursor glow, the hero scroll indicator, the card hover effects, the skill bar animations on scroll... each one started as "this will take 20 minutes" and ended as a 2-hour rabbit hole.

The ones I'm most proud of:

  • Cursor glow — a 500px radial gradient that follows the mouse
  • Scroll-triggered fade-ups — IntersectionObserver with stagger delays
  • Skill bars — CSS width transition fired when the section enters view
  • Hero image ring — a floating orange ring with keyframe animation
Lesson learned: Always add prefers-reduced-motion media query to disable animations for users who need it. It's one CSS block and shows you care about accessibility.

5. Deploying to Vercel

Deployment was the easiest part. Connect your GitHub repo to Vercel, set the root directory, and that's it. Every push to main auto-deploys. Custom domain took under 10 minutes to configure.

The whole site scores 98+ on Lighthouse for performance because there are no unnecessary dependencies, no unused CSS frameworks, and all images are properly sized.

What I'd Do Differently

If I were starting over today, I'd do two things differently:

  1. Plan the component system upfront — I refactored the nav and footer 3 times
  2. Start with mobile layout first, then scale up to desktop

But honestly? Build it messy first. Refactor later. A live site beats a perfect Figma file every single time.

✦

If you've read this far — thanks for sticking around. I write about design and dev on this blog regularly. If something here helped you, share it with someone who's building their own portfolio. 🚀

👤 Sandy
Sandeep 09
UI/UX Designer & Frontend Developer
Building digital products from India. I write about design systems, frontend dev, and the occasional Android project. Reach out if you want to collaborate.