Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIntermediáriocódigo testado

Tipografia no CSS: fonte, escala e clamp() fluido

Como escolher a pilha de fontes, acertar line-height e medida de linha, e montar uma escala tipográfica que cresce sozinha com clamp().

Rodolfo Mori15 min de leitura

Tipografia no CSS é um punhado de decisões pequenas: qual pilha de fontes, como a fonte chega até o leitor, quanto de line-height, quantos caracteres cabem por linha e como o tamanho cresce junto com a tela. As quatro primeiras você resolve uma vez por projeto. A última é onde clamp() entra.

Todos os exemplos são do catálogo online da Ponto de Fuga, uma livraria de bairro com onze mil títulos e um clube de leitura que se encontra na última sexta do mês. A página que vamos vestir é a resenha: um título, um parágrafo longo e uma chamada em corpo maior.

font-family: a pilha e o fallback que evita o pulo do texto

font-family não recebe uma fonte, recebe uma lista de candidatas. O navegador anda por ela da esquerda para a direita e para na primeira que existe na máquina de quem lê — ou que terminou de baixar.

Isso quer dizer que o fallback não é enfeite para o caso raro de a fonte falhar. Ele é o que a pessoa lê nos primeiros instantes de toda visita, enquanto o arquivo da fonte ainda está na rede. E fontes diferentes ocupam larguras bem diferentes.

Medi a mesma frase, a 18px, em seis pilhas:

css
.a { font-family: Georgia, serif; }
.b { font-family: Helvetica, Arial, sans-serif; }
.c { font-family: system-ui, sans-serif; }
.d { font-family: 'Times New Roman', serif; }
.e { font-family: Verdana, sans-serif; }
.f { font-family: ui-monospace, monospace; }
Georgia, serif 705.6px 0.0% linhas: 4 line-height normal: 21px Helvetica, Arial, sans-serif 722.5px +2.4% linhas: 4 line-height normal: 21px system-ui, sans-serif 717.3px +1.7% linhas: 4 line-height normal: 21px "Times New Roman", serif 643.8px -8.8% linhas: 4 line-height normal: 22px Verdana, sans-serif 819.3px +16.1% linhas: 4 line-height normal: 22px ui-monospace, monospace 950.6px +34.7% linhas: 5 line-height normal: 21px

A mesma frase, no mesmo tamanho, ocupa de 643px a 950px. Verdana é 16% mais larga que Georgia; a monospace é 34% mais larga e já quebra numa linha a mais. Repare que a última coluna também muda: line-height: normal vale 21px em Georgia e 22px em Verdana, porque cada fonte carrega a própria altura de linha sugerida.

Escolha o fallback pelo desenho e pela largura da fonte que você vai carregar. Para a Literata, que é uma serifada de leitura, Georgia é a vizinha mais próxima que existe em quase toda máquina:

css
:root {
  --fonte-texto: Literata, Georgia, 'Times New Roman', serif;
  --fonte-marca: 'Space Grotesk', system-ui, sans-serif;
}

@font-face, font-display e o texto que some

Uma fonte própria entra pela regra @font-face, que dá um nome a um arquivo. O font-display decide o que o navegador faz enquanto esse arquivo não chega:

css
@font-face {
  font-family: 'Literata';
  src: url('/literata.woff2') format('woff2');
  font-display: block;
}

p {
  font-family: 'Literata', Georgia, serif;
  font-size: 18px;
  line-height: 1.6;
  width: 380px;
}

Omitir a linha não é ficar neutro. O valor inicial da propriedade é auto, que entrega a decisão ao navegador — e na prática os motores atuais resolvem auto como block: por até três segundos o navegador reserva o espaço do texto e não pinta nada. É o FOIT, flash of invisible text.

Para provar isso, servi a fonte com 1,5 segundo de atraso e capturei a tela aos 400 ms — antes de a fonte chegar — em cinco situações: sem declarar nada, com auto, com block, com swap, e com o parágrafo escondido por visibility: hidden para servir de gabarito de “página sem texto”.

=== tela aos 400 ms, fonte servida com 1,5 s de atraso === sem font-display 1078 bytes sha256 47ed40895478 font-display: auto 1078 bytes sha256 47ed40895478 font-display: block 1078 bytes sha256 47ed40895478 font-display: swap 19145 bytes sha256 6c7a594a4b36 parágrafo escondido 1078 bytes sha256 47ed40895478

