Uma linha de texto com tamanhos mistos pode ultrapassar os valores de line-height de ambos os intervalos porque o alinhamento pela linha de base combina suas extensões superior e inferior. Este guia mostra como diagnosticar essa altura extra e ajustar uma linha com dois rótulos em um navegador para desktop no Mac.
Por que o intervalo mais alto não determina a altura da linha?
Considere um cabeçalho abstrato de página que contém um Label A grande ao lado de um Label B pequeno. Ambos os intervalos têm line-height: 40px, mas a linha que os contém mede mais de 40 pixels CSS. Esse resultado decorre da forma como o layout em linha alinha as caixas antes de calcular a linha que as envolve.
Para texto em linha comum, o navegador calcula a contribuição de altura de cada caixa, alinha as caixas verticalmente e, em seguida, envolve as extensões combinadas. Com o alinhamento pela linha de base, os pontos mais altos e mais baixos podem pertencer a intervalos diferentes. Assim, o resultado pode ultrapassar a contribuição individual de qualquer um deles. O elemento pai também contribui com uma caixa invisível de tamanho mínimo, tradicionalmente chamada de strut. W3C: Cálculos da altura da linha
A linha de base é a referência de alinhamento do texto. Ela não é o ponto médio vertical da contribuição da altura de linha. Dar alturas de linha iguais a textos de tamanhos diferentes não faz com que ambos tenham distâncias iguais acima e abaixo dessa linha de base. O valor inicial de vertical-align é baseline. MDN: vertical-align
Mantenha separadas três medidas: as métricas de ascendente e descendente da fonte, os limites visíveis das letras específicas e o espaço contribuído para o layout da linha. O fundo colorido ou o retângulo de seleção de um intervalo não deve ser tratado como uma medição direta da caixa de linha que o envolve; o CSS distingue a geometria de pintura em linha dos cálculos de altura de linha. W3C: Entrelinha e meia entrelinha
Como duas contribuições de 40px podem produzir uma linha de 48,4px?
Para texto comum alinhado pela linha de base com uma altura de linha explícita, o cálculo útil é: subtraia a soma do ascendente e do descendente da fonte da altura de linha e distribua igualmente o espaço restante acima e abaixo. Esse espaço extra é chamado de entrelinha; cada metade é uma meia entrelinha. O Working Draft do CSS Inline Layout Level 3 descreve esse cálculo e permite uma entrelinha negativa. O status de rascunho não comprova o suporte dos navegadores a todas as propriedades mais recentes que ele contém. W3C: Cálculo dos limites do layout
Os valores a seguir são ilustrativos, não medições de Arial nem de qualquer fonte para Mac. Suponha uma strut do elemento pai cuja contribuição corresponda à do intervalo pequeno, sem outro conteúdo em linha ou deslocamentos verticais.
| Contribuição | Ascendente da fonte | Descendente da fonte | Altura de linha | Meia entrelinha | Acima da linha de base | Abaixo da linha de base |
|---|---|---|---|---|---|---|
| Intervalo grande | 32px | 8px | 40px | 0px | 32px | 8px |
| Intervalo pequeno | 9,6px | 2,4px | 40px | 14px | 23,6px | 16,4px |
| Strut do elemento pai | 9,6px | 2,4px | 40px | 14px | 23,6px | 16,4px |
Neste exemplo com alinhamento pela linha de base, considere separadamente a maior extensão de cada lado: max(32, 23.6, 23.6) + max(8, 16.4, 16.4) = 48.4px. Cada contribuição totaliza 40px, mas o intervalo grande define o topo, enquanto o intervalo pequeno e o elemento pai definem a parte inferior. Esse resultado demonstrado deriva do cálculo de layout documentado, não de uma medição prevista do navegador. W3C: Modelo de layout em linha
A pergunta de diagnóstico, portanto, é: qual participante define cada borda? Reduzir a altura de linha do intervalo pequeno pode remover sua contribuição inferior, mas deixar intacta uma contribuição idêntica da strut do elemento pai.
Como reproduzir e inspecionar isso em um Mac?
Use o Chrome no seu Mac para este passo a passo. Abra uma nova aba, navegue até about:blank e pressione Command + Option + J para abrir o Console. O Chrome documenta esse atalho para executar JavaScript. Chrome DevTools: visão geral
Execute o programa completo abaixo nessa aba em branco. Ele cria três linhas sintéticas com os mesmos rótulos, imprime as medições e usa uma raiz shadow para manter juntos os estilos da demonstração. Não é necessário instalar pacotes nem usar bibliotecas externas. Executá-lo novamente substitui a demonstração anterior.
(async () => {
document.getElementById('line-box-demo')?.remove();
const host = document.createElement('div');
host.id = 'line-box-demo';
document.body.append(host);
const root = host.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = `
:host { display: block; padding: 24px; color: #222; background: #fff; }
.label { margin: 20px 0 6px; font: 14px Arial, sans-serif; }
.row { font: 12px/40px Arial, sans-serif; white-space: nowrap;
margin: 0; padding: 0; border: 0; background: #e8eef4; }
.large { font-size: 40px; line-height: 40px; }
.small { font-size: 12px; line-height: 40px; }
.trim-span .small, .trim-both .small { line-height: 12px; }
.trim-both { font-size: 40px; }
`;
root.append(style);
const cases = [
['', 'Shared 40px line-height'],
['trim-span', 'Smaller span only'],
['trim-both', 'Parent and smaller span']
];
const rows = cases.map(([className, label]) => {
const caption = document.createElement('p');
caption.className = 'label';
caption.textContent = label;
const row = document.createElement('div');
row.className = `row ${className}`;
for (const [size, text] of [['large', 'Label A'], ['small', ' Label B']]) {
const span = document.createElement('span');
span.className = size;
span.textContent = text;
row.append(span);
}
root.append(caption, row);
return { label, row };
});
await document.fonts.ready;
console.table(rows.map(({ label, row }) => ({
case: label,
parentLineHeight: getComputedStyle(row).lineHeight,
largeLineHeight: getComputedStyle(row.children[0]).lineHeight,
smallLineHeight: getComputedStyle(row.children[1]).lineHeight,
rowHeight: row.getBoundingClientRect().height
})));
})();
O programa aguarda document.fonts.ready, que é resolvida depois que o carregamento das fontes do documento e o trabalho de layout relacionado são concluídos. Se você introduzir outra fonte ou alterar o conteúdo depois, faça novas medições quando essas alterações tiverem sido aplicadas. MDN: FontFaceSet.ready
Leia as três linhas como uma comparação controlada. Na primeira, os dois intervalos e o elemento pai têm altura de linha de 40px. Na segunda, somente a altura de linha do intervalo pequeno é reduzida para 12px. Na terceira, o tamanho da fonte do elemento pai também é alterado para 40px, fazendo com que sua strut corresponda ao tamanho da fonte e à altura de linha do texto grande. Se a segunda linha continuar alta, mas a terceira diminuir, essa comparação identifica a contribuição do elemento pai.
Use os resultados do seu navegador em vez de esperar o valor ilustrativo de 48,4px. O programa mede cada linha com getBoundingClientRect(), que retorna o retângulo delimitador de um elemento, incluindo preenchimento e bordas. Aqui, as linhas não têm nenhum dos dois, contêm uma única linha sem quebra e têm altura automática, o que torna suas alturas aproximações úteis das alturas das caixas de linha. Medir um intervalo com a mesma API mediria o retângulo desse intervalo, sem expor diretamente a caixa de linha que o envolve. MDN: getBoundingClientRect()
O que você deve verificar em um layout existente?
Clique no texto relevante com o botão de controle pressionado e escolha Inspecionar. Em Elements, inspecione os dois intervalos e o bloco que os contém. Use Styles para rastrear declarações e Computed para ver os valores aplicados; ative Show All quando necessário para inspecionar propriedades herdadas. O Chrome documenta essas visualizações e os filtros de propriedades. Chrome DevTools: referência de recursos CSS
Registre font-family, font-size, line-height, display e vertical-align para cada participante. Comece pelo elemento pai em vez de olhar apenas para os dois intervalos. Em seguida, use esta sequência para isolar a causa:
- Confirme o modelo de layout. Verifique se os elementos filhos participam do fluxo em linha normal ou se são itens flex ou grid.
- Confirme que há uma única linha. Use uma área de teste larga e rótulos sintéticos curtos para que as quebras de linha não confundam a medição.
- Verifique o alinhamento. Remova temporariamente os deslocamentos da linha de base ou o alinhamento vertical fora do padrão antes de aplicar o cálculo simples acima.
- Altere um contribuinte por vez. Reduza a altura de linha do intervalo pequeno, faça a medição e depois teste separadamente a contribuição da fonte e da altura de linha do elemento pai.
- Restaure as condições reais. Depois de identificar a causa, verifique novamente a fonte pretendida, a largura disponível e o texto.
Verifique também a herança. Um line-height: 1.2 sem unidade transmite um multiplicador que cada descendente aplica ao próprio tamanho de fonte. Um valor de comprimento, como 40px, transmite um comprimento fixo, a menos que seja sobrescrito; as porcentagens são calculadas em relação ao tamanho da fonte do elemento que as declara. Essas opções podem dar uma entrelinha inesperadamente generosa ao texto pequeno. normal depende da fonte e do navegador, portanto não serve como valor numérico fixo. MDN: valores e herança de line-height
Qual ajuste corresponde ao resultado pretendido?
Escolha o ajuste de acordo com o que os dois rótulos representam. Um cabeçalho compacto composto por rótulos separados tem requisitos diferentes dos de palavras inseridas em um parágrafo com quebra de linha.
| Resultado pretendido | Ajuste a testar | O que verificar |
|---|---|---|
| Manter uma linha de base de texto compartilhada | Defina uma altura de linha adequada para o intervalo pequeno e alinhe as métricas da fonte do elemento pai com o texto principal | A strut do elemento pai deixa de fornecer uma extensão inferior indesejada |
| Alinhar os topos de rótulos separados | Teste vertical-align: top nos dois intervalos em linha |
A relação de linha de base alterada corresponde ao design |
| Centralizar rótulos separados dentro de uma linha | Use um contêiner flex com align-items: center |
O comportamento em larguras menores e a quebra dos itens continuam adequados ao layout |
| Manter o fluxo contínuo do parágrafo | Mantenha o texto em linha e use valores herdados de altura de linha definidos intencionalmente | Todas as linhas resultantes acomodam o texto de tamanhos mistos |
vertical-align: top alinha um elemento e seus descendentes ao topo da linha. middle tem outra definição: alinha o ponto médio do elemento à linha de base do elemento pai mais metade da altura-x do elemento pai. Portanto, não significa centralização geométrica na linha inteira, e alterar o alinhamento não garante uma altura final específica. MDN: valores de vertical-align
Para dois rótulos independentes, o flexbox oferece alinhamento explícito no eixo transversal por meio de align-items. O alinhamento central e o alinhamento pela linha de base expressam escolhas de design diferentes. A mudança para flexbox altera o contexto de layout; use-o quando os rótulos forem itens separados e avalie o resultado em larguras menores. MDN: align-items
Uma altura de linha menor também pode permitir que o texto seja pintado além de sua contribuição para o layout. Verifique as linhas vizinhas depois de reduzir o espaçamento; uma caixa medida compacta, por si só, não comprova que o texto caiba sem sobreposição. W3C: Entrelinha e meia entrelinha
Medições de canvas podem substituir o layout de linha do navegador?
As medições de canvas respondem a uma pergunta relacionada. A WHATWG define measureText() e distingue as métricas de ascendente e descendente da fonte dos limites reais da caixa delimitadora do texto específico. Essas medições são úteis para desenhar em canvas, mas a medição dos limites de tinta não incorpora a strut do elemento pai do DOM que envolve o texto, o alinhamento em linha e as contribuições da altura de linha. Não a use sozinha para prever a altura desta linha. WHATWG: TextMetrics
Preserve os limites de modelagem de texto durante os experimentos. O CSS exige que a modelagem continue através dos limites entre elementos em linha quando não houver mudança efetiva de formatação, enquanto certos limites — incluindo o alinhamento vertical fora do valor inicial — exigem que a modelagem seja interrompida. Evite envolver caracteres individuais para medi-los: separar clusters de grafemas ou ligaduras pode produzir resultados inconsistentes. Mantenha intactos os rótulos completos ou trechos de texto com significado. W3C: processamento e modelagem de texto entre limites de elementos
Para resolver a altura extra, identifique o participante que define o topo e o que define a parte inferior, incluindo o elemento pai. Ajuste as contribuições de altura de linha ou escolha o modelo de alinhamento que corresponda à finalidade dos rótulos e, em seguida, meça novamente a linha completa com o texto e a fonte pretendidos.
Fontes
- Detalhes do modelo de formatação visual — W3C.
- CSS Inline Layout Module Level 3 — W3C.
- Propriedade CSS line-height — MDN Web Docs.
- Propriedade CSS vertical-align — MDN Web Docs.
- Visão geral: Chrome DevTools — Google for Developers.
- Referência de recursos CSS — Google for Developers.
- FontFaceSet: propriedade ready — MDN Web Docs.
- Element: método getBoundingClientRect() — MDN Web Docs.
- Propriedade CSS align-items — MDN Web Docs.
- Padrão HTML: Canvas — WHATWG.
- CSS Text Module Level 3 — W3C.