Hacker News new | ask | show | jobs
by overthrow 1180 days ago
The problem is caused by vertical-align. The default value for vertical-align is `baseline`. The flexbox container inside pill2 "hides" the inner text, so pill2 is treated as having no text at all (for layout purposes). This is treated the same as a font having zero height. So the browser aligns a 16px font (or whatever) with a 0px font, and it comes out misaligned.

My two solutions in order of preference would be:

1. Get rid of the inner flexbox, so the layout engine can see through to the text inside and use its baseline for alignment. Rely on the pill's parent for vertical alignment. I don't think I've ever come across a double-nested <span><span></span></span> (or div) that couldn't be flattened out.

2. Give .pill `vertical-align: middle` so its baseline is not used for alignment at all--but then the other non-pill items will need `vertical-align: middle` too in order to line up.

The author went the opposite way as #1 and added dummy text inside the pill, but outside the flex. Since the text is not inside a block element, the layout engine can see it. It has the same font size as the outer text, so with `vertical-align: baseline` both baselines line up again.

5 comments

I want to thank both you and the original article author for bringing this issue up, because until this article and this comment I never really understood the use of the vertical-align CSS property. This is the perfect example to illustrate the actual use of this property. Playing around with the author's codepen and seeing how it changed when I added the vertical-align CSS property really unlocked something for me.
I'm glad I could help!
> The flexbox container inside pill2 "hides" the inner text, so pill2 is treated as having no text at all (for layout purposes). This is treated the same as a font having zero height. So the browser aligns a 16px font (or whatever) with a 0px font, and it comes out misaligned.

That’s actually not what happened.

Images have their baseline at the bottom edge, so Pill 1 is simply aligning its text to the bottom of the <img> element which is the first item in Pill 2.

You can confirm this behavior by enlarging the image, you can see Pill 1 shifting vertically to align the text baseline to the new bottom edge of the image.

You can also swap the order of the <img> and <span>Pill 2</span> so that the text goes first. You will see that they will now align despite the fact that the inner text is supposed to be “hidden”.

An easy way to solve this is to give .wrapper a hidden pseudo element with a bit of text so it becomes the first item to be used for alignment.

.wrapper:before { content:"a"; width:0; visibility:hidden; }

You can also just set the image as background to avoid the <img> element altogether.

`vertical-align: middle` would also likely give them better preferred alignment the first time a "pill" thinks it needs a second line of text and sometimes when too many pills exist for available width and the container starts to spill to another row.

I understand why `vertical-align: baseline` is the default, but it seems like such a bad default for all the ways designers like to (over) use Flexbox. (Especially because designers really love the baseline alignment at first but often can't seem to think two and four dimensionally enough when designs meet real world constraints and user data and need to expand/contract/shift to available space.)

But flexbox is much newer than vertical-align.
As I said, I know why it is the default. There's nothing that would have stopped `display: flex` having a different default `vertical-align` given `vertical-align` original use cases mostly didn't overlap anyway, but that's obviously armchair quarterbacking and we have the CSS we have.
That's a much better solution than altering the content.
This is correct. See here for some more examples:

- https://codepen.io/romellem/pen/rNZbPZQ

TL;DR: whenever you are using `display: inline-block`, be aware of `vertical-aign` issues that may crop up.