Dev.to WebDev 🛠 Dev 👁 0 📖 3 min read

Por que texto estilizado vira quadradinho (tofu): fallback de fontes e Unicode

Você cola um nome em negrito cursivo no perfil, ele aparece perfeito no seu celular, e um amigo avisa que só vê uma fileira de quadradinhos. Nada está errado com o texto. O problema é que o aparelho dele não tinha como d

Você cola um nome em negrito cursivo no perfil, ele aparece perfeito no seu celular, e um amigo avisa que só vê uma fileira de quadradinhos. Nada está errado com o texto. O problema é que o aparelho dele não tinha como desenhar aqueles caracteres. Este post explica o porquê e mostra duas armadilhas de código que costumam aparecer junto.

O que é o "tofu"

O quadradinho vazio é chamado de tofu, porque lembra um bloco de tofu. Ele não é um caractere que você digita. É um sintoma de renderização: o sistema pediu uma forma (glyph) para aquele caractere em todas as fontes disponíveis, não achou nenhuma e desenhou um substituto. A família de fontes Noto, do Google, foi nomeada justamente com esse objetivo, "no tofu".

Texto estilizado não é fonte

Os geradores de "letras diferentes" não instalam fonte nenhuma. Eles trocam cada letra comum por outro caractere Unicode que parece negrito, cursivo ou gótico:

const normal = "Ghost";
const estilizado = "𝓖𝓱𝓸𝓼𝓽";

console.log(normal.length);       // 5
console.log(estilizado.length);   // 10
console.log([...estilizado].length); // 5

A maior parte desses estilos vem do bloco Mathematical Alphanumeric Symbols (U+1D400 a U+1D7FF), criado para notação matemática. Como cada letra estilizada está fora do plano básico do Unicode, em JavaScript ela ocupa duas unidades UTF-16. Por isso .length devolve 10 e não 5.

Armadilha 1: contar caracteres com .length

Se o seu formulário tem limite de caracteres (um nick de jogo, por exemplo), .length conta unidades UTF-16 e vai dizer que 𝓖𝓱𝓸𝓼𝓽 tem 10. O spread [...texto] conta pontos de código e dá 5. Mas ainda existe um terceiro caso: marcas combinantes, como as do texto "zalgo" ou de um acento decomposto.

const seg = new Intl.Segmenter("pt-BR", { granularity: "grapheme" });

const contar = (t) => ({
  utf16: t.length,
  codepoints: [...t].length,
  graphemes: [...seg.segment(t)].length,
});

Num texto de teste com 6 letras e 13 marcas combinantes empilhadas sobre elas, utf16 e codepoints devolvem 19, enquanto graphemes devolve 6, que é o que a pessoa enxerga. Escolha a contagem que combina com a regra do seu produto e deixe o limite claro para o usuário.

Armadilha 2: pilha de fontes sem fallback

O navegador percorre a lista de font-family até encontrar uma fonte que tenha o caractere. Se a lista termina cedo, sobra o tofu. Termine sempre com uma família genérica:

body {
  font-family: Inter, system-ui, sans-serif;
}

Com sans-serif no fim, o sistema ainda pode usar a fonte de reserva dele para os caracteres que a sua fonte principal não cobre. Isso não garante que todo caractere apareça em todo aparelho, mas reduz muito o problema. E atenção: font-family não "estiliza" texto Unicode estilizado. São caracteres diferentes, não a mesma letra com outra fonte.

Acessibilidade

Leitores de tela podem ler caracteres estilizados pelos nomes longos que o Unicode dá a eles, o que atrapalha quem usa esse recurso. Mantenha em texto comum o que é importante, como nome, data, endereço e link, e use o estilizado só como enfeite.

Checklist rápido

  • Termine a pilha de font-family com sans-serif ou system-ui.
  • Conte caracteres com [...texto] ou Intl.Segmenter, não com .length.
  • Teste a página em um segundo aparelho, de preferência um Android mais antigo.
  • Não coloque informação importante só em texto estilizado.

Se quiser um lugar para ver esses estilos em ação e testá-los em outro celular, eu mantenho um gerador gratuito em português chamado Letra Diferente.

Para saber mais

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.