<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>felipeweber.com – felipeweber.com</title><link>/</link><description>Recent content on felipeweber.com</description><generator>Hugo -- gohugo.io</generator><language>pt</language><managingEditor>weberecomp@gmail.com (Felipe Weber)</managingEditor><webMaster>weberecomp@gmail.com (Felipe Weber)</webMaster><lastBuildDate>Wed, 02 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>Publicando um site Hugo na Cloudflare Pages sem dor de cabeça</title><link>/2026/09/02/cloudflare-pages-hugo/</link><pubDate>Wed, 02 Sep 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/09/02/cloudflare-pages-hugo/</guid><description>
&lt;p>Colocar um site estático no ar deixou de ser um projeto. O que ainda consome
tempo é a primeira meia hora — a que separa &amp;ldquo;funciona na minha máquina&amp;rdquo; de
&amp;ldquo;funciona em produção com HTTPS e deploy automático&amp;rdquo;.&lt;/p>
&lt;p>Este é o caminho que uso hoje.&lt;/p>
&lt;h2>O contrato mínimo&lt;span class="hx:absolute hx:-mt-20" id="o-contrato-mínimo">&lt;/span>
&lt;a href="#o-contrato-m%c3%adnimo" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>A Cloudflare Pages precisa de três coisas: um repositório Git, um comando de
build e um diretório de saída. Para Hugo:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build command&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo --gc --minify
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Build output directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">public&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>O resto é configuração de ambiente. E é aqui que quase todo primeiro deploy
falha.&lt;/p>
&lt;h2>Armadilha 1: a versão do Hugo&lt;span class="hx:absolute hx:-mt-20" id="armadilha-1-a-versão-do-hugo">&lt;/span>
&lt;a href="#armadilha-1-a-vers%c3%a3o-do-hugo" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O runner da Pages não usa a mesma versão que você tem localmente. Sem fixar,
você recebe uma versão antiga — normalmente sem o build extended, o que quebra
qualquer tema que use SCSS.&lt;/p>
&lt;p>Defina a variável de ambiente no painel do projeto:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">HUGO_VERSION&lt;/span> &lt;span class="o">=&lt;/span> 0.165.0&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;blockquote>
&lt;p>Fixe a versão no repositório também, num arquivo que o time consiga ler.
Uma variável de ambiente invisível é uma dívida técnica esperando acontecer.&lt;/p>
&lt;/blockquote>
&lt;h2>Armadilha 2: Hugo Modules precisa de Go&lt;span class="hx:absolute hx:-mt-20" id="armadilha-2-hugo-modules-precisa-de-go">&lt;/span>
&lt;a href="#armadilha-2-hugo-modules-precisa-de-go" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Se o tema vem como Hugo Module — e hoje a maioria vem — o runner precisa do Go
disponível antes do &lt;code>hugo&lt;/code> rodar:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nv">GO_VERSION&lt;/span> &lt;span class="o">=&lt;/span> 1.27.1&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Sem isso o build morre com um &lt;code>module not found&lt;/code> que não diz nada sobre a
causa real.&lt;/p>
&lt;h2>Armadilha 3: &lt;code>baseURL&lt;/code> e os previews&lt;span class="hx:absolute hx:-mt-20" id="armadilha-3-baseurl-e-os-previews">&lt;/span>
&lt;a href="#armadilha-3-baseurl-e-os-previews" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Cada branch gera uma URL de preview própria. Se o &lt;code>baseURL&lt;/code> estiver cravado no
config, os links absolutos do preview apontam para produção. A saída é deixar
a própria Pages injetar o valor:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo --gc --minify --baseURL &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$CF_PAGES_URL&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Em produção o &lt;code>CF_PAGES_URL&lt;/code> é o domínio final, então o comando serve para os
dois casos sem &lt;code>if&lt;/code>.&lt;/p>
&lt;h2>O que você ganha de graça&lt;span class="hx:absolute hx:-mt-20" id="o-que-você-ganha-de-graça">&lt;/span>
&lt;a href="#o-que-voc%c3%aa-ganha-de-gra%c3%a7a" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Feito isso, cada &lt;code>git push&lt;/code> na &lt;code>main&lt;/code> vira um deploy. Branches viram previews.
O certificado é emitido e renovado sozinho. E o custo continua zero até um
volume de tráfego que a maioria dos blogs pessoais nunca vai ver.&lt;/p>
&lt;p>A documentação oficial está em
&lt;a href="https://developers.cloudflare.com/pages/"target="_blank" rel="noopener">developers.cloudflare.com/pages&lt;/a> e
vale a leitura da seção de build configuration antes do primeiro deploy.&lt;/p></description></item><item><title>Hugo Modules na prática: temas versionados sem submódulos</title><link>/2026/08/18/hugo-modules-na-pratica/</link><pubDate>Tue, 18 Aug 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/08/18/hugo-modules-na-pratica/</guid><description>
&lt;p>Todo tutorial de Hugo começa com &lt;code>git submodule add&lt;/code>. Funciona, mas transforma
o tema numa cópia congelada dentro do seu repositório, sem versão declarada e
sem caminho de upgrade.&lt;/p>
&lt;p>Hugo Modules resolve isso tratando o tema como o que ele é: uma dependência.&lt;/p>
&lt;h2>O setup&lt;span class="hx:absolute hx:-mt-20" id="o-setup">&lt;/span>
&lt;a href="#o-setup" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo mod init github.com/seu-usuario/seu-site
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo mod get github.com/imfing/hextra&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>O &lt;code>go.mod&lt;/code> gerado registra a versão exata:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;pre>&lt;code>module github.com/seu-usuario/seu-site
go 1.27.1
require github.com/imfing/hextra v0.12.3&lt;/code>&lt;/pre>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>No config, o import substitui o antigo &lt;code>theme:&lt;/code>:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">module&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">imports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">path&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">github.com/imfing/hextra&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;h2>Por que isso é melhor&lt;span class="hx:absolute hx:-mt-20" id="por-que-isso-é-melhor">&lt;/span>
&lt;a href="#por-que-isso-%c3%a9-melhor" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Upgrade vira um comando, e rollback também:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">hugo mod get -u github.com/imfing/hextra &lt;span class="c1"># última versão&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">hugo mod get github.com/imfing/hextra@v0.12.3 &lt;span class="c1"># volta para uma específica&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Nenhum arquivo do tema entra no seu repositório. O diff de um upgrade é uma
linha no &lt;code>go.mod&lt;/code>.&lt;/p>
&lt;h2>Override arquivo a arquivo&lt;span class="hx:absolute hx:-mt-20" id="override-arquivo-a-arquivo">&lt;/span>
&lt;a href="#override-arquivo-a-arquivo" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>A parte que mais me convenceu: os arquivos do projeto vencem os do módulo, um
a um. Se você quer trocar só o rodapé, cria só o rodapé:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;pre>&lt;code>layouts/_partials/footer.html&lt;/code>&lt;/pre>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>O resto do tema continua vindo do módulo e continua recebendo correções nos
upgrades. Comparado a copiar o tema inteiro para dentro do repo, a diferença
de manutenção é enorme.&lt;/p>
&lt;blockquote>
&lt;p>A regra prática: se você copiou mais de três arquivos do tema, revise se o
problema não é de configuração.&lt;/p>
&lt;/blockquote>
&lt;h2>O custo&lt;span class="hx:absolute hx:-mt-20" id="o-custo">&lt;/span>
&lt;a href="#o-custo" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Você precisa do Go instalado — localmente e no CI. É o preço de entrada, e
vale, porque a partir daí o tema para de ser um bloco opaco no seu histórico
de commits.&lt;/p>
&lt;p>O código do Hextra está em
&lt;a href="https://github.com/imfing/hextra"target="_blank" rel="noopener">github.com/imfing/hextra&lt;/a> e serve de
referência boa para ver como um tema modular se organiza.&lt;/p></description></item><item><title>Um índice de busca estático que cabe em 40 KB</title><link>/2026/08/04/indice-de-busca-estatico/</link><pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/08/04/indice-de-busca-estatico/</guid><description>
&lt;p>Busca em site estático tem três caminhos: serviço externo pago, servidor
próprio, ou um índice JSON gerado no build e consultado no navegador. Para um
blog, o terceiro é quase sempre o certo — desde que o índice não engorde.&lt;/p>
&lt;h2>O que faz o índice crescer&lt;span class="hx:absolute hx:-mt-20" id="o-que-faz-o-índice-crescer">&lt;/span>
&lt;a href="#o-que-faz-o-%c3%adndice-crescer" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O erro comum é indexar o conteúdo inteiro de cada post. Cinquenta artigos de
1.500 palavras viram um JSON de vários megabytes, que o visitante baixa antes
de digitar a primeira letra.&lt;/p>
&lt;p>O que realmente importa para uma busca de blog:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Campo&lt;/th>
&lt;th>Serve para&lt;/th>
&lt;th>Custo&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>título&lt;/td>
&lt;td>achar o post&lt;/td>
&lt;td>baixíssimo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>descrição&lt;/td>
&lt;td>contexto no resultado&lt;/td>
&lt;td>baixo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>headings&lt;/td>
&lt;td>achar a seção&lt;/td>
&lt;td>baixo&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>corpo completo&lt;/td>
&lt;td>achar uma frase exata&lt;/td>
&lt;td>altíssimo&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Na prática, título + descrição + headings resolvem quase toda busca real e
mantêm o índice em dezenas de kilobytes.&lt;/p>
&lt;h2>Gerando o índice no build&lt;span class="hx:absolute hx:-mt-20" id="gerando-o-índice-no-build">&lt;/span>
&lt;a href="#gerando-o-%c3%adndice-no-build" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Em Hugo, o índice é só mais um template de saída. A parte relevante:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-go-html-template" data-lang="go-html-template">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{{-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">:=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">slice&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">-}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{{-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">range&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">where&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">site&lt;/span>&lt;span class="na">.RegularPages&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Section&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;blog&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">-}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cp">{{-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">append&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nx">dict&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Title&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;url&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.RelPermalink&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;desc&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Description&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;headings&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nx">apply&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="na">.Fragments.Headings&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;index&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;Title&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">-}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{{-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">end&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">-}}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{{&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">$index&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nx">jsonify&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="cp">}}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Nenhum passo extra no pipeline: o arquivo nasce junto com o HTML.&lt;/p>
&lt;h2>Carregue tarde&lt;span class="hx:absolute hx:-mt-20" id="carregue-tarde">&lt;/span>
&lt;a href="#carregue-tarde" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O índice não precisa existir no primeiro paint. Busque no primeiro foco do
campo:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">let&lt;/span> &lt;span class="nx">indexPromise&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;focus&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">indexPromise&lt;/span> &lt;span class="o">??=&lt;/span> &lt;span class="nx">fetch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/search-index.json&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">then&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">r&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">r&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">once&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">true&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Quem nunca usa a busca nunca paga por ela. Quem usa paga uma vez, e o
navegador cacheia.&lt;/p>
&lt;blockquote>
&lt;p>A melhor otimização continua sendo não baixar o arquivo.&lt;/p>
&lt;/blockquote>
&lt;h2>Onde isso quebra&lt;span class="hx:absolute hx:-mt-20" id="onde-isso-quebra">&lt;/span>
&lt;a href="#onde-isso-quebra" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Se o seu caso de uso é achar uma frase exata dentro de um artigo longo — um
manual, uma base de conhecimento — o índice reduzido não serve. Aí vale
avaliar &lt;a href="https://pagefind.app/"target="_blank" rel="noopener">Pagefind&lt;/a>, que fragmenta o índice e baixa só
os pedaços necessários.&lt;/p>
&lt;p>Para um blog, é otimizar um problema que você não tem.&lt;/p></description></item><item><title>Dark mode sem flash: o que acontece antes do primeiro paint</title><link>/2026/07/21/dark-mode-sem-flash/</link><pubDate>Tue, 21 Jul 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/07/21/dark-mode-sem-flash/</guid><description>
&lt;p>Você implementa dark mode, testa, funciona. Aí recarrega a página e vê um
lampejo branco antes do tema escuro aparecer. O famoso FOUC.&lt;/p>
&lt;p>A causa não está no CSS. Está em quando o JavaScript roda.&lt;/p>
&lt;h2>A corrida&lt;span class="hx:absolute hx:-mt-20" id="a-corrida">&lt;/span>
&lt;a href="#a-corrida" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O navegador monta a página em ordem: lê o HTML, encontra o CSS, calcula o
estilo, pinta. Se o script que decide o tema roda &lt;em>depois&lt;/em> dessa pintura, o
usuário vê o tema padrão primeiro.&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;pre>&lt;code>parse HTML → carrega CSS → primeiro paint → DOMContentLoaded → seu script
▲ │
flash acontece aqui aplica o tema aqui&lt;/code>&lt;/pre>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Qualquer script com &lt;code>defer&lt;/code>, &lt;code>async&lt;/code> ou preso ao &lt;code>DOMContentLoaded&lt;/code> perde a
corrida por construção.&lt;/p>
&lt;h2>A correção&lt;span class="hx:absolute hx:-mt-20" id="a-correção">&lt;/span>
&lt;a href="#a-corre%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Um script &lt;strong>síncrono e inline&lt;/strong>, no &lt;code>&amp;lt;head&amp;gt;&lt;/code>, antes de qualquer folha de
estilo que dependa do tema:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">stored&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;color-theme&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">theme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">stored&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s2">&amp;#34;light&amp;#34;&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nx">stored&lt;/span> &lt;span class="o">===&lt;/span> &lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">?&lt;/span> &lt;span class="nx">stored&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nb">window&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">matchMedia&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;(prefers-color-scheme: dark)&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">matches&lt;/span> &lt;span class="o">?&lt;/span> &lt;span class="s2">&amp;#34;dark&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;light&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">classList&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">theme&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">documentElement&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">style&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">colorScheme&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">theme&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Ele bloqueia o parser por menos de um milissegundo e a classe já está no
&lt;code>&amp;lt;html&amp;gt;&lt;/code> quando o primeiro pixel aparece.&lt;/p>
&lt;h2>Os dois detalhes que quase todo mundo esquece&lt;span class="hx:absolute hx:-mt-20" id="os-dois-detalhes-que-quase-todo-mundo-esquece">&lt;/span>
&lt;a href="#os-dois-detalhes-que-quase-todo-mundo-esquece" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>&lt;strong>&lt;code>color-scheme&lt;/code>.&lt;/strong> Sem ele, o navegador continua pintando as barras de
rolagem, os campos de formulário e o fundo padrão em claro. É uma linha, e é a
diferença entre &amp;ldquo;escuro&amp;rdquo; e &amp;ldquo;escuro de verdade&amp;rdquo;.&lt;/p>
&lt;p>&lt;strong>&lt;code>localStorage&lt;/code> pode explodir.&lt;/strong> Em modo privado de alguns navegadores, o
acesso lança exceção. Envolva em &lt;code>try/catch&lt;/code> — o site precisa carregar mesmo
quando a preferência não pode ser lida.&lt;/p>
&lt;blockquote>
&lt;p>Três estados, não dois: claro, escuro e &lt;em>sistema&lt;/em>. &amp;ldquo;Sistema&amp;rdquo; é o padrão
correto, e é o único que respeita quem mudou a preferência no sistema
operacional depois de visitar seu site.&lt;/p>
&lt;/blockquote>
&lt;h2>Verificando&lt;span class="hx:absolute hx:-mt-20" id="verificando">&lt;/span>
&lt;a href="#verificando" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O teste honesto é com throttling de CPU ligado, em 6x slowdown, com o cache
desabilitado. O flash que você não vê no seu desktop aparece na hora em um
celular intermediário.&lt;/p></description></item><item><title>Escrevendo CSS que sobrevive ao próximo redesign</title><link>/2026/06/30/css-que-sobrevive/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/06/30/css-que-sobrevive/</guid><description>
&lt;p>Todo projeto de frontend chega no ponto em que ninguém quer mexer no CSS. Não
porque está mal escrito — porque ninguém sabe o que quebra.&lt;/p>
&lt;p>O sintoma é sempre o mesmo: a mesma decisão está escrita em quinze lugares.&lt;/p>
&lt;h2>Tokens antes de componentes&lt;span class="hx:absolute hx:-mt-20" id="tokens-antes-de-componentes">&lt;/span>
&lt;a href="#tokens-antes-de-componentes" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Antes de estilizar qualquer coisa, defina o vocabulário:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">:&lt;/span>&lt;span class="nd">root&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--surface&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#faf7f0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#23201c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text-muted&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">rgb&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">35&lt;/span> &lt;span class="mi">32&lt;/span> &lt;span class="mi">28&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">62&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#9a5b18&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--measure&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">68&lt;/span>&lt;span class="kt">ch&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Trocar o tema inteiro passa a ser trocar esse bloco. Sem tokens, é um
find-and-replace com risco.&lt;/p>
&lt;h2>Um tema, uma inversão&lt;span class="hx:absolute hx:-mt-20" id="um-tema-uma-inversão">&lt;/span>
&lt;a href="#um-tema-uma-invers%c3%a3o" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Dark mode não é um segundo CSS. É a mesma folha com os tokens redefinidos:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">html&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">dark&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--surface&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#1a1714&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--text&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#ece7de&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">--accent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#e2a05a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Se você precisou escrever uma regra &lt;code>html.dark .componente { ... }&lt;/code>, o
componente está lendo uma cor que deveria ser um token.&lt;/p>
&lt;h2>&lt;code>color-mix&lt;/code> mata a paleta de doze tons&lt;span class="hx:absolute hx:-mt-20" id="color-mix-mata-a-paleta-de-doze-tons">&lt;/span>
&lt;a href="#color-mix-mata-a-paleta-de-doze-tons" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Metade das variáveis de um design system são variações de opacidade da mesma
cor. &lt;code>color-mix&lt;/code> gera isso na hora:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="kt">px&lt;/span> &lt;span class="kc">solid&lt;/span> &lt;span class="nf">color-mix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">in&lt;/span> &lt;span class="n">srgb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="kc">text&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">transparent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">card&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="nd">hover&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">border-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nf">color-mix&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">in&lt;/span> &lt;span class="n">srgb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">var&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">--&lt;/span>&lt;span class="n">accent&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="mi">48&lt;/span>&lt;span class="kt">%&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">transparent&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>A borda acompanha o tema sozinha, nos dois modos, sem nenhuma variável nova.&lt;/p>
&lt;h2>Medida de leitura é uma decisão, não um acidente&lt;span class="hx:absolute hx:-mt-20" id="medida-de-leitura-é-uma-decisão-não-um-acidente">&lt;/span>
&lt;a href="#medida-de-leitura-%c3%a9-uma-decis%c3%a3o-n%c3%a3o-um-acidente" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="cl">&lt;span class="p">.&lt;/span>&lt;span class="nc">prose&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">max-width&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">68&lt;/span>&lt;span class="kt">ch&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">line-height&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mf">1.7&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Duas linhas que fazem mais pela legibilidade do que qualquer escolha de fonte.
&lt;code>ch&lt;/code> acompanha o tamanho do texto — se a fonte cresce, a medida cresce junto.&lt;/p>
&lt;blockquote>
&lt;p>Se você não consegue explicar por que um valor é &lt;code>1.7&lt;/code> e não &lt;code>1.5&lt;/code>, ele vai
virar &lt;code>1.6&lt;/code> no próximo PR e ninguém vai saber dizer se melhorou.&lt;/p>
&lt;/blockquote>
&lt;h2>O teste&lt;span class="hx:absolute hx:-mt-20" id="o-teste">&lt;/span>
&lt;a href="#o-teste" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>Pegue o CSS e tente mudar a cor de acento do site inteiro. Se der para fazer
em uma linha, ele sobrevive ao próximo redesign. Se precisar abrir cinco
arquivos, você já sabe onde começar a refatorar.&lt;/p></description></item><item><title>Medindo Core Web Vitals de verdade em um site estático</title><link>/2026/05/14/core-web-vitals-de-verdade/</link><pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate><author>weberecomp@gmail.com (Felipe Weber)</author><guid>/2026/05/14/core-web-vitals-de-verdade/</guid><description>
&lt;p>Site estático tem uma vantagem injusta em performance: não há servidor
pensando, não há hidratação, não há waterfall de API. É fácil tirar 100 no
Lighthouse.&lt;/p>
&lt;p>E é exatamente por isso que o número engana.&lt;/p>
&lt;h2>Lab não é campo&lt;span class="hx:absolute hx:-mt-20" id="lab-não-é-campo">&lt;/span>
&lt;a href="#lab-n%c3%a3o-%c3%a9-campo" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>O Lighthouse roda uma simulação: uma máquina, uma rede, um carregamento frio.
Os Core Web Vitals que o Google usa vêm do CrUX — usuários reais, aparelhos
reais, redes reais, no percentil 75.&lt;/p>
&lt;p>Os dois discordam com frequência, e o campo é quem manda.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Medição&lt;/th>
&lt;th>Onde vive&lt;/th>
&lt;th>O que responde&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Lighthouse&lt;/td>
&lt;td>seu terminal / CI&lt;/td>
&lt;td>&amp;ldquo;eu regredi desde o último commit?&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CrUX&lt;/td>
&lt;td>dados reais agregados&lt;/td>
&lt;td>&amp;ldquo;meus visitantes estão sofrendo?&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RUM próprio&lt;/td>
&lt;td>seu site&lt;/td>
&lt;td>&amp;ldquo;quais páginas, quais aparelhos?&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2>As três métricas, em uma frase cada&lt;span class="hx:absolute hx:-mt-20" id="as-três-métricas-em-uma-frase-cada">&lt;/span>
&lt;a href="#as-tr%c3%aas-m%c3%a9tricas-em-uma-frase-cada" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>&lt;strong>LCP&lt;/strong> — quando o maior elemento visível terminou de pintar. Num blog, quase
sempre é o título ou a primeira imagem. Fonte web mal configurada atrasa isso
mais do que qualquer imagem.&lt;/p>
&lt;p>&lt;strong>INP&lt;/strong> — quanto tempo a página leva para responder a uma interação. Site
estático raramente falha aqui, a menos que algum script de terceiro esteja
segurando a thread principal.&lt;/p>
&lt;p>&lt;strong>CLS&lt;/strong> — o quanto o conteúdo pula durante o carregamento. Causa número um:
imagem sem &lt;code>width&lt;/code>/&lt;code>height&lt;/code>. Causa número dois: fonte que troca e muda a
altura do texto.&lt;/p>
&lt;h2>Medindo no seu próprio site&lt;span class="hx:absolute hx:-mt-20" id="medindo-no-seu-próprio-site">&lt;/span>
&lt;a href="#medindo-no-seu-pr%c3%b3prio-site" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;p>A biblioteca oficial cabe em poucas linhas e reporta números reais:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">onLCP&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onINP&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">onCLS&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;web-vitals&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">metric&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">navigator&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">sendBeacon&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;/vitals&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">stringify&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">metric&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">value&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">metric&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">value&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">rating&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">metric&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">rating&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">path&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">location&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">pathname&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onLCP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onINP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">onCLS&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">);&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copiar código"
aria-label="Copiar código"
data-copied-label="Copiado!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>&lt;code>sendBeacon&lt;/code> não segura a navegação — o envio acontece mesmo quando o usuário
já saiu da página.&lt;/p>
&lt;h2>O que corrigir primeiro num site estático&lt;span class="hx:absolute hx:-mt-20" id="o-que-corrigir-primeiro-num-site-estático">&lt;/span>
&lt;a href="#o-que-corrigir-primeiro-num-site-est%c3%a1tico" class="subheading-anchor" aria-label="Link permanente para esta secção">&lt;/a>&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Fontes.&lt;/strong> &lt;code>font-display: swap&lt;/code>, &lt;code>preload&lt;/code> só nos arquivos usados acima da
dobra, e servidas do seu domínio.&lt;/li>
&lt;li>&lt;strong>Dimensões de imagem.&lt;/strong> Sempre &lt;code>width&lt;/code> e &lt;code>height&lt;/code>, mesmo com CSS
responsivo por cima.&lt;/li>
&lt;li>&lt;strong>Scripts de terceiro.&lt;/strong> Cada um deles é um risco de INP que você não
controla.&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>Otimizar de dentro para fora: primeiro o que você serve, depois o que você
pede emprestado.&lt;/p>
&lt;/blockquote>
&lt;p>A referência boa continua sendo &lt;a href="https://web.dev/vitals/"target="_blank" rel="noopener">web.dev/vitals&lt;/a>, que
acompanha as mudanças de definição das métricas ao longo do tempo.&lt;/p></description></item></channel></rss>