Skip to content

Architecture

Claude Code edited this page Feb 7, 2026 · 3 revisions

Architecture

Technical architecture and design decisions for the portfolio site.

Component Structure

App.tsx                 # Root with scroll progress bar, skip navigation
├── Navbar.tsx          # Fixed nav with scroll detection, dark mode toggle
├── Hero.tsx            # Apple-style parallax landing with gradient orbs
├── About.tsx           # Skills with animated counters, parallax effects
├── Projects.tsx        # Multi-view (Grid/Carousel/Table) with search/filter, carousel uses continuous sliding container
├── Donation.tsx        # Support section (Stripe, BTC, ETH)
├── Contact.tsx         # EmailJS contact form with dark mode
├── Footer.tsx          # Site footer
└── Snackbar.tsx        # Toast notifications (role="alert")

Data Flow

All project data lives in src/utils/constants.ts as typed arrays. Components import and render this data. There is no backend, API calls, or database — the site is entirely static except for the EmailJS contact form.

Dark mode state is managed via useTheme hook with localStorage persistence. Scroll animations use Framer Motion's useScroll and useTransform hooks for parallax effects.

constants.ts
  ├── PROJECTS[]     → Projects.tsx (filtered/sorted)
  ├── SOCIAL_LINKS[] → Hero.tsx, Footer.tsx
  ├── CONTACT_INFO[] → Contact.tsx
  └── NAV_ITEMS[]    → Navbar.tsx

Type System

All data structures are typed in src/types/index.ts:

Interface Properties
Project id, title, description, technologies[], githubUrl, liveUrl?, category, features[], screenshot?
SocialLink icon, url, label
ContactInfo icon, label, value, url, color

Note: Projects no longer have an icon field (removed in v2 redesign). Screenshots are stored in public/screenshots/project-{id}.png.

Styling Architecture

Tailwind CSS Configuration

The tailwind.config.js defines two custom color scales with dark mode support:

  • primary — Blue tones (50-900) for accent elements
  • surface — Slate tones (50-950) for backgrounds, text, borders
  • darkMode — Class-based strategy (darkMode: 'class') for theme toggle

CSS Layers

The index.css uses three Tailwind layers:

Layer Purpose
@layer base HTML defaults, smooth scroll, body font
@layer components .btn, .section-title, .container, .loading-screen
@layer utilities .text-gradient, .line-clamp-2, .line-clamp-3

Design Token Summary

Token Value Usage
Font Inter 300-800 All text
Radius rounded-lg (8px), rounded-xl (12px) Cards, buttons
Max width max-w-6xl (72rem) Container
Section padding py-24 Vertical rhythm

Animation Strategy

Framer Motion is used for:

  1. Parallax scrolling — Apple-style scroll-linked transforms using useScroll/useTransform
  2. Page load — Hero text staggers in with 0.1s delays
  3. Scroll reveal — Sections fade up with whileInView
  4. Project carousel — Continuous sliding container with spring physics for smooth, infinite transitions
  5. Project modals — AnimatePresence for thumbnail + modal transitions (Grid/Table views)
  6. Animated counters — requestAnimationFrame-based counting

Key features:

  • prefers-reduced-motion support for accessibility
  • WCAG 2.1 AA compliance patterns
  • Skip-to-content link for keyboard navigation

Email Integration

Contact form uses EmailJS (client-side):

  1. EmailJS SDK initialized on component mount
  2. Form validates name, email (regex), message
  3. Sends via emailjs.send() with service/template IDs
  4. Shows success/error via Snackbar component

No backend server needed. Credentials are public keys (not secrets) per EmailJS design.

Build & Deploy

npm run build → react-scripts build → build/
npm run deploy → gh-pages -d build → GitHub Pages

The site is hosted at davidagustin.github.io via the gh-pages branch.

Performance

The production bundle after gzip:

  • JS: ~93 KB
  • CSS: ~5 KB

Key optimizations:

  • No per-project icon imports (removed ~30 unused icons)
  • Minimal dependencies (React, Framer Motion, EmailJS, react-icons)
  • Static data, no runtime API calls
  • Tailwind CSS purges unused styles

Clone this wiki locally