sem font-display == escondido (FOIT)? true auto == escondido (FOIT)? true

Três das capturas são byte a byte idênticas à do gabarito: a de quem não declarou nada, a de auto e a de block têm o mesmo tamanho e o mesmo hash da página com o parágrafo escondido. Não havia texto nenhum na tela, só um retângulo de espaço reservado. Com swap, o mesmo instante tem 19 kB de pixels — o parágrafo já está lá, em Georgia.

O pulo: size-adjust faz o fallback ocupar o mesmo espaço

Trocar o FOIT pelo FOUT — flash of unstyled text, o texto que aparece no fallback e depois troca de fonte — resolve o texto invisível e cria outro problema: quando a Literata chega, ela ocupa mais espaço que a Georgia e o parágrafo empurra tudo que está abaixo. Medi a altura da caixa aos 400 ms e depois da troca.

A correção é declarar o fallback como uma @font-face própria, esticada até casar com a largura da fonte real:

css
@font-face {
  font-family: 'Georgia ajustada';
  src: local('Georgia');
  size-adjust: 105.38%;
}

p {
  font-family: 'Literata', 'Georgia ajustada', serif;
}
=== parágrafo da resenha, largura fixa de 380px === fallback Georgia 400 ms: 115.19px (4 linhas) com a fonte: 143.98px (5 linhas) pulo: 28.79px fallback size-adjust 400 ms: 143.98px (5 linhas) com a fonte: 143.98px (5 linhas) pulo: 0.00px

Com Georgia crua, o parágrafo salta de 4 para 5 linhas e desloca 28,79px de conteúdo — um layout shift visível. Com o fallback ajustado, o pulo é 0,00px: o texto já nasce com o número de linhas final.

Aqueles 105,38% não são chute. Medi a largura do parágrafo inteiro sem quebra nas duas fontes (1520,4px na Literata contra 1442,7px na Georgia) e dividi uma pela outra. A razão depende do texto — meça com o seu texto, não com uma palavra solta.

line-height sem unidade é o único que herda direito

line-height aceita número puro (1.6), comprimento (28.8px) e porcentagem (160%). Parecem três jeitos de dizer a mesma coisa. Não são — e a diferença só aparece quando um filho tem tamanho de fonte diferente do pai.

css
.resenha { font-size: 18px; line-height: 1.6; }
.resenha .chamada { font-size: 32px; }

Rodei as quatro variantes de line-height na mesma estrutura, lendo o valor computado no pai e no filho:

line-height | corpo 18px | chamada 32px | caixa da chamada -------------+---------------------+----------------------+----------------- 1.6 | 28.8px | 51.2px | 51.19px 28.8px | 28.8px | 28.8px | 28.8px 160% | 28.8px | 28.8px | 28.8px normal | normal | normal | 36px

O número puro é o único que herda como fator: o filho de 32px recebe 51.2px, que é 32 × 1,6. Com 28.8px e com 160% o valor é calculado uma vez, no pai, e o que desce para o filho é o resultado em pixels. A chamada de 32px fica com linha de 28,8px — menor que a própria fonte. As linhas se encavalam.

Para o corpo de texto, 1.5 a 1.7. Para títulos grandes, algo entre 1.1 e 1.25: quanto maior a letra, menos respiro entre linhas ela precisa.

Medida de linha: 65ch não são 65 caracteres

A recomendação clássica é entre 45 e 75 caracteres por linha. Acima disso, o olho perde o começo da linha seguinte; abaixo, a leitura vira uma sequência de saltos.

A unidade ch parece feita para isso — ela vale a largura do caractere 0 na fonte atual. O problema é que numa fonte proporcional o 0 é bem mais largo que a letra média: em Georgia 18px ele mede 11,05px, contra 8,04px de largura média por caractere no texto da resenha — 37% a mais. Por isso em 65ch cabe bem mais que 65 letras. Medi contando de verdade, caractere por caractere, em qual linha cada um caiu:

