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

Pipeline editorial com notas, Git e paginas estaticas

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:

  1. Escrever na branch draft.
  2. Revisar conteudo no Obsidian.
  3. Abrir PR para main.
  4. Rodar validacao, lint, typecheck, testes, build e Lighthouse.
  5. 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;
  • hreflang apenas 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.xml

O 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-sistema

Se 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,seo

Series 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.svg
Imagem: ./assets/pipeline.svg
Alt: Diagrama do pipeline editorial
Diagrama do pipeline editorial exportado do Excalidraw

Essa 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.

Sistema social selecionavel alice owns bob owns post follow vote post follow vote

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 main

Renderer 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 arquitetura

Vira 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.