Blog pessoal como sistema de publicacao
Como este blog combina Obsidian, Astro, SEO automatico, performance estatica e publicacao por Git para transformar escrita em sistema.
8 min de leitura

Um blog tecnico parece simples por fora: uma home, uma lista de posts e uma pagina de artigo. A parte importante esta no que acontece antes do HTML chegar ao leitor.
Este projeto foi desenhado para transformar escrita em publicacao repetivel: Obsidian para capturar e revisar, Git para controlar estado, Astro para gerar HTML estatico, Zod para validar conteudo e Cloudflare Pages para entregar tudo perto do usuario.
O principio: conteudo primeiro
O repositorio trata content/ como fonte de verdade editorial. Isso muda a
forma de trabalhar: o post nao nasce dentro de um painel de CMS, mas em arquivos
versionados, legiveis e faceis de abrir no Obsidian.
Cada post tem uma pasta propria:
content/posts/blog-pessoal-como-sistema/
post.yml
pt-br.mdx
assets/O post.yml guarda estado global: status, visibilidade, indexacao, tags,
related posts e comentarios. O .mdx guarda o texto e os metadados localizados,
como slug, titulo, descricao e idioma.
Por que isso importa
Separar metadados globais de conteudo localizado evita fallback falso entre idiomas. Se um post existe em portugues, mas a traducao em ingles ainda esta em rascunho, a URL em ingles simplesmente nao existe.
Publicacao sem magia
O fluxo de publicacao e deliberadamente explicito:
- Escrever na branch
draft. - Revisar conteudo no Obsidian.
- Abrir PR para
main. - Rodar validacao, lint, typecheck, testes, build e Lighthouse.
- Publicar no Cloudflare Pages apenas depois do merge.
O blog nao depende de um clique invisivel em painel. Git vira o historico editorial e tambem o mecanismo de entrega.
Performance como requisito de produto
Artigo comum precisa carregar como HTML estatico. Sem framework JavaScript global. Sem busca na v1. Sem embed social pesado. Sem comentarios no carregamento inicial.
O JavaScript permitido e pequeno e isolado:
- tema claro/escuro salvo no navegador;
- filtro de tags somente na pagina
/tags/; - comentarios carregados sob demanda;
- demos futuras apenas quando o post pedir explicitamente.
Isso deixa a experiencia de leitura leve e previsivel. O leitor recebe primeiro o texto, nao uma aplicacao inteira.
SEO gerado a partir do modelo
SEO nao fica espalhado em componentes manuais. O modelo de conteudo alimenta:
- canonical por pagina;
hreflangapenas para idiomas existentes e publicados;- sitemap filtrado por indexacao;
- RSS por idioma;
- Open Graph e Twitter Cards;
- JSON-LD de
BlogPosting, colecoes e breadcrumbs; - imagens OG estaticas geradas por rota.
Este post, por exemplo, publica em:
/pt-br/blog-pessoal-como-sistema/
/og/pt-br/blog-pessoal-como-sistema.svg
/pt-br/rss.xmlO mesmo conteudo tambem aparece na home, na pagina de tags e na serie quando estiver listado e publicado.
Multilingue sem fingir traducao
O blog suporta pt-BR, en e es, mas cada idioma e opcional. A regra e
simples:
lang: pt-BR
localeStatus: published
slug: blog-pessoal-como-sistemaSe a traducao existir como en.mdx, mas estiver com localeStatus: draft, ela
continua no repositorio e nao entra no site publico. Isso permite trabalhar em
traducoes sem quebrar rotas, sitemap ou hreflang.
Tags, series e posts relacionados
Tags vivem em content/tags.yml e precisam ter rotulo nos tres idiomas. A pagina
de tags permite combinar filtros via query string, usando AND:
/pt-br/tags/?tags=performance,seoSeries sao entidades proprias. Elas organizam posts em uma ordem editorial sem
duplicar estado dentro de cada artigo. Related posts usam translationKey, nao
slug, para continuar estaveis mesmo se a URL mudar.
Na pagina deste post, essas features aparecem juntas: sumario lateral gerado por
h2 e h3, navegacao da serie, card de post relacionado e tags renderizadas nos
cards de listagem.
Experiencia visual editorial
O design segue uma ideia simples: pagina clara, leitura densa, detalhes suficientes para parecer produto, mas sem competir com o texto.
O layout de artigo entrega:
- titulo grande e descricao direta;
- tempo de leitura automatico;
- tipografia customizada com Tailwind Typography;
- code blocks renderizados no build com Shiki;
- sumario lateral com anchors;
- modo claro/escuro via CSS variables;
- cards consistentes para home, series, tags e relacionados.
O resultado esperado nao e uma landing page. E um blog que abre rapido, ajuda o leitor a se orientar e deixa cada camada tecnica trabalhar em silencio.
Laboratorio de integracoes
Esta secao existe para testar a experiencia editorial completa: syntax highlight com Shiki, desenho em Excalidraw, video sob demanda, comandos Git e um exemplo de bloco que pode virar demo interativa depois.
TypeScript
O exemplo em TypeScript mostra como um post poderia declarar integracoes editoriais sem acoplar o texto ao runtime do site.
type EditorialIntegration = {
kind: "excalidraw" | "video" | "code-demo";
path: string;
lazy: boolean;
};
export function createPublishingIntegrations(): EditorialIntegration[] {
return [
{ kind: "excalidraw", path: "./assets/pipeline.excalidraw.md", lazy: false },
{ kind: "video", path: "cloudflare-stream:demo-intro", lazy: true },
{ kind: "code-demo", path: "./examples/renderer.ts", lazy: true }
];
}GraphQL
O exemplo em GraphQL simula uma consulta para buscar metadados publicaveis do post, incluindo assets e integracoes.
query PostPublishingPreview($translationKey: String!, $lang: String!) {
post(translationKey: $translationKey, lang: $lang) {
title
slug
status
seo {
canonical
ogImage
}
integrations {
kind
path
lazy
}
}
}HTML
O exemplo em HTML mostra a saida que a camada de artigo pode renderizar para uma demo futura. A ideia e manter o HTML inicial estatico e carregar interatividade apenas quando a demo for aberta.
<section class="code-demo" data-demo="publishing-pipeline">
<header>
<h2>Publishing pipeline</h2>
<button type="button" data-load-demo>Carregar demo</button>
</header>
<pre><code>content -> validate -> build -> deploy</code></pre>
</section>Excalidraw
Para desenhos tecnicos, o arquivo fonte fica junto do post. O Obsidian edita o desenho, o repositorio versiona a fonte e o build pode usar uma exportacao leve quando a imagem final precisar aparecer no artigo.
content/posts/blog-pessoal-como-sistema/assets/
pipeline.excalidraw.md
pipeline.svgImagem: ./assets/pipeline.svg
Alt: Diagrama do pipeline editorialEssa estrutura preserva a intencao do desenho e evita que a imagem final vire um asset sem historico editorial.
SVG inline com texto selecionavel
Quando o desenho precisa parecer parte do texto, o SVG pode ficar junto do post
e ser renderizado inline pelo build. Assim nomes como alice, post, follow
e vote continuam dentro do DOM, selecionaveis pelo leitor, em vez de virarem
pixels dentro de uma imagem.
Video
Video deve ser opt-in e carregar sob demanda. O caso pequeno pode usar um .webm
local com preload="none". Video grande deve ir para Cloudflare Stream ou URL
externa permitida.
<video
controls
playsinline
poster="./assets/cover.webp"
preload="none"
src="https://customer-example.cloudflarestream.com/demo/manifest/video.m3u8"
></video>
No componente planejado do blog, a mesma intencao ficaria assim:
<BlogVideo
caption="Visao rapida do fluxo Obsidian, validacao e deploy."
poster="./assets/cover.webp"
src="https://customer-example.cloudflarestream.com/demo/manifest/video.m3u8"
/>Git
Git e o trilho de publicacao. Um fluxo de exemplo para transformar rascunho em post publicado:
git switch draft
git add content/posts/blog-pessoal-como-sistema
git commit -m "content: expand publishing system article"
git switch main
git merge draft
git push origin mainRenderer de codigo planejado
Para demos mais ricas, a ideia e declarar o exemplo no post e deixar o componente carregar a ferramenta certa apenas quando necessario. O conteudo inicial continua legivel mesmo sem JavaScript.
type CodeRendererConfig = {
engine: "shiki" | "sandbox";
language: "ts" | "graphql" | "html";
source: string;
interactive: boolean;
};
export const rendererExample: CodeRendererConfig = {
engine: "shiki",
language: "ts",
source: "./examples/publishing-pipeline.ts",
interactive: false
};Midia sem perder controle
Imagens continuam no repositorio, perto do post. Em desenvolvimento, o arquivo local e usado diretamente. Em producao, a camada de Cloudflare Images pode gerar variantes otimizadas.
Markdown simples ja recebe atributos de performance no build:
Imagem: ./assets/arquitetura.png
Alt: Diagrama da arquiteturaVira imagem com loading="lazy", decoding="async" e classe padronizada. Para
casos especiais, componentes MDX como BlogImage e BlogVideo existem para
controlar prioridade, variantes e comportamento.
Comentarios sob demanda
Comentarios estao planejados para giscus e ligados ao translationKey, assim a
discussao pode acompanhar o post em vez de depender de uma URL especifica.
Na v1 atual, a secao ja e lazy e preserva o carregamento inicial. A configuracao final do giscus ainda fica pendente, entao o artigo nao promete um sistema de comentarios ativo antes dessa etapa estar fechada.
O que torna o sistema bonito
Beleza aqui nao e enfeite. E previsibilidade:
- o editor sabe onde escrever;
- o build sabe o que publicar;
- o leitor recebe HTML rapido;
- o mecanismo de SEO nasce dos mesmos dados do conteudo;
- o deploy tem validacoes antes de tocar producao.
Um blog pessoal fica forte quando cada post e facil de criar, facil de revisar, facil de publicar e facil de manter. Este projeto tenta fazer exatamente isso, sem transformar escrita em operacao pesada.
Posts relacionados
Comentarios
Comentarios carregam sob demanda para preservar o carregamento inicial do artigo.