css
p {
  font-size: 18px;
  line-height: 1.6;
  max-width: 65ch;
}
max-width: 65ch, font-size: 18px Georgia, serif caixa 718.1px 4 linhas caracteres por linha: 82 a 88 (média 85) Verdana, sans-serif caixa 743.8px 4 linhas caracteres por linha: 75 a 76 (média 75.7) ui-monospace, monospace caixa 702.1px 5 linhas caracteres por linha: 61 a 66 (média 63)

Em Georgia, 65ch entrega 85 caracteres por linha — vinte a mais que o topo da faixa recomendada. Só na monospace o número bate com a intuição, porque ali todo caractere tem a largura do 0.

O medidor é um laço que pede o retângulo de cada caractere e agrupa pela coordenada vertical. Cole no console do DevTools, em qualquer página:

js
const texto =
  'A Ponto de Fuga abriu em 2019 numa sala de esquina, com três estantes e ' +
  'uma poltrona que ninguém queria comprar. Hoje são onze mil títulos, um ' +
  'clube de leitura que se encontra toda última sexta e um catálogo online ' +
  'que a Marta atualiza de manhã, antes de abrir a loja para a rua.';

const palco = document.body.appendChild(document.createElement('div'));

const medir = (ch) => {
  palco.innerHTML = `<p style="font: 18px/1.6 Georgia, serif; max-width: ${ch}ch">${texto}</p>`;
  const p = palco.querySelector('p');
  const no = p.firstChild;
  const range = document.createRange();
  const porLinha = new Map();
  for (let i = 0; i < texto.length; i++) {
    range.setStart(no, i);
    range.setEnd(no, i + 1);
    const r = range.getBoundingClientRect();
    if (r.width === 0 && r.height === 0) continue;
    const chave = Math.round(r.top);
    porLinha.set(chave, (porLinha.get(chave) ?? 0) + 1);
  }
  const cheias = [...porLinha.values()].slice(0, -1); // a última linha não conta
  return {
    ch,
    px: +p.getBoundingClientRect().width.toFixed(1),
    media: +(cheias.reduce((a, b) => a + b, 0) / cheias.length).toFixed(1),
  };
};

console.log('Georgia 18px — quantos caracteres reais cabem em cada valor de ch');
for (let ch = 35; ch <= 70; ch += 5) {
  const d = medir(ch);
  const faixa = d.media >= 45 && d.media <= 75 ? '   (dentro dos 45 a 75)' : '';
  console.log(
    `max-width: ${String(d.ch).padStart(2)}ch = ${String(d.px).padStart(6)}px  =  ${d.media} caracteres${faixa}`,
  );
}
Georgia 18px — quantos caracteres reais cabem em cada valor de ch max-width: 35ch = 386.7px = 45.8 caracteres (dentro dos 45 a 75) max-width: 40ch = 441.9px = 53.6 caracteres (dentro dos 45 a 75) max-width: 45ch = 497.1px = 59.8 caracteres (dentro dos 45 a 75) max-width: 50ch = 552.4px = 65.8 caracteres (dentro dos 45 a 75) max-width: 55ch = 607.6px = 73 caracteres (dentro dos 45 a 75) max-width: 60ch = 662.9px = 78.7 caracteres max-width: 65ch = 718.1px = 85 caracteres max-width: 70ch = 773.3px = 90.5 caracteres

Para a resenha da Ponto de Fuga em Georgia, o alvo de 65 caracteres reais fica em 50ch, ou 552px. É esse valor que vai para o CSS — e ele muda se a fonte mudar. Mais sobre ch e as outras unidades relativas na lição de unidades no CSS.

clamp(): mínimo, preferido e máximo numa declaração

clamp(mínimo, preferido, máximo) devolve o preferido, desde que ele fique entre os outros dois. Se passar do teto, devolve o teto. Se cair abaixo do piso, devolve o piso. Uma declaração faz o trabalho de duas media queries e do intervalo inteiro entre elas.

css
h2 {
  font-size: clamp(1.5rem, 5vw, 3rem);
}
font-size: clamp(1.5rem, 5vw, 3rem) (1.5rem = 24px, 3rem = 48px) viewport 320px : 5vw seriam 16px : font-size real 24px viewport 400px : 5vw seriam 20px : font-size real 24px viewport 480px : 5vw seriam 24px : font-size real 24px viewport 640px : 5vw seriam 32px : font-size real 32px viewport 800px : 5vw seriam 40px : font-size real 40px viewport 960px : 5vw seriam 48px : font-size real 48px viewport 1120px : 5vw seriam 56px : font-size real 48px viewport 1280px : 5vw seriam 64px : font-size real 48px

