# Como escrever um artigo em MDX neste blog

> O guia que eu consulto quando vou escrever, do celular ou do computador. Cada recurso do blog aparece aqui duas vezes, funcionando e com o código para copiar.

Este texto é a minha cola. Quando escrevo pelo celular, é fácil esquecer como
se faz um destaque ou onde vai a legenda de uma imagem. Então juntei tudo
aqui: cada recurso aparece funcionando e, logo abaixo, o código que o produz.

> [!NOTE] Este é o meu fluxo
> Uso as ferramentas que escolhi para este site, mas existem outras para cada
> etapa, e há uma seção sobre elas logo depois do fluxo. O Markdown daqui
> funciona em qualquer blog com MDX. O que depende de como este site foi
> montado está marcado com uma nota **Só neste blog**.

## O que é MDX

[MDX](https://mdxjs.com) é Markdown que aceita componentes. Na prática, você
escreve como sempre escreveu (`**negrito**`, `## título`, listas) e, quando
precisa de algo que o Markdown não tem, como uma imagem com legenda, usa uma
tag como `<Figure />`.

O arquivo de cada texto mora em `content/blog/` e o nome dele é a URL. Este
aqui é `como-escrever-um-artigo-em-mdx.mdx`, por isso o endereço termina em
`/blog/como-escrever-um-artigo-em-mdx`.

> [!WARNING] Só neste blog: nome de arquivo sem acento
> Aqui o nome do arquivo precisa ser em minúsculas, com hífens e sem acento. Um
> `ação-rápida.mdx` quebra o build, e nada novo é publicado até ele ser
> renomeado. O site que já está no ar continua no ar. Outros blogs têm regras
> parecidas, e um nome sem acento nunca dá problema.

## Do celular até o site

<Figure
  src="/content/blog/como-escrever-um-artigo-em-mdx/fluxo.svg"
  alt="Quatro caixas em sequência, ligadas por setas: 1. Pages CMS, você escreve; 2. GitHub, cada Salvar vira um commit; 3. Vercel, o build valida e gera as páginas; 4. O site, no ar se não for rascunho."
  caption="O caminho de um texto neste blog, do primeiro rascunho até a publicação."
/>

Eu escrevo no [Pages CMS](https://pagescms.org), que mostra os arquivos do
repositório como um formulário. Cada vez que toco em **Salvar**, ele faz um
commit no GitHub, e a Vercel gera o site de novo. Não existe banco de dados nem
painel: o texto é um arquivo, e o histórico dele é o do Git.

### Se o seu blog usa outras ferramentas

A ideia é a mesma em qualquer combinação: o texto é um arquivo no repositório,
e cada mudança gera o site de novo. Só os nomes mudam.

| Etapa | Neste blog | Outras opções |
| --- | --- | --- |
| Escrever | Pages CMS | Decap CMS, TinaCMS, Keystatic, a extensão Front Matter no VS Code ou qualquer editor de texto |
| Guardar | GitHub | GitLab, Bitbucket |
| Publicar | Vercel | Netlify, Cloudflare Pages, GitHub Pages |
| Transformar o MDX em páginas | Vite | Astro, Next.js, Docusaurus |

Confira se as peças conversam entre si. O Pages CMS, por exemplo, só funciona
com repositórios no GitHub; o Decap CMS também aceita GitLab e Bitbucket.

## Os campos do formulário

A parte de cima do arquivo, entre os dois `---`, se chama frontmatter. Ela
existe em qualquer blog com Markdown. No Pages CMS ela vira o formulário; no
arquivo, fica assim:

```yaml title="frontmatter"
---
title: Como escrever um artigo em MDX neste blog
date: 2026-10-10
summary: Uma ou duas frases sobre o texto.
tags:
  - MDX
  - Markdown
draft: true
---
```

| Campo | Obrigatório | Para que serve |
| --- | --- | --- |
| Título | sim | Vai na capa e na aba do navegador. |
| Data de publicação | sim | Ordena o blog e as séries. |
| Resumo | sim | Aparece no Google, na listagem e embaixo do título. |
| Tags | não | Cada tag vira uma página em [/tags](/tags). A primeira define a cor do texto. |
| Série | não | Liga textos com o mesmo nome de série, na ordem da data. |
| Revisado | não | Só para quando mexer num texto já publicado. |
| Rascunho | não | Marcado, salva sem publicar. |
| Imagem de capa | não | Substitui a capa gerada. Quase sempre fica vazio. |
| Descrição da capa | só com capa | O que a imagem mostra, para quem usa leitor de tela. |

> [!NOTE] Só neste blog
> Os nomes dos campos são os deste site. `title` e `date` são quase universais;
> os outros mudam de um blog para outro, então confira a documentação do seu.

> [!TIP] Reaproveite as tags
> "React" e "ReactJS" virariam duas páginas diferentes. Antes de criar uma tag,
> veja as que já existem em [/tags](/tags).

### Rascunho ou publicado

<Gallery columns={2}>
  <Figure
    src="/content/blog/como-escrever-um-artigo-em-mdx/rascunho.svg"
    alt="Cartão draft: true. Salvo no GitHub: sim. Aparece no npm run dev: sim. Vai para o ar: não."
    caption="Rascunho: pode salvar à vontade."
  />
  <Figure
    src="/content/blog/como-escrever-um-artigo-em-mdx/publicado.svg"
    alt="Cartão draft: false. Vai para o ar no deploy, entra em /blog e no RSS, ganha capa e og:image."
    caption="Publicado: entra no próximo deploy."
  />
</Gallery>

Neste blog, um rascunho fica salvo no GitHub, mas não vai para o ar. A contrapartida é que,
pelo celular, não dá para ver como ele está ficando: só no computador, com
`npm run dev`.

## O básico do Markdown

Isto aqui é **negrito**, isto é *itálico*, isto é ~~riscado~~ e isto é
`código no meio da frase`. Um link para outro site abre uma prévia quando você
passa o mouse, como este para o [guia de Markdown do GitHub](https://docs.github.com/pt/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
Um link para dentro do blog é escrito com o caminho, como a [busca](/busca).

```md title="markdown"
Isto aqui é **negrito**, isto é *itálico*, isto é ~~riscado~~ e isto é
`código no meio da frase`.

[texto do link](https://exemplo.com)
[link interno](/busca)
```

Listas com marcador:

- uma ideia por item;
- itens curtos leem melhor no celular;
- dá para aninhar:
  - com dois espaços antes do hífen.

E numeradas, quando a ordem importa:

1. Escrever com **Rascunho** marcado.
2. Revisar no dia seguinte.
3. Desmarcar **Rascunho** e salvar.

```md title="listas"
- uma ideia por item
- dá para aninhar:
  - com dois espaços antes do hífen

1. Escrever com Rascunho marcado.
2. Revisar no dia seguinte.
```

Para atalhos de teclado existe a tag `kbd`: a busca do blog abre com
<kbd>Ctrl</kbd> + <kbd>K</kbd>.

```mdx title="teclas"
<kbd>Ctrl</kbd> + <kbd>K</kbd>
```

---

Uma linha com três hífens, sozinha, vira um separador como o de cima.

## Títulos e o índice

O título do texto já é o `#`. Dentro do texto, comece em `##` e use `###` para
as subseções. Com três títulos ou mais, aparece o índice **Neste artigo**: ao
lado do texto na tela grande, e num botão no canto da tela no celular.

```md title="títulos"
## Uma seção
### Uma subseção
```

> [!NOTE]
> Não pule níveis (de `##` direto para `####`). Quem navega com leitor de tela
> usa os títulos como sumário, e um nível pulado parece uma seção perdida.

## Destaques

Destaques chamam atenção para uma nota, uma dica ou um aviso. O jeito mais
simples é a sintaxe do GitHub[^1], que é Markdown comum:

> [!NOTE]
> Uma nota: informação extra, que não muda o sentido do texto.

> [!TIP]
> Uma dica: um atalho ou um jeito melhor de fazer.

> [!WARNING]
> Um aviso: algo que pode dar errado.

```md title="destaques"
> [!NOTE]
> Uma nota.

> [!TIP]
> Uma dica.

> [!WARNING] Um título só seu
> Um aviso, com título próprio na primeira linha.
```

Os nomes em português também funcionam: `[!NOTA]`, `[!DICA]` e `[!AVISO]`.

> [!NOTE] Só neste blog
> O MDX não transforma `> [!NOTE]` em destaque sozinho: aqui um plugin faz isso
> no build. Num blog sem ele, o bloco aparece como uma citação comum, com o
> `[!NOTE]` escrito. Os nomes em português e o `Callout` abaixo também são
> deste site.

Existe também o componente `Callout`, que faz exatamente o mesmo:

<Callout type="dica" title="Prefira a sintaxe do GitHub">

Ela é Markdown puro, então qualquer editor entende como citação e nada se perde
se o texto for salvo por uma ferramenta que não conhece MDX. O `Callout` fica
para quando você já estiver escrevendo outros componentes em volta.

</Callout>

```mdx title="callout"
<Callout type="dica" title="Prefira a sintaxe do GitHub">

O conteúdo, com uma linha em branco antes e depois.

</Callout>
```

## Blocos de código

Três crases abrem e fecham um bloco. A palavra depois das crases é a linguagem,
que define as cores. Cada bloco ganha uma barra com o nome e um botão
**copiar**.

```ts
export function soma(a: number, b: number) {
  return a + b;
}
```

Com `title`, a barra mostra o nome do arquivo:

```ts title="src/soma.ts"
export function soma(a: number, b: number) {
  return a + b;
}
```

Com `showLineNumbers`, as linhas são numeradas:

```ts title="src/soma.ts" showLineNumbers
export function soma(a: number, b: number) {
  return a + b;
}

export function media(valores: number[]) {
  return valores.reduce(soma, 0) / valores.length;
}
```

E com `bash`, o bloco vira um terminal:

```bash
npm run dev
```

````md title="como escrever"
```ts title="src/soma.ts" showLineNumbers
export function soma(a: number, b: number) {
  return a + b;
}
```

```bash
npm run dev
```
````

As cores são aplicadas no build pelo [Shiki](https://shiki.style), com um tema
para o claro e outro para o escuro. Nenhum código de realce vai para o
navegador de quem lê.

> [!NOTE] Só neste blog
> Três crases e a linguagem funcionam em qualquer lugar. A barra com o nome, o
> botão **copiar**, o `title`, o `showLineNumbers` e o terminal dependem de como
> o realce foi configurado aqui.

## Citações

Uma citação começa com `>`. Se a última linha começar com travessão, o botão
**copiar citação** inclui o autor.

> Programas devem ser escritos para pessoas lerem e, só incidentalmente, para
> máquinas executarem.
>
> — Harold Abelson, Structure and Interpretation of Computer Programs

```md title="citação"
> Programas devem ser escritos para pessoas lerem e, só incidentalmente, para
> máquinas executarem.
>
> — Harold Abelson
```

O `>` é Markdown comum. O botão **copiar citação** é deste blog.

## Notas de rodapé

Uma nota de rodapé guarda um detalhe sem interromper a frase[^2]. Tocar no
número abre a nota por baixo, sem sair do lugar; com o mouse, basta passar por
cima.

```md title="notas de rodapé"
Uma frase com nota.[^1]

[^1]: O texto da nota, que pode ficar em qualquer lugar do arquivo.
```

A sintaxe funciona em quase todo blog com Markdown. Abrir a nota por baixo,
sem rolar a página, é coisa deste site; em outros lugares, o número leva até o
fim do texto.

## Imagens

Em qualquer Markdown, uma imagem é assim:

```md title="imagem"
![O que a imagem mostra](/caminho/da/imagem.png)
```

> [!NOTE] Só neste blog
> `Figure` e `Gallery` são componentes que criei para ter legenda e imagens lado
> a lado. As imagens vão em `client/public/content/blog/` + o nome do texto, e
> são chamadas pelo caminho que começa em `/content`. No Pages CMS, o botão de
> mídia faz o upload para lá.

```mdx title="figura"
<Figure
  src="/content/blog/meu-texto/diagrama.png"
  alt="O que a imagem mostra, para quem não vê."
  caption="Legenda opcional, que aparece embaixo."
/>
```

Para duas ou três imagens lado a lado, como as do rascunho e do publicado lá em
cima, há a galeria:

```mdx title="galeria"
<Gallery columns={2}>
  <Figure src="/content/blog/meu-texto/a.png" alt="..." />
  <Figure src="/content/blog/meu-texto/b.png" alt="..." />
</Gallery>
```

> [!IMPORTANT] Sempre com alt
> O `alt` não é enfeite: é o que um leitor de tela lê no lugar da imagem, e o
> que aparece se ela não carregar. Descreva o que a imagem mostra, não "imagem".

## A capa que o blog gera

> [!NOTE] Só neste blog
> A capa é gerada por um script deste site. Muitos blogs fazem algo parecido,
> mas cada um de um jeito.

Um texto sem **Imagem de capa** ganha uma capa feita a partir do título, da
data, da primeira tag e do tempo de leitura. É a mesma imagem que aparece
quando alguém compartilha o link. A deste texto é esta:

<Figure
  src="/og/blog/como-escrever-um-artigo-em-mdx.png"
  alt="Capa gerada deste texto: o nome Carlos Eduardo e a data, o título Como escrever um artigo em MDX neste blog, a tag MDX e o tempo de leitura."
  caption="A imagem de compartilhamento deste texto, gerada no build."
/>

Por isso o campo de capa quase sempre fica vazio. Uma imagem ali substitui as
duas, a do topo e a de compartilhamento.

## O que vem de graça

Neste blog, nenhum destes precisa de código no texto:

- o tempo de leitura (passe o mouse para ver a que horas você termina);
- o índice, a partir de três títulos;
- os links para resumir o texto com IA, logo abaixo do resumo;
- compartilhar, comentários e textos relacionados no fim;
- a versão em Markdown do texto, para quem prefere a fonte[^3];
- a entrada no [RSS](/rss.xml) e no sitemap.

## Antes de publicar

1. Releia com **Rascunho** marcado, de preferência no dia seguinte.
2. Confira se o nome do arquivo ficou sem acento.
3. Veja se as tags já existem em [/tags](/tags).
4. Desmarque **Rascunho** e salve.
5. Se o texto tiver links para outros sites, rode `npm run links` no
   computador, para as prévias mostrarem título e descrição. Esse comando é
   deste blog.

```bash title="terminal"
npm run links
```

Pronto. Agora é só escrever.

[^1]: É a mesma sintaxe dos "alerts" do GitHub, então um README escrito assim
    fica igual no repositório e aqui.

[^2]: Como esta. Ela fica no fim do texto, em **Notas de rodapé**, e também
    abre aqui mesmo, sem rolar a página.

[^3]: Acrescente `/index.md` ao endereço do texto. A página também anuncia
    essa versão no cabeçalho, para ferramentas e leitores de feed encontrarem.
