Portfolio of Rafael Fernandes, product engineer.
I solve problems. I build products. Code is just a consequence.
A personal site built the way I build products: small, owned end to end and running in production. It is an editorial, notebook-style page covering experience, open source work, projects, how I work, and the path from physics to production systems.
The site is also a working example of the rest of the delivery chain: a static build, component docs, end-to-end and visual tests, a gated CI/CD pipeline, and a hardened nginx server on hardware I run myself.
- Bilingual, statically. English at
/and Brazilian Portuguese at/pt, each with its own root layout and correct<html lang>. First-time visitors are sent to their browser's language by a tiny inline script. After that, the language toggle's choice wins. - Content as data. All copy lives in typed modules under
src/config/content, one per locale, behind a singleSiteContenttype. A missing translation is a type error. - Privacy first. Google Analytics runs in consent mode and stays denied until the visitor accepts. Click tracking is declarative through
data-*attributes. Bilingual privacy policy (LGPD) at/privacyand/pt/privacidade. - Accessible. axe-core runs on every page, in both languages, on desktop and mobile.
- Documented components. Storybook for the UI building blocks, published at storybook.rafer.dev.
flowchart LR
dev[git push] --> pr{Pull request}
pr --> checks[Checks<br/>lint · types · storybook · playwright]
checks -->|required to merge| main[main]
main --> deploy[Build and Deploy]
deploy --> c2[Checks again]
c2 --> build[Build<br/>next export · storybook · gzip -9]
build --> runner[Self-hosted runner]
runner -->|rsync --delete| www["/srv/rafer.dev"]
www --> nginx[nginx in Docker<br/>gzip_static · security headers]
nginx --> site[rafer.dev<br/>storybook.rafer.dev]
- Build. Next.js 15 with
output: 'export'produces plain HTML/CSS/JS. Storybook is built alongside it, and both are pre-compressed so nginx serves.gzfiles directly. - Deploy. A self-hosted GitHub Actions runner on the server pulls the build artifacts and rsyncs only what changed. It rebuilds the nginx image only when
nginx/changes, then health-checks the live host before finishing. A missing or empty build is refused rather than deployed. - Serve. nginx in a memory-capped container: Content Security Policy, HSTS, and long-lived caching for hashed assets. TLS terminates in front of it.
Requires Node 24 and pnpm 8.14.1.
pnpm installAnalytics env vars are validated at build time. Create .env.local with NEXT_PUBLIC_GA_SRC and NEXT_PUBLIC_GA_TAG_ID (any placeholder works locally).
pnpm devThe site runs at http://localhost:3000; Portuguese is at /pt.
pnpm dev.storybookStorybook runs at http://localhost:6006.
To test the real static export behind the same nginx config used in production, build it and run Playwright. The config starts the nginx test container on port 3000 via Docker Compose.
pnpm buildpnpm test.playwrightEvery pull request to main runs the Checks workflow, and the same workflow runs again before every deploy.
| Job | What it runs |
|---|---|
| Lint | ESLint (flat config) and tsc --noEmit |
| Storybook | A full Storybook build |
| Test | Playwright on desktop and mobile Chrome: sections, i18n, links, analytics consent, privacy pages, axe accessibility and full-page screenshots |
Lint, Storybook and Test are required status checks on main, alongside CodeQL. Screenshot baselines are generated on CI so the rendering environment always matches. After an intentional visual change, run the Checks workflow manually with update_snapshots and commit the uploaded baselines.
Locally, Husky runs ESLint and Prettier on staged files, and Commitizen guides conventional commit messages.
src/
app/
(en)/ (pt)/ root layouts and pages per locale
_layout/ shell: header, footer, consent banner, analytics, metadata
_page/ home page sections
components/ shared UI (annotations, scribbles, mockups, policy)
config/
content/ typed copy per locale
i18n.ts locales, routes and the language redirect
site.ts links and site metadata
lib/ analytics, consent, fonts
stories/ Storybook stories
tests/ Playwright specs and screenshot baselines
nginx/ production and test nginx images
scripts/deploy.sh the deploy step run on the server
Spotted a bug, a typo, or a security issue? Open an issue. For code, fork from development and open a pull request to main.
CC0 1.0. The code is free to reuse. The written content and personal details describe me, so please don't present them as yours.