A curva tem três trechos: um patamar embaixo, uma rampa no meio, um patamar em cima. Repare em 480px, onde 5vw dá exatamente 24px: é ali que a rampa começa. E em 960px, onde 5vw dá 48px: é ali que ela trava.

mínimo preferido máximo 480px 960px largura da janela

5vw é fácil de escrever e ruim de controlar: a rampa é íngreme demais e você não escolhe onde ela começa nem onde termina. A seção seguinte resolve isso.

Montando a escala do corpo ao h1 com uma razão só

Uma escala tipográfica é uma razão aplicada repetidas vezes ao tamanho do corpo. Com razão 1,25 (a terça maior), o corpo de 16px gera 20px, 25px e 31,25px. Você escolhe dois números — o corpo na tela estreita e o corpo na tela larga — e a escala nasce dos dois.

Falta transformar cada par em um clamp. A conta é uma reta que passa por dois pontos: (320px de janela, tamanho mínimo) e (1280px, tamanho máximo). A inclinação vira vw; onde a reta corta o eixo vira rem.

js
const BASE_MIN = 16; // px do corpo na tela estreita
const BASE_MAX = 20; // px do corpo na tela larga
const RAZAO = 1.25; // uma razão só, dos dois lados
const VW_MIN = 320;
const VW_MAX = 1280;
const NIVEIS = ['corpo', 'h3', 'h2', 'h1'];

const arred = (n) => +n.toFixed(3);

function fluido(minPx, maxPx) {
  const inclinacao = (maxPx - minPx) / (VW_MAX - VW_MIN);
  const intercepto = minPx - inclinacao * VW_MIN;
  return {
    min: arred(minPx / 16),
    rem: arred(intercepto / 16),
    vw: arred(inclinacao * 100),
    max: arred(maxPx / 16),
  };
}

console.log(':root {');
NIVEIS.forEach((nome, i) => {
  const minPx = BASE_MIN * RAZAO ** i;
  const maxPx = BASE_MAX * RAZAO ** i;
  const f = fluido(minPx, maxPx);
  console.log(
    `  --fonte-${nome}: clamp(${f.min}rem, ${f.rem}rem + ${f.vw}vw, ${f.max}rem);` +
      `  /* ${arred(minPx)}px a ${arred(maxPx)}px */`,
  );
});
console.log('}');
:root { --fonte-corpo: clamp(1rem, 0.917rem + 0.417vw, 1.25rem); /* 16px a 20px */ --fonte-h3: clamp(1.25rem, 1.146rem + 0.521vw, 1.563rem); /* 20px a 25px */ --fonte-h2: clamp(1.563rem, 1.432rem + 0.651vw, 1.953rem); /* 25px a 31.25px */ --fonte-h1: clamp(1.953rem, 1.79rem + 0.814vw, 2.441rem); /* 31.25px a 39.063px */ }

Quatro variáveis, e cada elemento passa a apontar para uma delas. Se a razão mudar, você roda o script de novo e cola o novo :root — nenhum seletor precisa ser tocado. É o mesmo raciocínio das variáveis CSS aplicado a tamanho de fonte.

Agora a pergunta que importa: o navegador entrega o que o script prometeu? Medi o h1 de 320px a 1920px, de 80 em 80 pixels.

css
h1 {
  font-size: clamp(1.953rem, 1.79rem + 0.814vw, 2.441rem);
}
font-size: clamp(1.953rem, 1.79rem + 0.814vw, 2.441rem) viewport | font-size | quem está mandando 320px | 31.248px | o mínimo 400px | 31.896px | o preferido 480px | 32.547px | o preferido 560px | 33.198px | o preferido 640px | 33.850px | o preferido 720px | 34.501px | o preferido 800px | 35.152px | o preferido 880px | 35.803px | o preferido 960px | 36.454px | o preferido 1040px | 37.106px | o preferido 1120px | 37.757px | o preferido 1200px | 38.408px | o preferido 1280px | 39.056px | o máximo 1360px | 39.056px | o máximo 1440px | 39.056px | o máximo 1520px | 39.056px | o máximo 1600px | 39.056px | o máximo 1680px | 39.056px | o máximo 1760px | 39.056px | o máximo 1840px | 39.056px | o máximo 1920px | 39.056px | o máximo

