Cap-Height Vertical Align
- Published on:
- Categories:
- Cap unit, CSS 55, Typography 3
- Current music:
- Ichiko Aoba — Sagu Palm’s Song
- Current drink:
- Lapsang Souchong tea
Introduction
The new year is here, and, with it, a season where people put out their CSS wishlists for the future. I did already read two of them: December’s CSS wishlist from Sarah Gebauer, and today’s “Tyler’s CSS Wish List for 2024” from Tyler Sticka.
One thing I love to do when I see these wishlists is to think of all the ways we can do the things from them already. It is always a good challenge, and quite often can result in some wild experiments.
There are other things that I’d want to experiment on later, but one thing caught my attention in Tyler’s list: an ability to vertically align to the middle of the font’s cap-height.
This is something I did research in the past, and I am thrilled to report that there are at least two relatively ok workarounds available in the most recent browsers, courtesy of the new cap
unit!
Two Techniques
Unknown Size, Negative Margin
The simplest way we can utilize the cap
to achieve alignment to the middle of the capital letter of the font (also known as “cap-height”) is by relying on vertical-align: middle
and a simple calculation:
Why do we subtract cap height from the x-height? When we align things with vertical-align
to middle
, we’re aligning things to the middle of the font’s x-height. Here is an example of how things look without our technique:
Occasionally, this method might work, but in very rare cases where we’re certain that we’re aligning things over the lowercase letters only, with not many ascenders on the letters. Otherwise, predominantly, we want to align things to the cap height.
So, if the middle
value aligns things to the x-height, but we want the cap height, the only thing we need to do is just move the element by its difference! That’s where the 1ex - 1cap
comes from, and the element’s height is already accounted for by the middle
value.
Known Size, Just Vertical-Align
The example above is nice but requires us to use a negative margin. There is a different way to achieve the same, but only if we know the height of the element we’re aligning:
Our formula here is 0.5cap - 0.5 * var(--size)
: half of the cap height minus half of our element’s height. How is this calculated? When the value of vertical-align
is a <length>
, an element is aligned in a way its bottom edge stays on the text’s baseline:
You might ask: why use this method when it has the limitation of having to know the element’s height?
The answer: sometimes we might not know the context in which our element will be present, and if we will place our element in a context with align-items: center
, it might not look good! We can easily demonstrate this by placing both examples inside a flex context:
Because, in this case, we’re relying on the flex alignment, the vertical-align
stops working. For our second technique, it is not relevant, as it was the only thing that handled the alignment (which is now handled by flex), but for the first technique, the margin stays, resulting in misalignment.
That means, that if we’re sure our element will always be in a regular inline flow, we can rely on the negative margin. But if we know the element’s size, then it might be beneficial to use the method that only uses vertical-align
.
The Fallbacks
As I mentioned at the beginning of this post, while the cap
is newly available, we should not use it in production as is. Too soon.
However, usually, it is not super complicated to work around it, at least partially.
The way to go could be to use a CSS variable and @supports
, below is the same technique as in the first example, but modified to work without cap
support:
The only thing we have to do is to replace all our cap
units with a var(--cap)
variable and set it via @supports
to 1cap
while setting it to the necessary value in em
based on the font’s metrics by default.
How did I determine the value 0.704583em
? It is simple! Now, with cap
being available in the browsers, it is effortless to get the proportions of any font. What I did was get a square with a font-size of 1000px
, and set its height to 1cap
, which resulted in 704.583px
, which, when divided by 1000
results in our value!
Of course, it will work perfectly only for the font I did test it with — “Iowan Old Style” in my case, which I can see on my macOS as I’m using a stack with the built-in fonts. If you’re looking at this example in a browser that does not support cap
and does not have Iowan Old Style installed, the alignment could break a bit. Which, in most cases, could be fine if this is only a visual alignment issue, and should not result in the content being inaccessible.
And if you’re using a custom web font, then it is more likely it will be always present, so it is safer to embed its metrics in this way.
In Conclusion
I’m delighted that browsers improve the ways we can work with web typography. Maybe progress is not as fast as I would’ve liked (there are so many things I’d want to have!), but the arrival of cap
(alongside other very helpful units like ic
and lh
) unlocks many doors.
As demonstrated, both techniques involving the cap
rely on calculations and have their issues — that’s why I will still recommend going over the Tyler’s CSSWG issue and voting for it, optionally providing your use cases or thoughts on the desired syntax in the comments. And maybe one day we could solve this just by using a single declaration involving vertical-align
!