Personal portfolio site for Manny Flores, Senior Systems Engineer.
Live: mannyflo.com
- React 19 + Vite 7 (SPA)
- CSS variables (tokens) + scoped component primitives
@vercel/analytics+@vercel/speed-insights- Deployed on Vercel
manny-portfolio/
├── public/ Static assets (logo, profile image, favicon)
├── src/
│ ├── App.jsx Single-page portfolio component
│ ├── main.jsx React entry
│ ├── index.css Global reset
│ └── styles/
│ ├── tokens.css Design tokens (color, spacing, radius, shadow, easing, motion)
│ └── app.css Component primitives + app-specific styles
├── tests/
│ ├── setup.ts Vitest + Testing Library harness
│ └── *.test.jsx Component smoke tests
├── index.html
├── vercel.json Security headers (CSP, HSTS, XFO, Referrer, Permissions)
└── vite.config.js
| Command | What it does |
|---|---|
npm run dev |
Start Vite dev server on http://localhost:5173 |
npm run build |
Production build to dist/ |
npm run preview |
Serve the production build locally |
npm run lint |
ESLint across the repo |
npm test |
Run vitest suite once |
npm run test:watch |
Vitest in watch mode |
All visual constants live in src/styles/tokens.css as CSS custom properties.
- Color:
--color-bg,--color-surface,--color-border,--color-text,--color-text-muted,--color-accent,--color-accent-strong,--color-success - Spacing:
--space-1(4px) through--space-10(80px); 4px base grid - Radius:
--radius-sm(4px),--radius-md(8px),--radius-lg(12px),--radius-xl(16px),--radius-pill(20px) - Font size:
--fs-xsthrough--fs-5xl; mobile inputs use--fs-base(16px) to prevent iOS zoom - Shadow:
--shadow-sm,--shadow-md,--shadow-glow - Easing / motion:
--ease-out,--ease-in-out,--motion-fast,--motion-base,--motion-slow
Add a new token by extending tokens.css. Do not introduce new raw hex values in components: reference a token instead.
Defined in src/styles/app.css:
.card: surface container (border + subtle bg).chip: pill tag for skills / status tags.btn/.btn-ghost: primary and outline button.icon-btn: square icon-only button (44px hit target).dot: status indicator (paired with.dot--success).sr-only: visually-hidden but screen-reader accessible.tabular:font-variant-numeric: tabular-numsfor aligned numbers
Targeting WCAG 2.1 AA.
- Skip link →
<main id="main">landmark :focus-visibleon every interactive elementaria-expanded/aria-controlson the mobile menu buttonaria-current="page"on the active nav itemprefers-reduced-motionrespected: animations collapse to 0.01ms- Color contrast ≥ 4.5:1 on body text, ≥ 3:1 on large text and UI
- 16px minimum font size on mobile inputs (none currently, documented as a rule)
- Semantic landmarks:
<nav>,<main>,<section>witharia-labelledby,<footer>
| Key | Action |
|---|---|
Esc |
Close the mobile menu if open |
Tab |
Move focus through interactive elements (visible ring) |
Enter / Space |
Activate focused button / link |
Nav buttons update the URL hash (#expertise, #skills, …) via history.replaceState so the active section is shareable without triggering a full scroll reset.
Configured in vercel.json:
Content-Security-Policy:default-src 'self'; allows inline styles (required by component-levelstyle={{...}}); fonts self-hosted under/fonts(no external font sources); analytics fromva.vercel-scripts.comandvitals.vercel-insights.comStrict-Transport-Security:max-age=63072000; includeSubDomains; preloadX-Frame-Options:DENYX-Content-Type-Options:nosniffReferrer-Policy:strict-origin-when-cross-originPermissions-Policy:camera=(), microphone=(), geolocation=(), interest-cohort=()
Add a nav section:
- Add
{ id, label }toNAV_SECTIONSinApp.jsx. - Add a
<section id={id}>inside<main>. - IntersectionObserver will sync the active state automatically.
Add a skill group: append to the skills array in App.jsx.
Add a project card: append to the currentProjects array in App.jsx.
Add a writing card: edit the posts array inside the Writing <section> in App.jsx. Set WRITING_ENABLED = false at the top of the file to hide the section entirely.
Each axis scored 0–5; targeting ≥ 4.
- a11y: WCAG 2.1 AA
- perf: Lighthouse 90+, CWV green
- security: headers configured, no secrets in client, deps audited
- ux: URL state, keyboard shortcuts, clear empty/loading states
- tests: vitest + RTL + jsdom, critical-path coverage
- observability: Vercel Analytics + Speed Insights
- docs: this README
- design-system: tokens + primitives, no raw hex in components
Copyright Manny Flores. All rights reserved.
This repository is public so the code can be read, not reused. No license is
granted for the code, content, images, or resume. The fonts in public/fonts/
are the exception: they are subsets of IBM Plex Sans and IBM Plex Mono,
redistributed under the SIL Open Font License 1.1 (see public/fonts/OFL.txt).