piso 31.248px, teto 39.056px primeira largura acima do piso: 321px (31.2529px) primeira largura já no teto: 1280px (39.0560px)

Uma varredura de 1 em 1 pixel achou as duas dobradiças: 320px é a última largura ainda encostada no piso — em 321px a fonte já saiu dele — e 1280px é exatamente a primeira largura que atinge o teto. São os dois números que entraram no script como VW_MIN e VW_MAX. Arredondar os coeficientes para três casas custou 0,007px no teto (39,056 em vez de 39,063). Ninguém enxerga isso; a conta está certa.

O clamp que quebra o zoom, e a correção com rem no meio

Aqui mora o defeito mais comum de tipografia fluida, e ele não aparece em teste nenhum de layout: clamp com só vw no meio ignora o zoom do navegador.

O motivo é geométrico. Zoom de 200% em uma janela de 1280 pixels de tela deixa a viewport com 640 pixels de CSS, e cada pixel de CSS passa a ocupar dois pixels de tela. Um valor em vw cai pela metade e é multiplicado por dois: dá no mesmo. Um valor em rem não cai — e é ele que faz o texto crescer.

Medi três variantes do mesmo h1, sem e com zoom:

css
.so-vw-min-baixo { font-size: clamp(1rem, 3.051vw, 2.441rem); }
.so-vw-min-alto  { font-size: clamp(1.953rem, 3.051vw, 2.441rem); }
.rem-mais-vw     { font-size: clamp(1.953rem, 1.79rem + 0.814vw, 2.441rem); }
janela de 1280 px de tela, com e sem zoom do navegador

variante | 100%: css / tela | 200%: css / tela | o texto cresceu ———————+—————––+—————––+–––––––– só vw, mínimo baixo | 39.05 / 39.05px | 19.53 / 39.05px | 1.00x só vw, mínimo alto | 39.05 / 39.05px | 31.25 / 62.5px | 1.60x rem + vw (a nossa) | 39.06 / 39.06px | 33.85 / 67.7px | 1.73x

A primeira variante cresce 1,00x: a pessoa apertou zoom duas vezes e o título ficou exatamente do mesmo tamanho na tela. Isso reprova no critério 1.4.4 da WCAG, que exige texto redimensionável até 200%.

A segunda melhora por acidente — o zoom empurra o vw para baixo do mínimo, e o mínimo está em rem. Só a terceira tem rem no termo preferido, e é a única que cresce em toda a faixa, não só nas pontas.

text-wrap: balance no título, pretty no parágrafo

Faltam as quebras. Um título de três linhas em que a última tem uma palavra solta é feio, e nenhuma escala conserta isso — quem decide onde quebrar é o algoritmo de linha do navegador.

text-wrap: balance manda o navegador distribuir os caracteres igualmente entre as linhas. Ele custa caro, então só vale em blocos curtos: títulos, legendas, chamadas.

css
body { font-family: Georgia, serif; }

h2 {
  font-size: 28px;
  line-height: 1.25;
  width: 420px;
  text-wrap: balance;
}

p {
  font-size: 18px;
  line-height: 1.6;
  width: 360px;
  text-wrap: pretty;
}
título (h2, caixa de 420px): "Clube de leitura de agosto: A Cidade e Seus Muros Incertos" text-wrap: wrap : 3 linhas de 30, 20, 8 caracteres text-wrap: balance : 3 linhas de 20, 19, 19 caracteres parágrafo (p, caixa de 360px) text-wrap: wrap : 4 linhas de 46, 38, 39, 6 caracteres text-wrap: pretty : 4 linhas de 41, 41, 38, 9 caracteres

No título, balance transformou 30/20/8 em 20/19/19 — três linhas do mesmo tamanho, sem a sobra de oito caracteres. No parágrafo, pretty reorganizou as quatro linhas para tirar a órfã de seis caracteres, deixando nove na última.

pretty foi feito para texto longo justamente porque não tenta equilibrar tudo: ele mexe só nas últimas linhas. Onde não há órfã, ele não muda nada — foi o que aconteceu quando testei o mesmo parágrafo numa caixa de 380px.

