sergiomarquez.dev Portfolio Guide
Single-page portfolio site. All content driven by public/cv.json.
Content Updates
To update portfolio content, edit public/cv.json:
| Section | JSON Path | Component |
|---|---|---|
| Bio/summary | basics.summary | About.astro |
| Job title | basics.label | SidebarLeft.astro |
| Work history | experience[] | Experience.astro |
| Projects | projects[] | Projects.astro |
| Certifications | certifications[] | Certifications.astro |
| Social links | basics.profiles[] | SocialLinks.astro |
No code changes needed for content updates -- just edit the JSON.
Adding a New Section
- •Add data to
public/cv.jsonunder a new key - •Update
CvDatatype insrc/data/cv.ts - •Create component:
src/components/NewSection.astro - •Add to
src/pages/index.astrowith<section id="new-section"> - •Add nav link in
src/components/layout/Navigation.astro - •Add test in
src/data/__tests__/cv.test.ts
Adding a Social Redirect
Create src/pages/platform-name/index.astro:
---
import { cv } from '@/data/cv';
const url = cv.basics.profiles.find(p => p.network === 'PlatformName')?.url;
return Astro.redirect(url ?? 'https://sergiomarquez.dev');
---
Layout Structure
┌──────────────────────────────────────────────┐ │ Spotlight │ ├──────────┬───────────────────────┬────────────┤ │ Sidebar │ Main Content │ Sidebar │ │ Left │ │ Right │ │ (sticky) │ ┌─ About ──────┐ │ (email) │ │ │ ├─ Experience ──┤ │ │ │ Name │ ├─ Projects ───┤ │ │ │ Title │ └─ Certs ──────┘ │ │ │ Nav │ │ │ │ Social │ │ │ ├──────────┴───────────────────────┴────────────┤ │ (mobile: single column) │ └──────────────────────────────────────────────┘
Desktop: three-column (sidebar sticky at lg: breakpoint).
Mobile: single column with mobile header.
File Conventions
| Type | Location | Naming |
|---|---|---|
| Section components | src/components/ | PascalCase.astro |
| Layout components | src/components/layout/ | PascalCase.astro |
| Icon components | src/components/icons/ | PascalCaseIcon.astro |
| Data loaders | src/data/ | kebab-case.ts |
| Tests | src/data/__tests__/ | module-name.test.ts |
| Pages | src/pages/ | kebab-case or index.astro |
| Styles | src/styles/ | kebab-case.css |
Design Tokens
| Token | Value | Usage |
|---|---|---|
--background | #0a192f | Page background (navy) |
--primary-text | #ccd6f6 | Headings, primary text |
--secondary-text | #8892b0 | Body text, descriptions |
--accent | #64ffda | Links, highlights, hover |
--accent-muted | rgba(100,255,218,0.1) | Subtle backgrounds |
--navy-light | #112240 | Card backgrounds |
--navy-lighter | #233554 | Borders, hover states |
Font: Geist Variable (non.geist package).
Testing
- •Runner: Vitest (globals mode)
- •Tests:
src/data/__tests__/cv.test.ts,github.test.ts - •Focus: Data loader validation (structure, required fields)
- •Command:
bun run test
CI/CD Pipeline
Push/PR to main → GitHub Actions (ci.yml) ├── bun install ├── type-check (astro check) ├── lint (biome check) ├── test (vitest) └── build (astro build) Push to main → Cloudflare Pages (automatic) ├── npm ci (uses package-lock.json) └── npm run build → dist/
Pre-commit: Husky runs lint-staged → Biome check on src/**/*.
Key Gotchas
- •Content = JSON only: All portfolio content lives in
public/cv.json. Never hardcode content in components. - •Dual lockfile:
bun.lock+package-lock.jsonmust stay in sync. - •Path alias: Use
@/imports (e.g.,import { cv } from '@/data/cv'). - •Biome LF: Enforces LF endings. Windows users may see phantom diffs.
- •Redirect pages:
astro checkfalse positives on unused imports -- ignore. - •Lighthouse target: 95+ performance, 100 SEO. Test before merging visual changes.