Modern portfolio website built with Next.js 16, React 19, TypeScript, and Tailwind CSS 4. Deployed globally on Cloudflare Pages.
npm install
npm run dev # Development server
npm run build # Production build
npm run lint # ESLint checks
npm run preview # Local preview with Wranglernpm run test:a11y:contracts # WCAG-focused Vitest contracts
npm run test:a11y:scanners # pa11y + Nu HTML validator against local site
npm run test:a11y:external # pa11y against deployed/external URL
npm run test:a11y:automated # Runs both in sequence
npm run test:a11y:ci # Contracts + local scanners + external pa11yOptional scanner env flags:
A11Y_AUTOSTART=0do not auto-startnext dev(expects server already running)A11Y_PORT=3001change the local port used by scanner checksA11Y_URL=http://127.0.0.1:3001override scanner target URL directlyA11Y_NU_STRICT=1fail the scanner suite on Nu validator HTML errorsA11Y_EXTERNAL_URL=https://cameronaaron.comtarget URL fortest:a11y:external
npm run security:leaks:staged # fast — scans staged changes (pre-commit)
npm run security:leaks:history # scans full git history (pre-push)Both run locally via git hooks; CI runs a third, independent pass with
gitleaks/gitleaks-action on
every push and pull request. Requires the gitleaks CLI (brew install gitleaks) for the local scripts.
npm run test:pages:parityThis check fails if required migration parity contracts drift:
- host/canonical redirects in
public/_redirects - security/cache/404 headers in
public/_headers - Pages-first deploy script wiring in
package.json - no production worker deploy surface in
wrangler.toml
CI now runs this automatically before lint/typecheck/build.
npm run test:performance:contracts # Static output budget checks (HTML/JS/CSS/images)
npm run test:performance # Build + budget checks + Lighthouse CI assertionstest:performance:contracts validates deterministic build-time budgets from out/.
Lighthouse assertions remain active in CI for runtime UX metrics (FCP, LCP, CLS, TBT, Speed Index, and Interactivity).
src/
├── app/ # Next.js App Router
├── components/ # UI components
├── data/ # Content (edit here!)
└── utils/ # Helper functions
public/ # Static assets (.avif images)
- Next.js 16.2.6 - React framework with App Router
- React 19.2.6 - UI library
- TypeScript 6.0.3 - Type safety
- Tailwind CSS 4.3.0 - Styling
- Framer Motion 12.40.0 - Animations
- Cloudflare Pages - Global static deployment
Update content in src/data/ directory:
profile.ts- Personal infoexperience.ts- Work historyprojects.ts- Portfolio projectsskills.ts- Technical skillstestimonials.ts- Recommendationsfaqs.ts- FAQ section
Images go in public/ as .avif files (AVIF is the required render-path format; see the asset-weight contract).
Automatic: Push to master → Live via Cloudflare Pages Git integration
Manual (recommended):
./scripts/verify/verify-deployment.sh # Pre-flight checks
npm run deploy:prod # Deploy to productionDeployment is now blocked unless strict Lighthouse/performance checks pass (100 category scores + metric assertions via test:performance).
Requirements:
- Cloudflare Pages project:
cameronaaronsite
The production redirect behavior is enforced by public/_redirects and public/_headers:
https://www.cameronaaron.com/* -> https://cameronaaron.com/:splathttps://workshop.cameronaaron.com/* -> https://cameronaaron.com/https://2eschool.org/* -> https://cameronaaron.com/https://www.2eschool.org/* -> https://cameronaaron.com//index.html -> /- Security headers are provided by
public/_headers(includingX-Frame-Options: DENY).
Dashboard-level items that are not code-configured in this repo:
- Add
2eschool.organdwww.2eschool.orgas custom domains (or create zone Redirect Rules if they should canonicalize tocameronaaron.com). - Ensure SSL/TLS and proxy are enabled for all mapped domains.
- In Cloudflare Pages, open project
cameronaaronsiteand verify latest production deployment is healthy. - In Pages custom domains, add/verify
cameronaaron.com,www.cameronaaron.com, andworkshop.cameronaaron.com. - In DNS for
cameronaaron.com, set records exactly as requested by the Pages domain wizard and keep proxy enabled. - Wait until each Pages domain shows active/verified status and TLS certificate is issued.
- Run live probes:
https://cameronaaron.comreturns 200https://www.cameronaaron.comreturns 301 to apexhttps://workshop.cameronaaron.comreturns 301 to apex- missing route returns 404 with expected body
- Only after all probes pass, confirm there are no legacy Worker custom-domain or Worker route bindings left.
- Bundle: 53.3 kB
- First Load: 155 kB
- Static files: 59
- Response time: <50ms globally
- Deployment: 330+ edge locations
- 46+ structured data entities (Schema.org)
- OpenGraph & Twitter Cards
- Dynamic sitemap & robots.txt
- Google rich results ready (FAQ, Reviews, Breadcrumbs, etc.)
- Email: cameronthescientist@pm.me
- GitHub: github.com/cameronaaron
- LinkedIn: linkedin.com/in/cameronaaron
- Website: cameronaaron.com
Copyright © 2026 Cameron Aaron. All rights reserved. See LICENSE for terms.
Status: ✅ Production Ready | Last Updated: August 2026