Your icon is centered against the wrong thing
Dex HalloranIcon on the left, label on the right, align-items: center on the flex row. Ship it, walk away. Then someone drops a longer string into the label, it wraps to two lines, and now the icon is hovering in the dead space between line one and line two like it's trying to remember its lines in a school play. You've seen it. You have shipped it. It is on a live docs page you are responsible for right now.
Ahmad Shadeed's article on ishadeed.com sits on the reason. align-items: center centers the icon against the whole line box of the wrapped label, not against the first line's text. Which is exactly what the property is supposed to do. You asked for center, you got center. But the visual intent was "icon lines up with the first word", and the platform never had a clean way to say that. Until lh showed up.
Center of what, exactly
Here's the pattern he starts from. Nothing exotic.
.list-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
One line of label, this is fine. Two lines and the icon slips to the vertical middle of the whole item, because that is what centering an item inside a flex line does. Flip to align-items: start and the icon jumps to the top of the container, above where the text glyphs actually start. The font is carrying leading — empty space baked in above and below every line. Shadeed says it plainly: "The text element height has an empty space above and below it that comes from the font itself." That gap is why start also looks wrong.
You are stuck between two references. Center of the whole box (wrong when it wraps). Top of the container (wrong because leading pushes the text down). Neither one is "top of the first text line."
The one-line fix, in lh
Here's the rad part. 1lh is exactly one line's height in the current context. Not line-height the property. lh the unit, computed. You can do arithmetic on it. So you keep align-items: start, and shove the icon down by half of the leftover space inside a single line:
.icon {
--size: var(--icon-size);
--offset: calc((1lh - var(--size)) / 2);
transform: translateY(var(--offset));
}
Read that once. 1lh - --size is the vertical space left over inside one line after the icon takes its share. Halve it. Move the icon down by that amount. The icon now sits centered inside the first line's line box, and it stays there when the font size changes, when the icon size changes, when the container width changes and the label wraps to three lines. No pixel constants, no magic number, no re-tuning per breakpoint.
The reason old attempts at this hit a wall (including the version most of us shipped, which was transform: translateY(3px) and a prayer) is that a pixel offset is only right at one font size. Change the type ramp and every list breaks. lh scales with the type ramp because it IS the type ramp. That is the whole story.
Pseudo-elements need a little extra
Second pattern in the piece: the icon isn't in the markup, it's a background-image on ::before. Same offset trick, but a pseudo-element in a flex container has some sharp edges. Shadeed lists them:
.list-item::before {
content: "";
flex-shrink: 0;
aspect-ratio: 1;
width: var(--size);
height: auto;
background-size: contain;
background-repeat: no-repeat;
transform: translateY(var(--offset));
}
flex-shrink: 0 stops the icon from collapsing when the label gets long. aspect-ratio: 1 with height: auto lets the flex line stretch the pseudo without stretching the icon itself. background-size: contain and background-repeat: no-repeat keep the artwork from being clipped or tiled when the box grows with the font. The translateY is the same as before. Same mechanism, more plumbing, because you're aligning a box you never explicitly sized, whose height is at the mercy of the flex line.
The kicker
The bug this fixes is not a spec gap. It is that every list component ever written asked the wrong CSS question. "Center the icon" was never the intent. "Put the icon on the first line's text" was. 1lh finally makes that expressible in one line of math you can drop into a design token. Nothing to ship, nothing to feature-detect, nothing to hedge. Refactor your icon lists. 9/10.
Source: ishadeed.com (ishadeed.com)