Fontes web podem melhorar muito a identidade visual, mas também são uma das causas mais comuns de lentidão percebida e instabilidade visual. O objetivo aqui é carregar fontes com previsibilidade (rapidez) e com o mínimo de “troca” de texto na tela (estabilidade), combinando três frentes: preload (antecipar), subset (reduzir) e fallbacks (degradar bem).
O que acontece quando uma fonte web carrega
Quando o navegador encontra um CSS com @font-face, ele precisa: (1) baixar o CSS (ou receber inline), (2) descobrir as fontes necessárias (arquivos .woff2, .woff), (3) baixar esses arquivos e (4) aplicar a fonte ao texto. O detalhe importante é que o texto pode aparecer antes da fonte estar pronta, e depois “trocar” para a fonte final. Essa troca pode causar mudança de largura/altura de linhas e, portanto, deslocamentos visuais.
Existem dois comportamentos ruins clássicos: FOIT (Flash of Invisible Text) quando o texto fica invisível esperando a fonte, e FOUT (Flash of Unstyled Text) quando o texto aparece com fallback e depois troca para a fonte final. Na prática, você quer evitar invisibilidade prolongada e, quando houver troca, quer que ela seja o mais imperceptível possível.
Escolhendo a estratégia: rapidez sem instabilidade
Uma estratégia robusta costuma seguir esta ordem de prioridade:
- Reduzir o custo do download e parsing da fonte (subset, menos pesos, menos estilos, WOFF2).
- Antecipar o download do que é realmente crítico (preload seletivo).
- Controlar o comportamento de renderização com
font-displaye fallbacks bem calibrados (incluindosize-adjust). - Evitar solicitações desnecessárias (cache, versionamento, self-host quando fizer sentido, e não carregar pesos que não usa).
Preload de fontes: quando e como usar
O que é preload e por que ajuda
<link rel="preload"> avisa ao navegador que um recurso será necessário em breve, permitindo iniciar o download mais cedo. Para fontes, isso pode reduzir o tempo até a fonte estar disponível e diminuir o período em que o texto usa fallback (ou fica invisível, dependendo do font-display).
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
O preload é mais efetivo quando a fonte é usada em conteúdo acima da dobra e quando o CSS que referencia a fonte não chega cedo o suficiente. Mas ele também pode ser prejudicial se você preloader fontes demais: você compete por banda com CSS/JS e pode atrasar recursos mais importantes.
Regras práticas para preload de fontes
- Preload apenas do essencial: normalmente 1 fonte (ou 1–2 arquivos) que aparece no primeiro viewport. Evite preloader todos os pesos.
- Preload do formato certo: priorize
.woff2. Se você já tem fallback para navegadores antigos, não precisa preloader.woffna maioria dos casos. - Use
as="font"etypepara que o navegador trate corretamente. - Inclua
crossoriginquando a fonte for servida com CORS (muito comum). Mesmo em self-host, é comum mantercrossoriginpara consistência. - Garanta que o preload corresponda ao que será usado: se o CSS pede
font-weight: 700e você preloader apenas o 400, você não resolve o problema.
Exemplo de preload no HTML
<!-- No <head> --> <link rel="preload" href="/fonts/Inter-roman-subset.woff2" as="font" type="font/woff2" crossorigin>Esse preload deve apontar para o arquivo exato que será usado no CSS. Se você usa fontes variáveis, muitas vezes um único arquivo variável cobre vários pesos e reduz a necessidade de múltiplos preloads.
Preload + CSS: garantindo que o navegador use o que você preloader
O preload sozinho não aplica a fonte; ele apenas baixa. Você ainda precisa do @font-face:
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-roman-subset.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap;}Se o arquivo for variável, use intervalos de peso. Se não for variável, declare pesos específicos e evite declarar pesos que não existem.
Subset de fontes: menos bytes, menos atraso
O que é subset
Subset é gerar uma versão da fonte contendo apenas os glifos (caracteres) necessários para o seu site. Fontes completas podem ter milhares de glifos (latim estendido, cirílico, símbolos, etc.). Se seu produto usa apenas português e alguns símbolos, você pode remover o restante e reduzir bastante o tamanho.
Subset também pode significar reduzir variações: remover itálico se você não usa, remover pesos intermediários, ou preferir uma fonte variável única em vez de vários arquivos estáticos.
Passo a passo prático: criando subset com fonttools (pyftsubset)
Uma forma comum é usar o fonttools (Python) para gerar subset. Exemplo de fluxo:
- 1) Instale o fonttools no ambiente de build.
- 2) Defina quais caracteres você precisa (por idioma, por páginas críticas ou por conjunto de UI).
- 3) Gere um arquivo WOFF2 subsetado.
- 4) Atualize o CSS para apontar para o subset.
Comandos típicos (ajuste caminhos e nomes):
# Instalação (uma vez) pip install fonttools brotli # brotli ajuda no woff2 em alguns ambientes # Gerar subset para Latin básico + acentos comuns em PT-BR pyftsubset ./src-fonts/Inter-VariableFont_slnt,wght.ttf --output-file=./public/fonts/Inter-roman-subset.woff2 --flavor=woff2 --layout-features='*' --unicodes='U+0000-00FF,U+0100-017F,U+0180-024F,U+1E00-1EFF' --with-zopfliO parâmetro --unicodes define intervalos. Para português, normalmente você precisa de Latin-1 Supplement e Latin Extended-A (e às vezes Extended-B). Se você usa símbolos específicos (setas, ícones em texto, bullets especiais), inclua os codepoints correspondentes.
Outra abordagem é subsetting por texto real (útil para landing pages com texto fixo):
pyftsubset ./src-fonts/MinhaFonte.ttf --output-file=./public/fonts/MinhaFonte-subset.woff2 --flavor=woff2 --text-file=./scripts/texto-critico.txtEssa técnica pode gerar arquivos muito pequenos, mas exige cuidado: se o texto mudar (CMS, i18n, A/B), caracteres podem faltar e aparecer “quadradinhos”. Para produtos com conteúdo dinâmico, prefira subset por intervalos de Unicode ou por idioma.
Subsets por faixa: crítico vs completo
Um padrão eficiente é ter dois níveis:
- Subset crítico: cobre o essencial para o primeiro viewport (UI, menu, headline). Pode ser preloaded.
- Fonte completa (ou menos restrita): carregada depois, para páginas longas, conteúdo gerado por usuário, idiomas adicionais.
Isso pode ser feito com unicode-range em múltiplos @font-face. O navegador baixa apenas o arquivo que cobre os caracteres usados.
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-latin-subset.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap; unicode-range: U+0000-00FF, U+0100-017F;} @font-face { font-family: 'Inter'; src: url('/fonts/Inter-latin-ext.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap; unicode-range: U+0180-024F, U+1E00-1EFF;}Se a maior parte dos usuários só usa o primeiro conjunto, o segundo arquivo raramente será baixado.
Fallbacks e estabilidade: evitando “pulos” quando a fonte troca
font-display: controlando FOIT/FOUT
O font-display define como o navegador se comporta enquanto a fonte não chegou. Valores comuns:
- swap: mostra fallback imediatamente e troca quando a fonte chega. Bom para evitar texto invisível, mas pode causar mudança de layout.
- optional: semelhante ao swap, mas o navegador pode decidir não trocar se a fonte demorar; favorece estabilidade e rapidez percebida em conexões lentas.
- block: pode esconder texto por um período (evite na maioria dos casos).
Para a maioria das interfaces, swap é um padrão seguro, mas se você sofre com instabilidade por troca de fonte, optional pode ser um bom teste, especialmente em páginas onde a tipografia customizada não é crítica para compreensão.
Escolhendo uma boa fonte fallback
Fallback não é “qualquer sans-serif”. A troca fica menos perceptível quando a fonte fallback tem métricas parecidas (largura média, altura-x, espaçamento). Uma prática comum é:
- Definir uma pilha de fallback com fontes do sistema (rápidas e disponíveis).
- Preferir fallbacks com métricas próximas da fonte final (por exemplo, para uma fonte humanista, usar fallbacks humanistas).
Exemplo de pilha:
body { font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;}Isso garante que o texto apareça imediatamente com uma fonte local, enquanto a webfont carrega.
Ajustando métricas com size-adjust, ascent-override e descent-override
Mesmo com uma boa pilha, a troca pode causar mudança de largura e altura de linhas. CSS Fonts Level 4 oferece propriedades para aproximar as métricas da fallback às da fonte final. A ideia é declarar um @font-face “fallback” com ajustes, e usá-lo como primeira opção até a fonte real carregar (ou como fallback permanente).
Exemplo (valores ilustrativos; você deve medir e ajustar):
@font-face { font-family: 'Inter Fallback'; src: local('Arial'); ascent-override: 90%; descent-override: 22%; line-gap-override: 0%; size-adjust: 107%;} body { font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;}Com size-adjust, você altera o tamanho efetivo da fallback para aproximar a largura dos textos. Com ascent-override e descent-override, você reduz mudanças de altura de linha. Isso tende a diminuir deslocamentos visuais quando a fonte final substitui a fallback.
Como obter bons valores? Você pode usar ferramentas que comparam métricas entre fontes (existem scripts e utilitários na comunidade) ou medir visualmente em um ambiente de teste: renderize o mesmo texto com a fonte final e com a fallback, ajuste até que quebras de linha e alturas fiquem próximas.
Evite “falsos itálicos” e pesos inexistentes
Se você define font-style: italic mas não fornece um arquivo itálico real, o navegador pode sintetizar itálico (obliquar artificialmente). O mesmo vale para pesos: pedir 600 quando só existe 400 e 700 pode gerar síntese. Isso pode mudar métricas e causar instabilidade. Garanta que:
- Você só usa pesos/estilos que realmente carrega.
- Ou você aceita explicitamente a síntese e testa o impacto visual.
Passo a passo: implementando uma configuração completa (preload + subset + fallbacks)
1) Inventarie o que você realmente usa
Liste famílias, pesos e estilos usados no CSS. Um erro comum é carregar 4–6 arquivos (400/500/600/700 + itálico) quando a UI usa apenas 400 e 600, por exemplo. Reduzir arquivos é uma das maiores vitórias.
2) Gere um subset WOFF2 para o idioma principal
Crie um arquivo subsetado para Latin + acentos do seu público principal. Se você tem i18n, considere separar por idioma com unicode-range ou carregar fontes adicionais sob demanda.
3) Publique com cache forte e versionamento
Fontes mudam pouco. Sirva com cache longo e nome com hash (ou versão) para permitir Cache-Control agressivo sem risco de conteúdo desatualizado. Exemplo de nome: Inter-roman-subset.v3.woff2.
4) Adicione preload apenas para a fonte crítica
Escolha o arquivo que impacta o texto acima da dobra. Se você usa uma fonte variável única para o corpo, geralmente basta preloader esse arquivo subsetado.
<link rel="preload" href="/fonts/Inter-roman-subset.v3.woff2" as="font" type="font/woff2" crossorigin>5) Configure @font-face com font-display adequado
Para a maioria dos casos, comece com swap. Se você observar muita troca perceptível em conexões lentas, experimente optional em um teste controlado.
@font-face { font-family: 'Inter'; src: url('/fonts/Inter-roman-subset.v3.woff2') format('woff2'); font-weight: 100 900; font-style: normal; font-display: swap;}6) Defina uma pilha de fallback e, se necessário, ajuste métricas
Primeiro, use uma pilha simples com fontes do sistema. Se ainda houver instabilidade, crie um fallback ajustado com size-adjust e overrides.
@font-face { font-family: 'Inter Fallback'; src: local('Segoe UI'); size-adjust: 105%; ascent-override: 92%; descent-override: 24%; line-gap-override: 0%;} :root { --font-sans: 'Inter', 'Inter Fallback', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;} body { font-family: var(--font-sans);}7) Valide se não há downloads duplicados
Erros comuns que geram duplicidade:
- Declarar a mesma fonte em arquivos CSS diferentes com URLs diferentes (ex.: com e sem querystring).
- Servir a fonte com redirecionamento (301/302) antes do arquivo final.
- Preload apontando para um arquivo, mas o CSS usando outro (nome diferente, caminho diferente, ou formato diferente).
Garanta que o preload e o @font-face apontem para o mesmo recurso e que a resposta do servidor tenha o tipo correto (font/woff2).
Fontes variáveis: menos arquivos, mas atenção ao subset
Fontes variáveis podem substituir vários pesos/estilos com um único arquivo, reduzindo overhead de múltiplas requisições. Porém, o arquivo variável pode ser maior do que um único peso estático. Por isso, o subset é ainda mais importante: um variável subsetado para o seu idioma costuma ficar bem competitivo.
Ao usar variável, declare corretamente os intervalos de peso e evite carregar e declarar eixos que você não usa.
@font-face { font-family: 'MinhaVar'; src: url('/fonts/MinhaVar-subset.woff2') format('woff2'); font-weight: 200 800; font-style: normal; font-display: swap;}Armadilhas comuns e como evitar
Carregar fontes via @import no CSS
@import pode atrasar a descoberta das fontes, porque o navegador precisa baixar e processar o CSS importado antes de ver os @font-face. Prefira incluir CSS crítico cedo (ou consolidar) e use preload quando necessário.
Excesso de pesos e estilos
Cada peso/estilo adicional é um arquivo (ou uma parte significativa do arquivo variável). Se o design pede “muitos pesos”, tente mapear para menos opções (por exemplo, 400 e 600) e use variações de tamanho/spacing para hierarquia visual.
Ícones como fonte
Fontes de ícones podem causar problemas de acessibilidade e também atrasar renderização (ícones podem “sumir” até a fonte carregar). Quando possível, prefira SVG inline ou sprites SVG. Se você precisar manter fonte de ícones, trate como crítica apenas se os ícones forem essenciais acima da dobra e considere subset apenas dos glifos usados.
Sem fallback para caracteres fora do subset
Se você subseta agressivamente, caracteres inesperados podem não existir. Garanta que sua pilha de fallback inclua fontes do sistema capazes de cobrir esses caracteres, ou mantenha um segundo arquivo com unicode-range para cobrir o restante.
Checklist rápido de implementação
- Você está carregando apenas famílias/pesos/estilos realmente usados?
- As fontes estão em WOFF2 e, quando possível, subsetadas para o idioma?
- Há preload apenas do arquivo crítico (e ele corresponde ao
@font-face)? font-displayestá definido (swap/optional) e testado?- A pilha de fallback usa fontes do sistema e, se necessário, métricas ajustadas?
- Não há redirecionamentos, duplicidade de URLs ou downloads repetidos?