Os cinco defeitos, e o que os causa

o que aparece na tela causa correção
o texto some por um segundo font-display ausente: auto age como block font-display: swap
o conteúdo desce quando a fonte chega métricas do fallback diferentes size-adjust numa @font-face de fallback
linhas do título se encavalam line-height com unidade, herdado em px line-height sem unidade
a linha atravessa a tela inteira sem max-width no bloco de texto max-width em ch, medido na sua fonte
o título não cresce com o zoom clamp só com vw no meio somar rem ao vw no termo preferido

Juntando tudo, a folha de tipografia da Ponto de Fuga cabe em menos de trinta linhas — e cada valor dela apareceu numa medição desta página:

css
@font-face {
  font-family: 'Literata';
  src: url('/fonts/literata.woff2') format('woff2');
  font-display: swap;
}

@font-face {
  font-family: 'Georgia ajustada';
  src: local('Georgia');
  size-adjust: 105.38%;
}

:root {
  --fonte-texto: 'Literata', 'Georgia ajustada', serif;
  --fonte-corpo: clamp(1rem, 0.917rem + 0.417vw, 1.25rem);
  --fonte-h2: clamp(1.563rem, 1.432rem + 0.651vw, 1.953rem);
  --fonte-h1: clamp(1.953rem, 1.79rem + 0.814vw, 2.441rem);
}

body {
  font-family: var(--fonte-texto);
  font-size: var(--fonte-corpo);
  line-height: 1.6;
}

h1 { font-size: var(--fonte-h1); line-height: 1.15; text-wrap: balance; }
h2 { font-size: var(--fonte-h2); line-height: 1.2; text-wrap: balance; }
p  { max-width: 50ch; text-wrap: pretty; }

O que treinar agora

Pegue uma página que você já escreveu, abra o DevTools e faça três medições: o line-height computado de um título grande, a largura em pixels do seu bloco de texto e o font-size do h1 com zoom em 200%. Cada uma das três costuma revelar um dos defeitos da tabela acima.

Com a fonte no lugar, sobra a fronteira entre tipografia e layout: a media query ainda decide o que muda por tamanho de tela, enquanto as container queries deixam o componente responder ao espaço que ele realmente recebeu — o que também vale para escolher o tamanho da fonte dentro de um card. Depois disso, a próxima lição da trilha põe esse texto em movimento, com transition e transform. O mapa completo da matéria está no guia de CSS.

  • css
  • tipografia
  • fonte
  • clamp
  • line-height

Perguntas frequentes

Uso o Google Fonts pelo link ou hospedo a fonte no meu servidor?
Hospede. O link do Google Fonts custa uma conexão a mais com outro domínio antes de o navegador saber qual arquivo baixar, e desde o fim do cache compartilhado entre sites ele não traz mais a vantagem que trazia. Baixe o woff2, ponha em /fonts e escreva o @font-face na mão.
Quantos pesos da mesma fonte vale a pena carregar?
Dois na maioria dos projetos - um para o corpo e um para os títulos. Cada peso é um arquivo separado, e o navegador só baixa o que é usado. Se precisar de mais variação, uma fonte variável entrega todos os pesos num arquivo só, o que costuma sair mais barato que três estáticos.
Qual a diferença entre font-display swap e optional?
Com swap o navegador troca o fallback pela fonte assim que ela chega, a qualquer momento. Com optional ele espera cerca de 100 ms e, se a fonte não chegou, desiste dela naquele carregamento e não faz mais a troca - some o pulo do texto, ao preço de a primeira visita ficar no fallback.
Posso usar clamp() no line-height e no espaçamento também?
Pode, e no espaçamento costuma valer a pena. No line-height evite - o valor sem unidade já se adapta sozinho ao tamanho da fonte, que é exatamente o que você quer. Um clamp ali cria um valor computado em px, e você perde a herança proporcional.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Chromium 151.0.7922.34 (Playwright 1.62, macOS), e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN - clamp() — developer.mozilla.org
  2. MDN - @font-face: font-display — developer.mozilla.org
  3. MDN - @font-face: size-adjust — developer.mozilla.org
  4. MDN - text-wrap — developer.mozilla.org
  5. WCAG 2.2 - Understanding Success Criterion 1.4.4: Resize Text — w3.org

Continue por aqui