Em uma linha horizontal de imagens inline alinhadas pela linha de base, a linha de base fica na borda inferior da margem de cada imagem. Este guia mostra como localizar essa coordenada no Chrome em um Mac, verificá-la com um pequeno marcador e reconhecer quando esse método não se aplica.
Por que uma linha que contém apenas imagens tem uma linha de base?
Uma linha composta apenas por imagens ainda participa do layout inline do CSS. Um img exibido corretamente é um elemento substituído: o navegador dispõe sua caixa de acordo com o CSS, em vez de tratar a imagem como texto. O WHATWG documenta esse comportamento de renderização nas regras de renderização de imagens do padrão HTML.
No layout inline horizontal comum, uma imagem sem uma linha de base de texto intrínseca usa a borda inferior da margem para o alinhamento pela linha de base. Assim, duas imagens de alturas diferentes alinhadas pela linha de base podem compartilhar a mesma linha de base, embora suas bordas superiores fiquem em alturas diferentes. A regra diz respeito às caixas; ela não examina a arte dentro delas. Consulte CSS 2, alinhamento vertical.
Três limites distintos são relevantes:
| Limite | Significado | Referência adequada para a linha de base? |
|---|---|---|
| Borda da arte visível | Onde terminam os pixels visíveis da imagem | Não; o arquivo pode conter espaço transparente |
| Parte inferior da caixa de borda da imagem | A parte inferior retornada por getBoundingClientRect() |
Sim, após considerar a margem inferior e confirmar o alinhamento pela linha de base |
| Parte inferior da caixa de linha | O limite inferior da linha CSS completa | Não; ela pode se estender abaixo da linha de base |
A fonte do elemento pai e line-height ainda importam mesmo quando não há letras visíveis. O CSS descreve uma contribuição invisível baseada na fonte, chamada strut, que ajuda a estabelecer a altura mínima e a profundidade da linha. Isso explica por que uma imagem pode ficar sobre a linha de base enquanto a linha mantém espaço abaixo dela. A especificação CSS Inline Layout descreve essas métricas de fonte e limites de layout; a versão publicada consultada é um Working Draft, portanto não se deve presumir que seus recursos mais recentes tenham suporte universal nos navegadores.
Como encontrar a linha de base em uma página existente?
Comece por uma página que você possa inspecionar no Chrome no seu Mac. Clique com o botão direito em uma imagem e escolha Inspecionar. O Chrome abre o painel Elements com esse elemento selecionado; o painel Styles mostra o CSS aplicado. O Google documenta esse fluxo em Abrir as Ferramentas do Desenvolvedor do Chrome.
Verifique as condições a seguir antes de calcular uma coordenada:
- A imagem participa do fluxo inline normal na linha em questão.
- Seu alinhamento efetivo é
vertical-align: baseline. - O modo de escrita é horizontal.
- Nem a imagem nem seus elementos ancestrais têm uma transformação ou um deslocamento de posicionamento visual que afete a medição.
- Você está medindo a própria imagem, e não um contêiner ao redor com comportamento próprio de linha de base.
Essas condições definem o escopo do cálculo. Por exemplo, vertical-align: bottom alinha um elemento à parte inferior da linha, enquanto middle usa a linha de base do elemento pai mais metade da altura-x. Nenhum dos valores significa que a borda inferior da imagem identifica a linha de base do elemento pai. A MDN explica essas distinções em sua referência de vertical-align.
Quando essas condições forem atendidas, use esta relação, expressando todas as distâncias em pixels CSS:
baselineY = imageBorderBoxBottom + imageMarginBottom
getBoundingClientRect() fornece a parte inferior da caixa de borda em relação à janela de visualização. Seu retângulo inclui preenchimento e bordas, então eles não devem ser somados novamente. As margens ficam fora desse retângulo. A MDN documenta a geometria retornada e o sistema de coordenadas em Element.getBoundingClientRect().
Para um cálculo explicitamente ilustrativo, suponha que a parte inferior da caixa de borda da imagem seja 240.5 e que sua margem inferior seja 6. Portanto, a linha de base fica a 246.5 pixels CSS da parte superior da janela de visualização. Se a parte superior da caixa de borda do contêiner for 200, essa linha de base estará a 46.5 pixels CSS da parte superior do contêiner. Esse deslocamento é medido a partir da borda da borda, e não automaticamente a partir da borda do conteúdo.
Meça outra imagem qualificada na mesma linha para fazer uma verificação cruzada. A borda inferior da margem dela deve indicar a mesma linha de base, mesmo que sua altura seja diferente. Uma divergência é motivo para inspecionar o alinhamento, os contêineres, o posicionamento ou a quebra de linha antes de alterar o CSS. Mantenha os valores fracionários durante a comparação.
Como verificar o resultado com um marcador de linha de base?
Um recurso de diagnóstico útil é um inline-block vazio, com largura, altura, margens, preenchimento e bordas iguais a zero, alinhado pela linha de base. Como ele não tem caixas de linha no fluxo, sua linha de base é a borda inferior da margem. Com todas essas dimensões iguais a zero, essa borda coincide com sua posição vertical medida. Isso segue a regra de linha de base para inline-block do CSS 2.
O programa completo de console a seguir cria uma demonstração temporária com duas imagens retangulares neutras e um marcador. Ele usa uma shadow root para manter os estilos internos do exemplo separados dos seletores comuns da página. Execute-o em um documento no modo de padrões, sem transformações no documento ou no elemento body. Ele mede apenas sua própria linha sintética, não o conteúdo de uma página existente.
Abra o Console com Command + Option + J, conforme documentado nos atalhos das Ferramentas do Desenvolvedor do Chrome, e execute o programa. Não é necessário instalar uma biblioteca nem usar um arquivo de imagem externo. O exemplo gera os retângulos com a API Canvas padrão.
(async () => {
if (document.compatMode !== "CSS1Compat") {
throw new Error("Use a standards-mode document for this demonstration.");
}
const host = document.createElement("div");
host.style.cssText =
"all:initial;position:fixed;top:16px;left:16px;" +
"z-index:2147483647;background:white;padding:16px;" +
"color:#222;font:16px/1.5 sans-serif;";
const root = host.attachShadow({ mode: "open" });
const label = document.createElement("div");
label.textContent = "Sample A and Sample B";
const row = document.createElement("div");
row.style.cssText =
"display:block;position:relative;width:280px;" +
"margin:0;padding:0;border:0;white-space:nowrap;" +
"writing-mode:horizontal-tb;font:16px/24px sans-serif;" +
"background:#eee;";
const close = document.createElement("button");
close.textContent = "Remove demonstration";
close.addEventListener("click", () => host.remove());
root.append(label, row, close);
document.body.append(host);
try {
const images = [];
const samples = [
{ label: "Sample A", height: 32, color: "#777" },
{ label: "Sample B", height: 48, color: "#aaa" }
];
for (const sample of samples) {
const canvas = document.createElement("canvas");
canvas.width = 64;
canvas.height = sample.height;
const context = canvas.getContext("2d");
if (!context) throw new Error("A 2D canvas context is required.");
context.fillStyle = sample.color;
context.fillRect(0, 0, canvas.width, canvas.height);
const image = document.createElement("img");
image.alt = sample.label;
image.width = 64;
image.height = sample.height;
image.style.cssText =
"display:inline;vertical-align:baseline;" +
"margin:0 12px 0 0;padding:0;border:0;";
image.src = canvas.toDataURL("image/png");
row.append(image);
images.push(image);
}
await Promise.all(images.map(image => image.decode()));
if (!host.isConnected) return;
const marker = document.createElement("span");
marker.style.cssText =
"display:inline-block;vertical-align:baseline;" +
"width:0;height:0;margin:0;padding:0;border:0;" +
"font-size:0;line-height:0;";
row.append(marker);
const baselineY = marker.getBoundingClientRect().bottom;
const rowTop = row.getBoundingClientRect().top;
console.table(images.map(image => ({
sample: image.alt,
imageBottom: image.getBoundingClientRect().bottom,
markerBaseline: baselineY,
baselineFromRowTop: baselineY - rowTop
})));
const guide = document.createElement("div");
guide.style.cssText =
"position:absolute;left:0;right:0;height:1px;" +
"background:#222;pointer-events:none;";
guide.style.top = `${baselineY - rowTop}px`;
row.append(guide);
} catch (error) {
host.remove();
throw error;
}
})();
O programa aguarda a decodificação das duas imagens antes de medir. O método documentado HTMLImageElement.decode() retorna uma promessa e pode ser rejeitado se a decodificação falhar; nesse caso, o programa remove o painel temporário e informa o erro.
As duas imagens têm margem inferior zero, portanto os valores de imageBottom informados devem corresponder a markerBaseline. A linha horizontal fina marca essa coordenada. O fundo da linha também permite distinguir a linha de base de qualquer espaço restante abaixo dela. Selecione Remove demonstration quando terminar.
Para um experimento controlado, altere a margem inferior das imagens de exemplo de 0 para 6px no estilo da imagem. As partes inferiores de suas caixas de borda devem ficar então seis pixels CSS acima da linha de base marcada. Aplique a fórmula anterior para recuperar a coordenada compartilhada. Assim, você terá duas referências independentes no exemplo: a borda da margem da imagem e o marcador vazio.
Quando este método exige uma abordagem diferente?
Um marcador é uma inserção de diagnóstico, portanto mede o layout resultante. Adicionar um a um parágrafo existente qualquer pode criar uma oportunidade de quebra ou afetar o layout de outra forma. Prefira o cálculo com a imagem existente quando os pré-requisitos forem atendidos. Se inserir um marcador em conteúdo real, confirme que as mesmas imagens continuam na mesma linha e que suas posições não mudaram.
| Situação | Próxima etapa adequada |
|---|---|
As imagens usam o alinhamento top, bottom ou middle |
Inspecione essa regra de alinhamento; não aplique sem alterações a fórmula para imagens alinhadas pela linha de base |
| A imagem está dentro de um contêiner inline-block | Inspecione a linha de base do contêiner, que pode vir de sua última linha de conteúdo |
| As imagens ocupam várias linhas após a quebra | Determine uma linha de base separadamente para cada linha |
| O elemento pai usa layout flex ou grid | Inspecione as regras de alinhamento desse layout antes de tratar a linha como uma linha inline |
| O documento usa escrita vertical | Use regras de linha de base que considerem o modo de escrita, em vez de uma coordenada y da borda inferior |
| A imagem não foi carregada corretamente | Resolva seu estado de renderização antes de tratá-la como uma caixa de imagem exibida |
A última condição importa porque o WHATWG descreve caminhos de renderização diferentes para imagens exibidas, imagens pendentes e texto alternativo. Portanto, uma imagem quebrada que exibe seu rótulo pode exigir uma interpretação diferente da imagem renderizada corretamente que você pretendia medir. Consulte as regras de renderização de imagens do HTML.
Evite deduzir que há uma única linha de base compartilhada apenas porque vários retângulos de imagem têm coordenadas superiores semelhantes. Imagens com alturas diferentes podem compartilhar uma linha de base e ter bordas superiores em posições distintas. Da mesma forma, bordas superiores alinhadas não comprovam alinhamento pela linha de base. Confirme quais caixas participam da mesma linha e qual regra de alinhamento se aplica.
APIs de medição de texto ou bibliotecas de modelagem podem fornecer a resposta?
Os métodos de geometria do navegador usados aqui retornam retângulos, em vez de um campo dedicado à linha de base da linha. Range.getClientRects() pode ajudar a inspecionar as áreas ocupadas por um intervalo, mas os retângulos retornados não fornecem diretamente a coordenada da linha de base. A MDN descreve essa interface em Range.getClientRects().
As métricas de texto do Canvas respondem a outra pergunta: como um trecho de texto no Canvas se relaciona com a linha de base de texto selecionada no contexto do Canvas. Por exemplo, actualBoundingBoxAscent e actualBoundingBoxDescent descrevem os limites visíveis do texto em relação a essa referência. Elas não localizam diretamente uma linha existente de imagens no DOM. Consulte a documentação de TextMetrics da MDN.
Uma biblioteca de modelagem de texto também tem uma responsabilidade mais restrita do que o layout de linhas do navegador. O manual do HarfBuzz afirma que ele modela sequências de texto e fornece informações de layout de glifos, enquanto a quebra de linhas de parágrafo e a justificação estão fora de sua responsabilidade. Portanto, instalar o HarfBuzz não fornece uma medição da linha do navegador composta apenas por imagens. Seu escopo documentado ajuda a delimitar essa responsabilidade.
Para uma linha horizontal composta apenas por imagens, primeiro confirme o fluxo inline normal e o alinhamento pela linha de base. Em seguida, obtenha a parte inferior da caixa de borda de uma imagem qualificada e some sua margem inferior. Use o marcador de tamanho zero em um layout controlado quando precisar de uma verificação visual cruzada e reavalie o contexto de formatação sempre que essas duas referências divergirem.
Fontes
Padrão HTML: Renderização — WHATWG.
Padrão HTML: O elemento canvas — WHATWG.
Abrir as Ferramentas do Desenvolvedor do Chrome — Google Chrome for Developers.
Propriedade CSS vertical-align — MDN Web Docs.
Elemento: método getBoundingClientRect() — MDN Web Docs.
HTMLImageElement: método decode() — MDN Web Docs.
Range: método getClientRects() — MDN Web Docs.
TextMetrics — MDN Web Docs.
O que o HarfBuzz não faz — HarfBuzz.