Per una riga orizzontale di immagini inline allineate alla baseline, la baseline si trova sul bordo del margine inferiore di ogni immagine. Questa guida mostra come individuare quella coordinata in Chrome su Mac, verificarla con un piccolo marcatore e riconoscere quando questo metodo non si applica.

Perché una riga contenente solo immagini ha una baseline?

Una riga con solo immagini partecipa comunque al layout inline CSS. Un elemento img visualizzato correttamente è un elemento rimpiazzato: il browser dispone la sua scatola secondo il CSS invece di trattare la sua immagine come testo. WHATWG documenta questo comportamento di rendering nelle regole di rendering delle immagini dello standard HTML.

Nel layout inline orizzontale ordinario, un'immagine senza una baseline testuale intrinseca usa il bordo del margine inferiore per l'allineamento alla baseline. Di conseguenza, due immagini allineate alla baseline con altezze diverse possono condividere la stessa baseline mentre i loro bordi superiori si trovano ad altezze diverse. La regola riguarda le loro scatole; non esamina l'immagine al loro interno. Vedere CSS 2, allineamento verticale.

Tre confini diversi sono importanti:

Confine Significato Riferimento baseline adatto?
Bordo dell'immagine visibile Dove finiscono i pixel visibili dell'immagine No; il file può contenere spazio trasparente
Bordo inferiore del border-box dell'immagine Il fondo restituito da getBoundingClientRect() Sì, dopo aver tenuto conto del margine inferiore e confermato l'allineamento alla baseline
Bordo inferiore del line-box Il limite inferiore della linea CSS completa No; può estendersi sotto la baseline

Il font del genitore e line-height contano ancora quando non ci sono lettere visibili. Il CSS descrive un contributo invisibile basato sul font chiamato strut, che aiuta a stabilire l'altezza minima e la profondità della linea. Questo spiega perché un'immagine può poggiare sulla baseline mentre la linea mantiene spazio sotto di essa. La specifica CSS Inline Layout descrive queste metriche del font e i limiti di layout; la versione pubblicata recuperata è una Working Draft, quindi le sue funzionalità più recenti non dovrebbero essere considerate universalmente supportate dai browser.

Come trovare la baseline in una pagina esistente?

Inizia con una pagina che puoi ispezionare in Chrome sul tuo Mac. Fai clic destro su un'immagine e scegli Ispeziona. Chrome apre il pannello Elements con quell'elemento selezionato; il pannello Styles mostra il CSS applicato. Google documenta questo flusso di lavoro in Aprire Chrome DevTools.

Verifica le seguenti condizioni prima di calcolare una coordinata:

  1. L'immagine partecipa al flusso inline normale sulla linea di destinazione.
  2. Il suo allineamento effettivo è vertical-align: baseline.
  3. La modalità di scrittura è orizzontale.
  4. Né l'immagine né i suoi antenati hanno un transform o un offset di posizionamento visivo che influenzi la misurazione.
  5. Stai misurando l'immagine stessa, non un wrapper circostante con il suo comportamento di baseline.

Queste condizioni definiscono l'ambito del calcolo. Ad esempio, vertical-align: bottom allinea un elemento con il fondo della linea, mentre middle usa la baseline del genitore più la metà della sua x-height. Nessuno dei due valori significa che il bordo inferiore dell'immagine identifichi la baseline del genitore. MDN spiega queste distinzioni nel suo riferimento vertical-align.

Una volta che queste condizioni sono soddisfatte, usa questa relazione, con tutte le distanze espresse in pixel CSS:

baselineY = imageBorderBoxBottom + imageMarginBottom

getBoundingClientRect() fornisce il bordo inferiore del border-box relativo al viewport. Il suo rettangolo include padding e bordi, quindi non vanno aggiunti una seconda volta. I margini si trovano fuori da quel rettangolo. MDN documenta la geometria restituita e il sistema di coordinate in Element.getBoundingClientRect().

Per un calcolo esplicitamente illustrativo, supponiamo che il bordo inferiore del border-box dell'immagine sia 240.5 e il suo margine inferiore sia 6. La baseline è quindi 246.5 pixel CSS sotto la parte superiore del viewport. Se il bordo superiore del border-box del contenitore è 200, quella baseline è 46.5 pixel CSS sotto il bordo superiore del contenitore. Questo è un offset dal bordo, non automaticamente dal bordo del contenuto.

Misura un'altra immagine qualificata sulla stessa linea come controllo incrociato. Il suo bordo del margine inferiore dovrebbe identificare la stessa baseline, anche se la sua altezza è diversa. Un disaccordo è un motivo per ispezionare allineamento, wrapper, posizionamento o interruzione di linea prima di modificare il CSS. Mantieni i valori frazionari durante il confronto.

Come verificare il risultato con un marcatore di baseline?

Una diagnostica utile è un inline-block vuoto con larghezza, altezza, margini, padding e bordi zero, allineato alla baseline. Poiché non ha line box in-flow, la sua baseline è il suo bordo del margine inferiore. Con tutte quelle dimensioni a zero, quel bordo coincide con la sua posizione verticale misurata. Questo segue la regola CSS 2 per la baseline degli inline-block.

