Back to Devoured - September 21, 2026
DEVOURED
Better Icon and Label Alignment

Better Icon and Label Alignment

Design I Shadeed
Using the line-height (lh) CSS unit combined with flexbox and calc() solves icon misalignment in lists when text wraps across multiple lines.
What: Ahmad Shadeed demonstrates that setting `align-items: start` and using a `translateY` offset calculated as `calc((1lh - var(--size)) / 2)` ensures icons remain centered relative to the first line of text regardless of font size or icon dimensions.
Why it matters: Developers often rely on fixed margins or padding for icon alignment, which break as soon as typography or container widths change dynamically.
Takeaway: Replace fixed `translateY` values with the `lh` unit in your CSS for icon alignment to ensure vertical centering remains consistent across varying font sizes and line heights.
Deep dive
  • Use display: flex with align-items: start on the container.
  • Define --size as the icon width/height.
  • Apply --offset: calc((1lh - var(--size)) / 2) as a translateY transform on the icon.
  • For pseudo-element icons, ensure aspect-ratio: 1 and flex-shrink: 0 are set to prevent distortion.
  • Use background-size: contain to keep the icon contained within the pseudo-element bounds.
  • This approach handles text wrapping and dynamic font changes without breaking alignment.
Decoder
  • lh unit: A CSS relative length unit that is equal to the computed value of the line-height property of the element on which it is used.
  • Pseudo-element: A keyword added to a CSS selector that lets you style a specific part of the selected element(s), such as ::before or ::after.
Original article

Better Icon and Label Alignment

There is a common design pattern that sometimes gets missed or overlooked. It’s having a list of items, each with an icon. When the text is one line, the icon is centered, but when the text has multiple lines, the icon is still centered but looks odd.

Throughout this article, I will show two solutions that I use based on the HTML:

  • The icon is an HTML element (e.g., <svg> or <img>)
  • The icon is added via a pseudo-element as a background image

The icon in the markup

In this case, we have a container with the icon and the content. Usually, we use flexbox. Like so:

<ul class="list">
  <li class="list-item">
    <svg class="icon"></svg>
    <span class="label">How to align an icon to a label.</span>
  </li>
  <!-- Other items -->
</ul>
.list-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

By default, it looks okay. However, when the list width is smaller and the text wraps, the icon will be centered. This is not the behavior we want.

How can we keep the icon aligned vertically when the text is wrapped? First, I went back to the basics. Instead of using align-items: center, I used start as a value.

.list-item {
  display: flex;
  align-items: start;
  gap: 0.5rem;
}

But this caused an even bigger alignment issue. The highlighted area is the line height. The text element height has an empty space above and below it that comes from the font itself. When align-items: start is applied, the icon will be aligned to the top of the container.

What if we pushed the icon down to have it aligned with the top line?

.list-item {
  display: flex;
  align-items: start;
  gap: 0.5rem;
}

.icon {
  transform: translateY(3px);
}

That works. Let’s push the limits of it and see if it persists! Using a fixed value for the transform won’t work when the icon size, font size, or container width changes.

The solution: using the lh unit

If we can get the line-height value and we have a known icon size, we can make the offset relative to them.

.icon {
  /* --icon-size is defined via JS. */
  --size: var(--icon-size);
  --offset: calc((1lh - var(--size)) / 2);
  transform: translateY(var(--offset));
}

In this solution, if you change any of the three values, the alignment will just work. In some cases, the offset might be negative.

Icon as a pseudo-element

Sometimes, we don’t need to add the icon as an HTML element, so we use a pseudo-element for that.

Here is the basic CSS:

.list-item {
  display: flex;
  gap: 0.5rem;
}

.list-item::before {
  content: "";
  --size: var(--icon-size);
  width: var(--size);
  height: var(--size);
  background-image: url("data:image/svg+xml,...");
  background-repeat: no-repeat;
}

At first glance, it looks fine, right? Did you notice that the icon is cut off? This is because we need to limit the background size.

.list-item::before {
  content: "";
  --size: var(--icon-size);
  width: var(--size);
  height: var(--size);
  background-image: url("data:image/svg+xml,...");
  background-size: contain;
  background-repeat: no-repeat;
}

If you increase the font size, the icon width shrinks because a flex item will shrink. We need to override that.

.list-item::before {
  content: "";
  --size: var(--icon-size);
  flex-shrink: 0;
  width: var(--size);
  height: var(--size);
  background-image: url("data:image/svg+xml,...");
  background-size: contain;
  background-repeat: no-repeat;
}

The solution

Currently, the list item is a flexbox container, and by default, the items stretch (vertically). I set the height property to auto.

.list-item::before {
  content: "";
  --size: var(--icon-size);
  flex-shrink: 0;
  width: var(--size);
  height: auto;
  background-image: url("data:image/svg+xml,...");
  background-size: contain;
  background-repeat: no-repeat;
}

To avoid icon distortion when the font size changes, we need to force the aspect ratio to a square.

.list-item::before {
  content: "";
  --size: var(--icon-size);
  flex-shrink: 0;
  aspect-ratio: 1;
  width: var(--size);
  height: auto;
  background-image: url("data:image/svg+xml,...");
  background-size: contain;
  background-repeat: no-repeat;
}

With that, we will use the same technique as before and offset the pseudo-element by a few pixels.

.list-item::before {
  content: "";
  --size: var(--icon-size);
  --offset: calc((1lh - var(--size)) / 2);
  flex-shrink: 0;
  aspect-ratio: 1;
  width: var(--size);
  height: auto;
  background-image: url("data:image/svg+xml,...");
  background-size: contain;
  background-repeat: no-repeat;
  transform: translateY(var(--offset));
}

The icon is now centered. It’s interesting to see what’s been used here: flexbox, aspect-ratio, the lh unit, calc(), and height: auto.

Outro

And that’s it. I solved a problem while working on a project and wanted to share it. Hope you’ve learned something new.

Thanks for reading.

Explore all 67 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi