Better Vertical Text Alignment With text-box-trim
A heading can look slightly out of line with an image even when their boxes align. CSS text-box-trim adjusts the space at the text box’s edges using font metrics, so the alignment can follow the type more closely.
I find that more appealing than a font-specific offset. It expresses which text edge matters, while leaving the browser to use the font’s metrics.
View code
<label for="sample">Sample text</label><input id="sample" type="text" value="Typography Āg">
<label for="font">Font family</label><select id="font"><option value="system-ui">System UI</option><option value="serif">Serif</option></select>
<div class="samples"><section><h2>Normal text box</h2><p class="sample normal">Typography Āg</p></section><section><h2>Trimmed: cap / alphabetic</h2><p class="sample trimmed">Typography Āg</p></section></div>
<p id="sizes" role="status"></p>
<p id="support" class="note">The outline shows the layout box. Accent marks and descenders can extend past a trimmed edge.</p>button, select { padding: .35rem .6rem; border: 1px solid var(--clg); background: var(--cw); color: var(--cb); }
input[type="range"] { width: min(100%, 18rem); vertical-align: middle; }
h2, h3 { font-size: 1rem; }
.note { color: var(--clg); }
label { display: block; margin-block: .5rem .25rem; }
input, select { max-width: 100%; padding: .35rem; }
.samples { --demo-font: system-ui; }
.samples section { padding-block: .5rem; }
h2 { margin-block: .5rem 1rem; font-weight: 400; }
.sample { width: fit-content; max-width: 100%; margin: 0; font-family: var(--demo-font); font-size: 2rem; line-height: 1.4; background: var(--cg); outline: 1px solid var(--clg); overflow-wrap: anywhere; }
@supports (text-box: trim-both cap alphabetic) { .trimmed { text-box: trim-both cap alphabetic; } }const input = document.querySelector('#sample');
const font = document.querySelector('#font');
function update() {
document.querySelector('.samples').style.setProperty('--demo-font', font.value);
document.querySelectorAll('.sample').forEach(sample => { sample.textContent = input.value; });
const height = selector => Number(document.querySelector(selector).getBoundingClientRect().height.toFixed(1));
document.querySelector('#sizes').textContent = `Box heights: normal ${height('.normal')}px; trimmed ${height('.trimmed')}px.`;
}
input.addEventListener('input', update);
font.addEventListener('change', update);
window.addEventListener('resize', update);
if (!CSS.supports('text-box', 'trim-both cap alphabetic')) document.querySelector('#support').textContent = 'This browser does not support the text-box shorthand. Both samples keep their ordinary text boxes.';
update();Choose the edges for text-box-trim
A background makes the heading’s box easier to see:
<h1 class="hero-title">Typography</h1>
.hero-title {
margin: 0;
font-family: system-ui, sans-serif;
font-size: clamp(3rem, 8vw, 6rem);
line-height: 1.1;
background: #eee;
text-box-trim: trim-both;
text-box-edge: cap alphabetic;
}
The text-box-trim property selects which edges to trim. Here, trim-both requests trimming at both the start and end of the text block.
The text-box-edge values choose the metrics. In horizontal text, cap refers to the font’s capital-letter height, and alphabetic refers to its alphabetic baseline.
That gives the heading box edges aligned to those metrics. It does not measure the outline of every letter in “Typography”. The g and p, for example, have descenders below the baseline.
Trimming space does not cut off the letters
The CSS inline layout specification treats ink outside a trimmed box as overflow. Trimming changes layout edges; it is not a command to crop glyphs to those edges.
An accent can extend above the cap height, and a descender can extend below the baseline. Adding overflow: hidden elsewhere can therefore make an otherwise reasonable trim interact badly with the surrounding layout.
Check the actual words and languages the component will display. A short heading containing only capitals is a useful comparison, but it does not cover accented text or letters with descenders.
Use the shorthand when the intent is clear
The text-box shorthand combines the trimming choice and edge metrics. It can replace the two longhands in the first example:
.hero-title {
text-box: trim-both cap alphabetic;
}
The longhands are useful while explaining or adjusting the two decisions separately. Once the intent is clear, the shorthand keeps them together.
Different fonts have different metrics, so the same declaration need not produce identical dimensions across typefaces. The useful consistency is the alignment rule, rather than a fixed number of pixels trimmed from every font.
Keep line-height for spacing between lines
line-height controls line box height. For ordinary text, it is an important part of the spacing between successive lines.
The trimming rules for a block concern the start of its first formatted line and the end of its last formatted line. They do not remove the space between every pair of lines.
A heading that wraps still needs a sensible line height. The example keeps line-height: 1.1; changing its outer edges does not make the internal line spacing irrelevant.
That is also why a global trimming rule for paragraphs would be a poor starting point. Keep the change scoped to a component whose alignment needs it, then check both one-line and wrapped text.
Trim a button label without shrinking the whole control
A button’s label and its surrounding space are separate concerns. An inner span makes it possible to adjust the text while keeping padding on the button:
<button class="save-button" type="button">
<span class="button-label">Save changes</span>
</button>
.save-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-block-size: 2.75rem;
padding: 0.75rem 1rem;
font: inherit;
}
.button-label {
display: block;
line-height: 1.2;
}
@supports (text-box: trim-both cap alphabetic) {
.button-label {
text-box: trim-both cap alphabetic;
}
}
The button retains its padding and minimum size independently of the label’s trim. Browsers without the shorthand keep the ordinary label box.
This is a layout example, not a claim that every button needs an extra span. If a label already sits comfortably inside its control, the additional structure may solve nothing.
Keep an ordinary heading underneath
MDN lists text-box-trim as Baseline 2026, available across current browser versions since August 2026. Older versions can still need a fallback.
For a complete heading stylesheet, keep the readable base styles outside the feature query:
.hero-title {
margin: 0;
font-family: system-ui, sans-serif;
font-size: clamp(3rem, 8vw, 6rem);
line-height: 1.1;
background: #eee;
}
@supports (text-box: trim-both cap alphabetic) {
.hero-title {
text-box: trim-both cap alphabetic;
}
}
Use this version instead of the first stylesheet when the trim should be an optional enhancement. The query checks the exact shorthand used inside it, while the font size and line height remain available without it.
A large heading beside an image is where I would start. The alignment benefit is visible, and the ordinary text box is a reasonable fallback. Keeping that scope small also makes it easier to check font changes and translated headings before applying the same rule elsewhere.