Fixing Baselines
- Published on:
- Categories:
- Typography 9, Obscure CSS 5, CSS 88
- Current music:
- 夢中夢 —
Unforgiven
- Current drink:
- Lapsang Souchong tea
Do you know how to center things in CSS? In year 2025? Easy! You just… align… or justify…, ok, maybe just place? your content… or items? …Forget it, this post is not about alignment. Or is it?
This post is about another common alignment concept that might seem simple: baseline alignment. In it, I share a technique I have already used for twelve years.
The Definition
To quote from the specs,
A baseline is a line along the inline axis of a line box along which individual glyphs of text are aligned.
The specs go into many details, and if you will start reading them (or if you have enough practice), you’ll get that it not ⭐️simple.
The Problems & Solutions
If you pay a close attention to the previous sentence, you might get the idea.
Overflowing Inline Elements
This is the simplest problem, which, today, has a simple solution. The problem: if you have an element with inline-block, giving it overflow: hidden (or auto, scroll, etc.) will break the baseline:
Hello, world!
<p>Hello, <span class="block-world">world</span>!</p> .block-world {
display: inline-block;
outline: 1px solid;
overflow: hidden;
} The solution: use inline-flex or inline-grid instead. They will retain the baseline!
Hello, world!
<p>Hello, <span class="flex-world">world</span>!</p> .flex-world {
display: inline-flex;
outline: 1px solid;
overflow: hidden;
} More than eleven years ago — almost twelve — I wrote a dedicated article about this topic: “Battle for Baseline”. If you want to read about all the crossbrowser issues I had to dig through to make this work at that time, I invite you to read it.
But today, now that inline-flex and inline-grid are baseline (sorry), we no longer have the same bugs and issues. Hooray.
Icons Before Text
The second problem can be much more pronounced today, and does not have a solution built into the specs (or — I don’t know about it).
It is what happens when you have some inline element, for example, a button, and then inside of it, but before its text, you add some icon. And then, you want to center everything vertically inside, but, at the same time, align this button’s content with the baseline of the nearby text. It doesn’t work:
Hello, !
<p>
Hello,
<button type="button" class="button-world">
<span class="icon">🌐</span>
world
</button>!
</p> .button-world {
display: inline-flex;
padding: 0 0.5em;
gap: 0.5em;
place-items: center;
border: 1px solid;
border-radius: 9em;
overflow: hidden;
font: inherit;
}
.icon {
font-size: 2em;
} If the font-size of the icon that contains an emoji was the same as of the text, it would look ok. But if we modify this element’s font-size, or use an empty element or a replaced element instead, then this element will be used for determining the baseline of our button.
This is because, by default, the button uses the first “baseline set” for aligning that inline-flex element, and that first baseline will come from our icon!
baseline-source
If we dig a bit into the specs, we could find a partial solution for our problem: the very obscure baseline-source property.
Why is it obscure?
First, it is not baseline (I am so sorry again), and is only supported in Firefox and Chrome, with support missing from Safari. It is not even present on MDN.
Then, if we were to use it in browsers that do support it to address our problem, it will help for a simple case:
Hello, !
.with-baseline-source {
baseline-source: last;
} But — the only value other than auto and first is last. And, guess what will happen if we would also add an icon to the end of our button:
Hello, !
And, the second problem will happen if we were to have a multiline button — and would want, for some reason, to still use the default behavior of aligning it via its first line:
Hello, !
It is not just the last element that defines the baseline now — it is the last line box of the last element. Oops.
An Extra Element Solution
So, how can we solve this problem today, and more reliably?
The solution that I used for years, but I don’t think I ever shared (or saw mentioned elsewhere — if someone did write about it, please let me know!) — to just use an extra element before the element that otherwise breaks our baseline. That extra element should have the same font metrics as the real content, and we need to make this element to not contribute any space, but otherwise this works pretty well:
Hello, !
.with-extra-element {
--gap: 0.5em;
gap: var(--gap);
}
.with-extra-element::before {
content: '\a0'; /* */
width: 0;
display: inline-block;
visibility: hidden;
margin-left: calc(-1 * var(--gap, 0));
} A few things to note:
- Because our element uses a
gap, we have to remove the extra gap that will appear when we insert the pseudo-element. - I am using a
'\a0'— a no-break space. I found it to work the best. Extra tip: whenever you use escaped characters in your CSS, add comments that communicate what they are. - For it to not impact anything other than the baseline, we give it
width: 0and hide it viavisibility. - Of course, if you don’t have a gap, you can skip the margin stuff.
And that’s it!
Prior Art
While I am using this technique for almost twelve years already, I used Marginalia search to look up if anyone else mentioned it elsewhere.
I might’ve missed something, but the closest thing I found is “SVG icon sizing and alignment” article by Dimiter Petrov.
Could There Be a Better Way?
Currently, we can only control which baseline set is used for our element on the wrapper element.
But what if we could have something like baseline-presence: exclude that could be used on our inner elements, so we could add it to our icons, and then they won’t participate in the definition of our baseline?
Right after published this post, I opened an issue about this in CSSWG, as I did not find any others that would cover this. I work on design systems for a very long time, and this problem appears over and over in all of them.
And — if you know of a different solution — please let me know, or — even better — write your own post about it and share it!