Personal website built with Next.js (Pages Router) and TypeScript. It renders profile content and fetches the latest public GitHub repositories and Medium articles at build time.
Live site: https://rodrigocastilho.com/
- Next.js 16
- React 19
- TypeScript (strict)
- ESLint + Prettier + Husky
- GitHub Pages (static export)
- Node.js 24.x (see
.nvmrc) - Yarn
nvm use
yarn install
yarn devyarn dev: Start the development server (setsNODE_TLS_REJECT_UNAUTHORIZED=0).yarn build: Build the statically exported site.yarn start: Run the production Next.js server locally.yarn lint: Run ESLint checks.yarn lint:fix: Run ESLint and auto-fix issues.yarn typecheck: Type-check the project withtsc --noEmit(strict mode).
Copy .env.example to .env.local and fill in what you need.
NEXT_PUBLIC_GA_TRACKING_ID(optional): Google Analytics measurement ID, read at build time. When it is unset, neither the cookie consent banner nor Google Analytics is rendered. In CI it comes from a GitHub Actions variable of the same name.
- Run
nvm use. - Install dependencies with
yarn install. - Start local development with
yarn dev. - Before opening a PR, run the full verification suite:
yarn lint && yarn prettier --check . && yarn typecheck && yarn build.
- Static export via Next.js (
output: 'export'). - The canonical deployment artifact is the static
out/directory. - Deployment runs through GitHub Actions workflow
.github/workflows/nextjs.yml, which runs lint, the Prettier check, and typecheck before building. - Production deploys are triggered by pushes to
master(and manual workflow dispatch). - When validating the exported site locally, serve
out/with a static file server. - Custom domain:
public/CNAME(rodrigocastilho.com).
- Keep these static export constraints in
next.config.mjs:output: 'export',trailingSlash: true, andimages.unoptimized: true. - Data is fetched at build time from GitHub and Medium.
- Normalize external API responses before passing data to components (
src/shared/utils/normalizeGitHub.tsandsrc/shared/utils/normalizeMedium.ts). - Use TypeScript path aliases from
tsconfig.json(for example@/components/*,@/utils/*) for cross-directory imports; same-directory siblings are imported relatively. - Component styles use CSS Modules in
src/styles/; only resets, CSS custom properties, and a few accessibility utilities live inglobals.css. - Branch and open a pull request for every change β never commit to
master(seeAGENTS.mdβ Git Workflow).
- API overview:
public/docs/api.md - OpenAPI contract:
public/docs/api/openapi.json - Agent registration contract:
public/auth.md - Static discovery metadata lives under
public/.well-known/, including the API catalog, MCP metadata, OAuth/OIDC metadata, agent card, and agent skills index. - GitHub Pages serves static files only, so the
Link/Varyheader rules inpublic/_headersand thevercel.jsonrewrites are inactive in production. Fetch/index.mddirectly instead of relying onAccept: text/markdown.
AGENTS.mdβ full project guide: structure, commands, conventions, verification, Git workflow.DESIGN.mdβ architecture decisions and rationale.CLAUDE.mdβ Claude-specific quick reference.SECURITY.mdβ vulnerability reporting policy.