Case study
2 min de leituraPortfolio Pessoal
Site pessoal com identidade editorial e gates de qualidade
Reescrever do zero o portfolio para parecer com a peça que o conteúdo descreve: editorial impresso, sóbrio, com craft à mostra em cada hairline gold-leaf e cada métrica de Lighthouse.
Fluxo de uma requisição
Decisões
Tipografia como design system
Cormorant Garamond italic para display, Inter para corpo, JetBrains Mono para captions. Tokens declarados no @theme do Tailwind v4 com line-heights e weights amarrados ao tamanho. Font-feature-settings opt-in: ligaduras e old-style figures no display, tabular-nums em anos e métricas.
Conteúdo tipado em TypeScript, sem CMS
src/data/site.ts, projects.ts, experience.ts, courses.ts, uses.ts. Cada update é um commit, revisão entra por PR. Zero infra de CMS, zero painel de admin para invadir, zero custo recorrente.
Performance e a11y como gates de CI, não checklists
Lighthouse CI com budget assertivo (LCP < 1.8s, CLS < 0.05, performance >= 0.95, a11y/best-practices/SEO 100). Playwright + axe-core falhando o build em qualquer violação serious ou critical. Vitest cobrindo as quatro peças interativas com mínimo de 70% nas linhas.
Segurança que renderiza A+
CSP por requisição via middleware: nonce de 16 bytes em base64, strict-dynamic, frame-ancestors none, report-uri /api/csp-report. HSTS com preload, Permissions-Policy negando câmera, microfone, geolocalização e pagamento. X-Frame-Options DENY como defesa em profundidade sobre o frame-ancestors.
“Cada hairline gold-leaf é a versão tipográfica de um aro de prova: discreta, mas sem ela a peça não existe.”
Tradeoffs
CSP nonce torna a home dinâmica
O nonce muda por request, então layout.tsx lê headers() e a home deixa de ser estática. Custo: ~50ms de cold start no Railway. Aceitável para um portfolio com o tráfego que tem; o ganho de score em securityheaders.com vale a perda do prerender total.
Framer Motion no client
Cada animação custa peso no bundle. A regra é: nada se mexe sem propósito editorial, e MotionConfig reducedMotion="user" no provider colapsa tudo para usuários com prefers-reduced-motion: reduce. Magnetic effect só no CTA primário, raio de 80px.
OG dinâmico com fontes via fetch
A imagem 1200×630 carrega Cormorant via fontsource CDN no build. Falha graciosamente para serif do sistema se o fetch falhar. Custo: a primeira geração é mais lenta; depois é estática.
Antes / depois
- Maior chunk client (gzip)
- Antes: sem split
- Depois: 70 KB
- Top-5 chunks somados (gzip)
- Antes: sem split
- Depois: 217 KB
- Lighthouse Performance (gate)
- Antes: sem CI de perf
- Depois: >= 95 desktop
- a11y violations serious/critical (gate)
- Antes: sem CI de a11y
- Depois: 0
Estado atual
O portfolio é o primeiro caso de estudo de si mesmo. Cada PR (1 a 8) corresponde a uma fase deste processo, com commits pequenos, gates verdes antes de cada merge e o tom mantendo-se editorial em todas as superfícies.