A personal website and portfolio built with Next.js 16, React 19, and TailwindCSS 4, deployed on Vercel.
- Next.js 16 with App Router; pages are prerendered and the chat API runs as a Vercel function
- AI chat assistant that answers visitors' questions about my experience and projects (Claude Haiku)
- React 19 with modern hooks and components
- TailwindCSS 4 for styling
- TypeScript for type safety
- Google Analytics 4 and Vercel Speed Insights integration
- GitHub Calendar visualization
- AOS (Animate On Scroll) animations
- Resume Integration at
/resumepath with YAML-based data and PDF download functionality - Coding Activity page with WakaTime tiles and a GitHub contribution calendar
- Scroll-to-top button for long pages
- Node.js 20 or higher
- npm or yarn
The fastest way to get started is to open this repository in a GitHub Codespace or any compatible Dev Container environment by clicking the badge above.
This will automatically set up your development environment with all dependencies pre-installed.
If you prefer to run locally without a Dev Container:
-
Clone the repository:
git clone https://github.com/brignano/brignano.io.git cd brignano.io -
Install dependencies:
npm install
-
Run the development server:
npm run dev
-
Open http://localhost:3000 in your browser
The resume is integrated directly into the application and accessible at /resume. Resume data is stored in YAML format (public/resume.yml) for easy editing and maintenance.
Features:
- PDF download functionality using
@react-pdf/renderer - Client-side PDF generation from YAML data
- Theme toggling support
- Responsive design
- Clean, professional layout
The PDF is rendered once at build time by the app/resume.pdf route, so the download is a plain static file.
npm run dev- Start the development server with Turbopacknpm run build- Build the application for productionnpm run start- Start the Next.js production servernpm run lint- Run ESLint to check code quality
npm run buildEvery page is still prerendered at build time; the only server code is the chat assistant's /api/chat route.
The floating chat widget (components/chat-widget.tsx) posts to app/api/chat/route.ts, which streams answers from Claude Haiku. Its context is built in lib/chat/system-prompt.ts from:
public/resume.yml- the resume, picked up automaticallylib/constants.ts- the projects listlib/chat/about-me.md- extra context in my own words (HTML comments are stripped before it reaches the model)
It needs an ANTHROPIC_API_KEY environment variable (set it in the Vercel project settings, or .env.local for npm run dev). Without one, the widget shows a "not configured" message. Set a monthly spend limit in the Anthropic Console as a backstop; the route also caps message length, conversation length, reply length, and per-IP request rate.
The route also enforces a site-wide daily request budget (CHAT_DAILY_LIMIT, default 300). Once it's used up, the widget asks visitors to email instead until the next UTC day. To share the count across all function instances, connect an Upstash Redis store (Vercel Marketplace → Upstash), which sets KV_REST_API_URL/KV_REST_API_TOKEN (or UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN). Without one, each instance counts on its own.
This project is optimized for deployment on Vercel:
Deploy your own version with one click:
- Push your code to a Git repository (GitHub, GitLab, Bitbucket)
- Import your project to Vercel:
- Go to vercel.com/new
- Select your repository
- Vercel will automatically detect the Next.js framework
- Click "Deploy"
Vercel will automatically:
- Build your Next.js application
- Deploy to a global CDN
- Provide automatic HTTPS
- Set up preview deployments for pull requests
- Enable Speed Insights (already configured)
Use the Vercel CLI to deploy from your machine or CI. Commands vary slightly by CLI version — prefer npx vercel if you don't want a global install.
- Authenticate and link the repo:
vercel login
vercel link # or `vercel link --yes` to auto-confirm- Deploy:
# interactive preview deploy
vercel
# production deploy (non-interactive)
vercel --prod --confirm
# or with npx
npx vercel --prod --confirm- Fetch environment variables locally (when needed):
# older CLI: positional
vercel env pull .env.local production
# newer CLI: long option
vercel env pull .env.local --environment=productionNotes:
vercel env add <NAME> productionadds a variable to the Vercel project; runvercel env pullafterwards to fetch it locally.- For CI, set env vars in the Vercel dashboard or use the Vercel Git integration.
There is no infrastructure-as-code for this site. Everything is managed through provider dashboards.
| Concern | Where | Notes |
|---|---|---|
| Hosting | Vercel | Static Next.js export (output: "export"), auto-deployed on push to main. Headers and CSP in vercel.json. |
| DNS | Cloudflare (free) | brignano.io and anthonybrignano.com. All records are DNS-only (grey cloud) — proxying breaks Vercel's certificate issuance. |
| Cloudflare Email Routing | hi@brignano.io forwards to Gmail. SPF, DKIM and DMARC configured. |
|
| Registration | AWS Route 53 Domains | Registration only — nameservers point at Cloudflare. |
DNS and email ran on AWS (Route 53 + SES → S3 → Lambda) until 2026-08-23. That stack
was decommissioned; the migration record lives in
brignano/aws,
which is now a documentation-only archive.
If you need environment variables for your deployment:
- Go to your project settings on Vercel
- Navigate to "Environment Variables"
- Add your variables for Production, Preview, and Development environments
This site sends events to Google Analytics 4 using the event() helper in lib/gtag.ts (e.g. CTA clicks, project links, résumé downloads, chat usage).
Notes about the analytics setup:
- The GA4 tag only loads in production (
VERCEL_ENV === 'production') and only whenGA_MEASUREMENT_IDis set. There is no default ID — without it, nothing is sent. - Page views, including client-side navigations, come from GA4's enhanced measurement ("Page changes based on browser history events"), which is on by default for a web data stream. Keep it on; there is no manual pageview code.
- Custom events show up in GA under Reports → Engagement → Events.
To enable Google Analytics:
GA_MEASUREMENT_ID=G-YOURIDHERE
WAKATIME_API_KEY=your_wakatime_api_key_hereGenerating .env.local from Vercel CLI
If you already have environment variables set up in the Vercel dashboard, you can pull them into a local .env.local file using the Vercel CLI. Follow these steps exactly:
- Install the Vercel CLI (if needed):
npm install -g vercel- Authenticate and link the local repo to the Vercel project (this is required so
env pullknows which project to read from):
vercel login
vercel link
# or `vercel link --yes` to auto-confirm when you already know the project- Pull environment variables into
.env.localfrom the linked Vercel project. Specify the environment if needed (production/preview/development):
vercel env pull .env.local --environment=developmentNotes and troubleshooting:
vercel env add <NAME> <environment>only sets a variable on Vercel (it will prompt for the value) — it does not automatically write to your local.env.localfile.vercel env pullreads variables from the currently linked Vercel project. If variables are missing afterenv pull, ensure your local repo is linked to the correct Vercel project (vercel link) and that you have permission to read that project's variables.- To re-check or re-link the project use
vercel link --yesor runverceland follow the interactive prompts. - If you prefer to add variables directly via CLI to Vercel (not local), you can run:
vercel env add GA_MEASUREMENT_ID production
vercel env add WAKATIME_API_KEY productionAfter adding variables on Vercel, re-run vercel env pull .env.local --environment=production to fetch them into your local file.
Be careful not to commit secrets — .env.local should remain local and is usually ignored by Git.
Required variables for development:
WAKATIME_API_KEY— required to render the WakaTime coding statistics on the/codingpage. If this is not set the coding page will show an error when fetching WakaTime data.
Optional:
GA_MEASUREMENT_ID— your GA4 measurement ID (G-…). Without it, Google Analytics is not loaded.
.
├── .devcontainer/ # DevContainer configuration
├── .github/ # GitHub workflows and configurations
├── app/ # Next.js app directory (App Router)
│ ├── page.tsx # Home page (/)
│ ├── coding/page.tsx # Coding Activity (/coding)
│ ├── resume/page.tsx # Resume (/resume)
│ ├── sitemap.ts # sitemap.xml
│ ├── globals.css # Global styles
│ └── layout.tsx # Root layout
├── components/ # React components (reusable UI)
├── public/ # Static assets
│ ├── robots.txt # Robots rules
│ └── resume.yml # Resume data (YAML format)
├── lib/ # Site constants, analytics helpers
├── types/ # TypeScript types
├── eslint.config.mjs # ESLint configuration
├── next.config.ts # Next.js configuration
├── postcss.config.mjs # PostCSS configuration
└── tsconfig.json # TypeScript configuration
/- Home page/coding- Coding Activity (WakaTime tiles + GitHub contribution calendar)/resume- Resume page/sitemap.xml- Generated sitemappublic/robots.txt- Robots rules for crawlers
outputFileTracingIncludes- Shipsresume.ymlandabout-me.mdwith the chat API functionimages.unoptimized: true- Serves images as-isbasePath: ""- Set to your repository slug if deploying to a subdirectory
Visitor analytics come from Google Analytics (see Analytics (gtag)). Page-load performance comes from Vercel Speed Insights via @vercel/speed-insights, which only collects data once it is enabled for the project in the Vercel dashboard (Project → Speed Insights → Enable).
Contributions are welcome! Feel free to open issues or submit pull requests.
This project is open source and available under the MIT License.