Il seguente programma console completo crea una dimostrazione temporanea con due immagini rettangolari neutre e un marcatore. Usa uno shadow root per mantenere gli stili interni del campione separati dai normali selettori di pagina. Eseguilo in un documento in modalità standard senza transform sul documento o sul body. Misura solo la sua riga sintetica, non il contenuto di una pagina esistente.

Apri la Console con Command + Option + J, come documentato in Scorciatoie DevTools di Chrome, ed esegui il programma. Non è richiesta l'installazione di librerie o file immagine esterni. Il campione genera i suoi rettangoli con la standard Canvas API.

(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;
  }
})();

Il programma attende che entrambe le immagini vengano decodificate prima di misurare. Il metodo HTMLImageElement.decode() documentato restituisce una promise e può essere rifiutato se la decodifica fallisce; in quel caso il programma rimuove il suo pannello temporaneo e segnala l'errore.

Entrambe le immagini hanno margine inferiore zero, quindi i loro valori imageBottom riportati dovrebbero corrispondere a markerBaseline. La sottile guida orizzontale segna quella coordinata. Lo sfondo della riga ti permette anche di distinguere la baseline da qualsiasi spazio rimanente sotto di essa. Seleziona Rimuovi dimostrazione quando hai finito.

Per un esperimento controllato, cambia il margine inferiore delle immagini del campione da 0 a 6px nello stile dell'immagine. I loro bordi inferiori del border-box dovrebbero quindi trovarsi a sei pixel CSS sopra la baseline del marcatore. Applica la formula precedente per recuperare la coordinata condivisa. Questo ti dà due riferimenti indipendenti all'interno del campione: il bordo del margine dell'immagine e il marcatore vuoto.

Quando questo metodo richiede un approccio diverso?

Un marcatore è un inserimento diagnostico, quindi misura il layout risultante. Aggiungerlo a un paragrafo esistente arbitrario può introdurre un'opportunità di interruzione o influenzare altrimenti il layout. Preferisci il calcolo sull'immagine esistente quando i suoi prerequisiti sono soddisfatti. Se inserisci un marcatore in contenuto reale, conferma che le stesse immagini rimangano sulla stessa linea e che le loro posizioni non siano cambiate.

Situazione Passo successivo appropriato
Le immagini usano allineamento top, bottom o middle Ispeziona quella regola di allineamento; non applicare la formula per immagini allineate alla baseline invariata
L'immagine si trova dentro un wrapper inline-block Ispeziona la baseline del wrapper, che può provenire dalla sua ultima linea di contenuto
Le immagini occupano più linee con wrapping Determina una baseline separatamente per ogni linea
Il genitore usa layout flex o grid Ispeziona le regole di allineamento di quel layout prima di trattare la riga come una linea inline
Il documento usa scrittura verticale Usa regole di baseline consapevoli della modalità di scrittura invece di una coordinata y del bordo inferiore
L'immagine non è stata caricata correttamente Risolvi il suo stato di rendering prima di trattarla come una scatola di immagine visualizzata

L'ultima condizione è importante perché WHATWG descrive percorsi di rendering diversi per immagini visualizzate, immagini in attesa e testo alternativo. Un'immagine rotta che mostra la sua etichetta potrebbe quindi richiedere un'interpretazione diversa dall'immagine renderizzata correttamente che intendevi misurare. Vedi le regole di rendering delle immagini HTML.

Evita di dedurre una baseline condivisa solo perché diversi rettangoli di immagine hanno coordinate superiori simili. Immagini con altezze diverse possono condividere una baseline e avere top diversi. Al contrario, top allineati non stabiliscono allineamento alla baseline. Conferma quali scatole partecipano alla stessa linea e quale regola di allineamento si applica.

Le API di misurazione del testo o le librerie di shaping possono fornire la risposta?

I metodi di geometria del browser usati qui restituiscono rettangoli, non un campo dedicato alla baseline della linea. Range.getClientRects() può aiutare a ispezionare le aree occupate da un range, ma i suoi rettangoli restituiti non forniscono direttamente la coordinata della baseline. MDN descrive quell'interfaccia in Range.getClientRects().

Le metriche del testo Canvas rispondono a un'altra domanda: come un pezzo di testo canvas si relaziona alla baseline del testo selezionata nel contesto canvas. Ad esempio, actualBoundingBoxAscent e actualBoundingBoxDescent descrivono i limiti dell'inchiostro del testo rispetto a quel riferimento. Non localizzano direttamente una riga di immagini DOM esistente. Vedi la documentazione TextMetrics di MDN.

Una libreria di text-shaping ha anche una responsabilità più ristretta del layout di linea del browser. Il manuale di HarfBuzz afferma che modella run di testo e fornisce informazioni di layout dei glifi, mentre l'interruzione di linea del paragrafo e la giustificazione rimangono fuori dalla sua responsabilità. Quindi, installare HarfBuzz non fornisce una misurazione della riga di solo immagini di un browser. Il suo ambito documentato aiuta a stabilire quel confine.

Per una riga orizzontale di solo immagini, prima conferma il flusso inline normale e l'allineamento alla baseline. Poi leggi il bordo inferiore del border-box di un'immagine qualificata e aggiungi il suo margine inferiore. Usa il marcatore a dimensione zero in un layout controllato quando hai bisogno di un controllo visivo incrociato, e rivaluta il contesto di formattazione ogni volta che quei due riferimenti non concordano.

Fonti