O build que se recusava a buildar
A primeira coisa que esse blog fez foi se recusar a subir. Zero posts, e o build inteiro falhou.
Motivo: com output: "export", o Next.js transforma o site em HTML estático puro na hora do build. Pra isso funcionar, uma rota dinâmica tipo blog/[slug] precisa entregar todos os slugs de antemão, via generateStaticParams(). Se essa função devolve um array vazio, ela não consegue gerar nenhuma página, dá erro e leva o resto do build junto.
O índice do blog compilava numa boa. A rota de post vazia, ali do lado, matava tudo.
Volto na correção daqui a pouco. Antes, o que o site é de fato.
A stack
Site em Next.js App Router com output: "export", então toda página vira HTML estático gerado no build. Não tem servidor, logo não tem nada pra corrigir em runtime.
O layout veio do template de portfólio Magic UI do Dillion Verma (licença MIT), um ponto de partida realmente bom. Crédito todo dele. Eu me mudei pra dentro e troquei a fechadura: todo conteúdo pessoal vive num único resume.tsx. O estilo é Tailwind CSS v4 com shadcn/ui, e o Magic UI cuida das animaçõezinhas.
O blog roda em content-collections. Os posts são arquivos .mdx em content/, e o Zod valida o frontmatter no build. Chato de propósito. O publishedAt tem que dar parse como data ISO de verdade, então errar o nome do campo ou o valor quebra o build em vez de publicar um "Invalid Date" caladinho.
Deploys
Ligado no Cloudflare Workers Builds. Todo push na main roda next build e publica out/ como static assets do Workers. Rollback é só redeploy de um build mais antigo.
Sem staging, então um post pela metade fica a um git push de virar público. A trava é draft: true. Qualquer post com isso sai filtrado das rotas e do sitemap no build, índice do blog incluído, e pode ficar na main o tempo que eu quiser sem ninguém ver.
Os headers de segurança saem de um arquivo _headers: CSP, HSTS, nosniff, X-Frame-Options: DENY. A zona do Cloudflare empurra todo request HTTP pra HTTPS na borda. Nada disso depende do código da aplicação.
Fazendo o negócio buildar
Voltando ao blog/[slug] vazio. A correção foi renomear a pasta pra _slug. O prefixo underscore faz ela virar pasta privada do Next.js, ignorada pelo roteamento, então o export para de tentar montar página pra uma rota sem nenhum slug. Feio, mas deixou o site subir com o blog ainda vazio.
O rename de volta pra [slug] saiu no mesmo commit deste post. Ou seja, o commit que consertou a rota é o mesmo que publicou a página que você tá lendo agora.