En textlinje med blandade storlekar kan överstiga båda spannas line-height-värden eftersom baslinjejustering kombinerar deras övre och undre utsträckning. Den här guiden visar hur du diagnostiserar den extra höjden och justerar en rad med två etiketter i en Mac-stationär webbläsare.
Varför bestämmer inte det högsta spannet linjehöjden?
Överväg en abstrakt sidhuvud som innehåller en stor Label A bredvid en liten Label B. Båda spannen har line-height: 40px, men deras innehållande rad mäter mer än 40 CSS-pixel. Det resultatet följer av hur inline-layout justerar boxar innan den beräknar den omslutande linjen.
För vanlig inline-text beräknar webbläsaren varje boxs höjdbidrag, justerar boxarna vertikalt och omsluter sedan deras kombinerade utsträckning. Med baslinjejustering kan den högsta toppen och den lägsta botten tillhöra olika spann. Resultatet kan därför överstiga varje enskilt bidrag. Föräldern bidrar också med en osynlig minimistorieksbox, traditionellt kallad strut. W3C: Line height calculations
Baslinjen är justeringsreferensen för texten. Den är inte den vertikala mittenpunkten av sitt linjehöjdsbidrag. Att ge olika stora text lika linjehöjder ger inte båda delarna lika avstånd ovanför och under den baslinjen. Det initiala vertical-align-värdet är baseline. MDN: vertical-align
Håll tre mätvärden separata: typsnittets ascent- och descent-mått, de synliga gränserna för de specifika bokstäverna, och det utrymme som bidras till radlayout. Ett spanns färglagda bakgrund eller urvalsrektangel bör inte behandlas som en direkt mätning av den omslutande line-boxen; CSS skiljer på inline-malningsgeometri och linjehöjdsberäkningar. W3C: Leading and half-leading
Hur kan två 40px-bidrag ge en 48,4px-linje?
För vanlig baslinjejusterad text med en explicit linjehöjd är den användbara beräkningen: subtrahera typsnittets ascent plus descent från linjehöjden, fördela sedan det återstående utrymmet lika ovanför och under. Det extra utrymmet kallas leading; varje halva kallas half-leading. CSS Inline Layout Level 3 Working Draft beskriver denna beräkning och tillåter negativ leading. Dess utkaststatus etablerar inte webbläsarstöd för varje nyare egenskap den innehåller. W3C: Calculating layout bounds
Följande indata är illustrativa, inte mätningar av Arial eller något Mac-typsnitt. Anta en förälder-strut vars bidrag matchar det lilla spannet, utan annat inline-innehåll eller vertikala förskjutningar.
| Bidrag | Typsnitt ascent | Typsnitt descent | Linjehöjd | Halv-leading | Ovanför baslinjen | Under baslinjen |
|---|---|---|---|---|---|---|
| Stort spann | 32px | 8px | 40px | 0px | 32px | 8px |
| Litet spann | 9,6px | 2,4px | 40px | 14px | 23,6px | 16,4px |
| Förälder-strut | 9,6px | 2,4px | 40px | 14px | 23,6px | 16,4px |
För detta baslinjejusterade exempel, ta den största utsträckningen på varje sida oberoende: max(32, 23.6, 23.6) + max(8, 16.4, 16.4) = 48.4px. Varje bidrag summerar till 40px, men det stora spannet sätter toppen medan det lilla spannet och föräldern sätter botten. Detta utarbetade resultat härleds från den dokumenterade layoutberäkningen, inte en förutsagd webbläsarmätning. W3C: Inline layout model
Den diagnostiska frågan är därmed: vilken deltagare sätter varje kant? Att minska det lilla spannets linjehöjd kan ta bort dess undre bidrag medan ett identiskt bidrag från förälder-struten kvarstår.
Hur kan du reproducera och inspektera det på en Mac?
Använd Chrome på din Mac för den här genomgången. Öppna en ny flik, navigera till about:blank, tryck sedan på Command + Option + J för att öppna Console. Chrome dokumenterar denna genväg för att köra JavaScript. Chrome DevTools: Overview
Kör det fullständiga programmet nedan i den tomma fliken. Det skapar tre syntetiska rader med samma etiketter, skriver ut deras mätvärden och använder en shadow root för att hålla demonstrationsstilarna tillsammans. Ingen paketinstallation eller externt bibliotek krävs. Att köra det igen ersätter dess tidigare demonstration.
(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
})));
})();
Programmet väntar på document.fonts.ready, som löses efter att dokumentets typsnittsinläsning och relaterade layoutarbeten har slutförts. Om du senare introducerar ett annat typsnitt eller ändrar innehållet, mät igen efter att ändringen har slutförts. MDN: FontFaceSet.ready
Läs de tre raderna som en kontrollerad jämförelse. Den första ger båda spannen och föräldern en 40px linjehöjd. Den andra minskar bara det lilla spannets linjehöjd till 12px. Den tredje ändrar också förälderns typsnittsstorlek till 40px, vilket gör att dess strut matchar den stora textens typsnittsstorlek och linjehöjd. Om den andra raden förblir hög men den tredje krymper, identifierar den jämförelsen förälderns bidrag.
Använd din webbläsares resultat snarare än att förvänta dig det illustrativa 48,4px-värdet. Programmet mäter varje rad med getBoundingClientRect(), som returnerar ett elements avgränsande rektangel inklusive padding och kanter. Här har raderna varken det ena eller det andra, innehåller en enda ojämnad rad och har automatisk höjd, vilket gör deras höjder till användbara proxies för line-box-höjder. Att mäta ett spann med samma API skulle mäta det spannets rektangel, inte direkt exponera den omslutande line-boxen. MDN: getBoundingClientRect()
Vad bör du kontrollera i en befintlig layout?
Kontrollklicka på relevant text och välj Inspectera. I Elements, inspektera båda spannen och deras innehållande block. Använd Styles för att spåra deklarationer och Computed för att se de tillämpade värdena; aktivera Visa alla vid behov för att inspektera arvda egenskaper. Chrome dokumenterar dessa vyer och deras egenskapsfilter. Chrome DevTools: CSS features reference
Registrera font-family, font-size, line-height, display, och vertical-align för varje deltagare. Börja med föräldern snarare än att bara titta på de två spannen. Använd sedan denna sekvens för att isolera orsaken:
- Bekräfta layoutmodellen. Fastställ om barnen deltar i normal inline-flöde eller är flex- eller grid-objekt.
- Bekräfta en enda rad. Använd ett brett testområde och korta syntetiska etiketter så att radbrytning inte förvirrar mätningen.
- Kontrollera justering. Ta tillfälligt bort baslinjeförskjutningar eller icke-standard vertikal justering innan du tillämpar den enkla beräkningen ovan.
- Ändra en bidragsgivare. Minska det lilla spannets linjehöjd, mät, testa sedan förälderns typsnitt och linjehöjdsbidrag separat.
- Återställ realistiska förhållanden. Kontrollera igen det avsedda typsnittet, tillgänglig bredd och text efter att orsaken identifierats.
Kontrollera också arv. En enhetslös line-height: 1.2 skickar ner en multiplikator som varje efterkommande tillämpar på sin egen typsnittsstorlek. En längd som 40px skickar ner en fast längd om den inte åsidosätts; procenter beräknas mot det deklarerande elementets typsnittsstorlek. Dessa val kan ge liten text oväntat generöst leading. normal är typsnitts- och webbläsarberoende, så det är olämpligt som en fast numerisk antagande. MDN: line-height values and inheritance
Vilken justering passar det avsedda resultatet?
Välj justering enligt vad de två etiketterna representerar. Ett kompakt rubrikutkast gjort av separata etiketter har andra krav än ord inbäddade i ett radbrytande stycke.
| Avsedd resultat | Justering att testa | Vad att verifiera |
|---|---|---|
| Behåll en gemensam textbaslinje | Ge det lilla spannet en lämplig linjehöjd och justera förälderns typsnittsmått med huvudtexten | Förälder-struten levererar inte längre en oönskad undre utsträckning |
| Justera topperna av separata etiketter | Testa vertical-align: top på båda inline-spann |
Den ändrade baslinjeförhållandet passar designen |
| Centrera separata etiketter inom en rad | Använd en flex-container med align-items: center |
Beteende vid smal bredd och objektomslutning passar fortfarande layouten |
| Behåll kontinuerligt stycke-flöde | Behåll inline-text och använd avsiktliga arvda linjehöjdvärden | Alla resulterande rader tar hand om den blandade texten |
vertical-align: top justerar ett element och dess efterkommande till toppen av linjen. middle har en annan definition: det justerar elementets mittpunkt med förälderbaslinjen plus hälften av förälderns x-höjd. Det betyder därmed inte geometrisk centrering inom hela raden, och att ändra justering garanterar inte en specifik slutlig höjd. MDN: vertical-align values
För två oberoende etiketter ger flexbox explicit korsaxelsjustering genom align-items. Centreringsjustering och baslinjejustering uttrycker olika designval. Att byta till flexbox ändrar layoutkontexten, så använd det när etiketterna är separata objekt och bedöm resultatet vid smalare bredder. MDN: align-items
En mindre linjehöjd kan också låta text målas utöver sitt layoutbidrag. Kontrollera angränsande rader efter att avståndet trängts ihop; en kompakt mätt box ensam etablerar inte att texten passar utan överlapp. W3C: Leading and half-leading
Kan canvas-mätningar ersätta webbläsarens radlayout?
Canvas-mätningar besvarar en relaterad fråga. WHATWG definierar measureText() och skiljer på typsnittets ascent/descent-mått från de faktiska avgränsande gränserna för den specifika texten. Dessa mätningar är användbara för canvas-ritning, men en bläck-gränsmätning inkluderar inte den omgivande DOM-förälderns strut, inline-justering och linjehöjdsbidrag. Använd det inte ensamt för att förutsäga denna rads höjd. WHATWG: TextMetrics
Bevara textformningsgränser när du experimenterar. CSS kräver att formning fortsätter över inline-gränser när det inte finns någon effektiv formateringsändring, medan vissa gränser – inklusive icke-initial vertikal justering – kräver att formningen bryts. Undvik att omslutta enskilda tecken bara för att mäta dem: att dela upp grafemkluster eller ligaturer kan ge inkonsistenta resultat. Behåll fullständiga etiketter eller meningsfulla textsträckor intakta. W3C: Text processing and shaping across element boundaries
För att lösa den extra höjden, identifiera deltagaren som sätter toppen och deltagaren som sätter botten, inklusive föräldern. Justera deras linjehöjdsbidrag eller välj den justeringsmodell som matchar etiketternas syfte, mät sedan den fullständiga raden igen med dess avsedda text och typsnitt.
Källor
- Visual formatting model details — W3C.
- CSS Inline Layout Module Level 3 — W3C.
- line-height CSS property — MDN Web Docs.
- vertical-align CSS property — MDN Web Docs.
- Overview: Chrome DevTools — Google for Developers.
- CSS features reference — Google for Developers.
- FontFaceSet: ready property — MDN Web Docs.
- Element: getBoundingClientRect() method — MDN Web Docs.
- align-items CSS property — MDN Web Docs.
- HTML Standard: Canvas — WHATWG.
- CSS Text Module Level 3 — W3C.