kristyan.dev

Hello World

Como construí este blog do zero com Astro, TypeScript e Tailwind CSS — arquitetura, decisões técnicas e o fluxo de desenvolvimento.

astro typescript tailwind ssg

Hello World

Todo blog de dev começa com um “Hello World”. Este não seria diferente.

Este é o primeiro post oficial, e nada mais justo do que escrever sobre como o próprio blog foi construído: as decisões técnicas, a arquitetura, o que funcionou e o que eu teria feito diferente.

Por que um blog?

Há um tempo venho sentindo que não documentava o suficiente o que aprendo. Livros, projetos, experimentos… tudo ficava na minha cabeça ou em arquivos avulsos. Um blog resolve isso. Não porque alguém necessariamente vai ler, mas porque escrever sobre algo é a melhor forma de descobrir se você realmente entendeu.

A stack

A escolha foi clara desde o início: queria algo estático, simples de manter e sem banco de dados. Nada de CMS, nada de painel administrativo. Só arquivos Markdown no repositório.

Astro foi a escolha natural para o framework. Ele foi feito exatamente para esse caso de uso: sites com pouco ou nenhum JavaScript no cliente, geração estática e suporte nativo a Markdown com Content Collections.

TypeScript para tudo. Sem any, sem atalhos. O schema do frontmatter é validado com Zod em tempo de build. Se um campo obrigatório estiver faltando, o build quebra. Isso evita posts malformados chegarem em produção.

Tailwind CSS para estilização. Sem CSS modules, sem styled-components. Só utilitários. O resultado é um bundle pequeno e componentes fáceis de escanear.

Arquitetura

A estrutura do projeto ficou assim:

tree -I 'node_modules|.astro'
.
├── astro.config.mjs
├── content
│   └── posts
│       └── hello-world.md
├── package.json
├── package-lock.json
├── public
├── README.md
├── src
│   ├── components
│   │   ├── Footer.astro
│   │   ├── Header.astro
│   │   ├── MarkdownContent.astro
│   │   ├── PostCard.astro
│   │   └── ThemeToggle.astro
│   ├── content.config.ts
│   ├── layouts
│   │   ├── BaseLayout.astro
│   │   └── PostLayout.astro
│   ├── lib
│   │   ├── markdown.ts
│   │   ├── posts.ts
│   │   └── seo.ts
│   ├── pages
│   │   ├── 404.astro
│   │   ├── index.astro
│   │   └── posts
│   │       └── [slug].astro
│   ├── styles
│   │   └── global.css
│   └── types
│       └── post.ts
├── tailwind.config.mjs
├── tsconfig.json
└── vercel.json

12 directories, 24 files

Os posts vivem fora do src/, dentro de content/posts/. O Astro lê essa pasta automaticamente via Content Collections. Nenhum import manual, nenhuma rota registrada na mão. Crio um arquivo .md, salvo, e ele aparece.

A lógica de leitura dos posts está isolada em src/lib/posts.ts. Essa função filtra posts não publicados, ordena por data e é reutilizada em todas as páginas que precisam listar posts.

Roteamento

Duas rotas principais:

  • / lista todos os posts publicados
  • /posts/[slug] renderiza o post completo

O slug vem diretamente do nome do arquivo. hello-world.md vira /posts/hello-world. Sem configuração extra.

Dark mode

O dark mode é controlado por classe CSS no elemento html, com preferência salva no localStorage. Um script inline no <head> aplica o tema antes do primeiro render para evitar flash de tema errado.

Deploy

O blog é hospedado na Vercel. O deploy é automatizado via GitHub Actions: qualquer push para main que altere um arquivo dentro de content/posts/ dispara um webhook que aciona a Vercel para reconstruir o site.

Escrevo o post, faço o commit, faço o push. Em alguns segundos está no ar.

O que ficou de fora

Intencionalmente não implementei:

  • Paginação - por enquanto não preciso
  • Busca - idem
  • RSS - está nos planos
  • Syntax highlighting - vem em breve

A ideia foi lançar simples e ir adicionando conforme a necessidade real aparecer. Overengineering em blog pessoal é uma armadilha fácil.

Considerações finais

O projeto inteiro tem menos de 25 arquivos. O build leva poucos segundos. Adicionar um post novo leva menos de um minuto.

É exatamente o que eu queria.

Se tiver curiosidade sobre algum detalhe específico da implementação, os próximos posts vão aprofundar cada parte: Content Collections, o pipeline de deploy, dark mode sem flash, e mais.

Até o próximo.