SVG has all but replaced icon fonts as the de facto standard for showing icons on the web. Earlier this week, I ran into a seemingly strange alignment issue when I tried to use one alongside text.

The problem

I was attempting to create an error message with an icon that’s:

  1. vertically centered, and
  2. has a margin between it and the text

Example:

SVG icon courtesy of https://feathericons.com/

My first instinct in vertically centering situations has been to reach for flexbox and align-items: center, which makes all things vertical-centering-related generally trivial.

HTML:

<div class="warning">
  <div class="warning__icon-wrapper">
    <!-- svg details omitted -->
    <svg />
  </div>
  Please enter a valid email
</div>

CSS:

.warning {
  display: flex;
  align-items: center;
  color: #dc3545;
}

.warning__icon-wrapper {
  margin-right: 0.5rem;
}

However, this results in the following image:

If you take a look in the inspector, you’ll see the following two boxes surrounding the <svg> vs. the <div> surrounding it:

The <div> is taller than its child, even though it has no other content in it! The extra height is causing the SVG to be vertically misaligned with the text.

What’s going on here?

<svg> elements are inline elements by default, which have vertical-align: baseline as their default. The implication, according to the spec, is that the baseline of the SVG element is aligned with the baseline of its parent. In all browsers that I know of, inline elements have an extra bit of height below the baseline of the font to accommodate descenders, the little hanging features of text e.g. below lowercase “g”s and “y”s.

Because the <svg> ‘s parent (the <div> ) is displayed as a block-level element, the parent’s height is going to be increased as a result of the of the descender padding, which is what you can see in the above screenshots, causing the parent to appear taller than it is and misaligning it with the text.

How do we fix this?

1. Set vertical-align: top | bottom | middle on the SVG element

E.g.

svg {
  vertical-align: top;
}

Pretty much anything other than baseline will solve this problem since top and bottom will align the element with one of the edges of the parent, while middle will align the middle of the SVG with the middle of the parent. All of these achieve the outcome we’re looking for.

Note that if you decide to set this globally, like the example above, the vertical-align value you use will have implications if you put the <svg> alongside other text.

2. Set display: block on the SVG element

E.g.

svg {
  display: block;
}

vertical-align does not apply to block elements, which will make the extra padding go away. Note that, as above, setting this globally as in the example above will affect your ability to put <svg> elements next to other text.

3. (In the example above) Wrap the text with margin-left instead of wrapping the SVG with margin-right

HTML

<div class="warning">
  <!-- svg details omitted -->
  <svg>...Inline SVG details omitted</svg>
  <span class="warning__text-wrapper">
    Please enter a valid email
  </span>
</div>

CSS

.warning {
  display: flex;
  align-items: center;
  color: #dc3545;
}

.warning__text-wrapper {
  margin-left: 0.5rem;
}

For the purposes of this component, this is basically the same as option #2. Children of display: flex components are treated similarly as block-level elements.