-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Technical architecture and design decisions for the portfolio site.
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")
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
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.
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
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
|
| 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 |
Framer Motion is used for:
-
Parallax scrolling — Apple-style scroll-linked transforms using
useScroll/useTransform - Page load — Hero text staggers in with 0.1s delays
-
Scroll reveal — Sections fade up with
whileInView - Project carousel — Continuous sliding container with spring physics for smooth, infinite transitions
- Project modals — AnimatePresence for thumbnail + modal transitions (Grid/Table views)
- Animated counters — requestAnimationFrame-based counting
Key features:
-
prefers-reduced-motionsupport for accessibility - WCAG 2.1 AA compliance patterns
- Skip-to-content link for keyboard navigation
Contact form uses EmailJS (client-side):
- EmailJS SDK initialized on component mount
- Form validates name, email (regex), message
- Sends via
emailjs.send()with service/template IDs - Shows success/error via Snackbar component
No backend server needed. Credentials are public keys (not secrets) per EmailJS design.
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.
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