Vertical-Align: All You Need To Know (CSS)

We need to talk about this!

Yep, let’s talk about the CSS property vertical-align. It’s intended use is to align text and elements next to each other. Like centering an icon next to a bit of text.

But, it can be a real scumbag sometimes with all its seemingly mysterious rules at work. For example, it might happen, that the element you changed vertical-align for doesn’t change its alignment at all, but other elements next to it do! What a joy!

So, to minimize future pain, I waded through W3C’s CSS specifications to clarify the behavior of vertical-align once and for all.

Let’s tackle the rules of the game!

What you will learn in this article:

  1. Vertical-Align Acts On Inline-level Elements In a Line Box.
  2. Inline-level Elements and Line Boxes Have Baselines, Tops and Bottoms.
  3. Vertical-Align Aligns Baselines, Tops and Bottoms.
  4. Example: How to center an icon next to a bit of text.
  5. Example: How the baseline might move.
  6. Example: How to vertically center elements without a gap at the bottom.
  7. Example: How to remove the gap between two aligned elements.

Vertical-Align Acts On Inline-Level Elements In a Line Box

vertical-align is used to align inline-level elements. These are elements, whose display property evaluates to

  • inline,
  • inline-block or
  • inline-table (not considered in this article).

Inline elements are basically tags wrapping text.

Inline-block elements are what their name suggests: block elements living inline. They can have a width and height (possibly defined by its own content) as well as padding, a border and margin.

Inline-level elements are laid out next to each other in lines. Once there are more elements than fit into the current line, a new line is created beneath it. All these lines have a so-called line box, which encloses all the content of its line. Differently sized content means line boxes of different height. In the following illustration the top and bottom of line boxes are indicated by red lines.

A tall in a line of text.
A short in a line of text.
This can happen.

The line boxes trace out the field we are playing on. Inside these line boxes the property vertical-align is responsible for aligning the individual elements. So, in respect to what are elements aligned?

About Baselines, Tops and Bottoms

The most important reference point to align vertically is the baseline of the involved elements. In some cases the top and bottom edge of the elements’ enclosing box becomes important, too. Let’s have a look where the baseline and outer edges live for each involved type of element:

Inline Element

aAÄ qQ
aAÄ qQ
aAÄ qQ

Here you see three lines of text next to each other. The top and bottom edge of the line height is indicated by red lines, the height of the font by green lines and the baseline by a blue line. On the left, the text has a line height set to the same height as the font-size. The green and red line collapsed to one line on each side. In the middle, the line height is twice as large as the font-size. On the right, the line height is half as large as the font-size.

The inline element’s outer edges align themselves with the top and bottom edge of its line height. It does not matter, if the line height is smaller than the height of the font. So, the outer edges are the red lines in the figure above.

The inline element’s baseline is the line, the characters are sitting on. This is the blue line in the figure. Roughly speaking, the baseline is somewhere below the middle of the font’s height. Have look at the W3C Specs for a detailed definition.

Inline-Block Element

c
c

From left to right you see: an inline-block element with in-flow content (a “c”), an inline-block element with in-flow content and overflow: hidden and an inline-block element with no in-flow content (but the content area has a height). The boundaries of the margin is indicated by red lines, the border is yellow, the padding green and the content area blue. The baseline of each inline-block element is shown as a blue line.

The Inline-block element’s outer edges are the top and bottom edge of its margin-box. These are the red lines in the figure.

The Inline-block element’s baseline depends on whether the element has in-flow content:

  • In case of in-flow content the baseline of the inline-block element is the baseline of the last content element in normal flow (example on the left). For this last element its baseline is found according to its own rules.
  • In case of in-flow content but an overflow property evaluating to something other than visible, the baseline is the bottom edge of the margin-box (example in the middle). So, it is the same as the inline-block element’s bottom edge.
  • In case of no in-flow content the baseline is, again, the bottom edge of the margin-box (example on the right).

Line Box

x This can happen.

You’ve already seen this setting above. This time I drew in the top and bottom of the line box’s text box (green, more on this below) and the baseline (blue), too. I also highlighted the area of the text elements by giving them a grey background.

The line box has a top edge aligned to the top edge of the top-most element of this line and a bottom edge aligned to the bottom edge of the bottom-most element of the line. This is the box indicated by the red lines in the figure above.

The line box’s baseline is variable:

CSS 2.1 does not define the position of the line box's baseline.

This is probably the most confusing part, when working with vertical-align. It means, the baseline is placed where ever it needs to be to fulfil all other conditions like vertical-align and minimizing the line box’s height. It is a free parameter in the equation.

Since the line box’s baseline is invisible, it may not immediately be obvious where it is. But, you can make it visible very easily. Just add a character at the beginning of the line in questions, like I added an “x” in the figure. If this character is not aligned in any way, it will sit on the baseline by default.

Around its baseline the line box has what we might call its text box. The text box can simply be thought of as an inline element inside the line box without any alignment. Its height is equal to the font-size of its parent element. Therefore, the text box only just encloses the unformatted text of the line box. The box is indicated by the green lines in the figures above. Since this text box is tied to the baseline, it moves when the baseline moves. (Side note: this text box is called strut in the W3C Specs)

Phew, this was the hard part. Now, we know everything to put things into perspective. Let’s quickly sum up the most important facts:

  • There is an area called line box. This is the area in which vertical alignment takes place. It has a baseline, a text box and a top and bottom edge.
  • There are inline-level elements. These are the objects that are aligned. They have a baseline and a top and bottom edge.

Vertical-Align Aligns Baselines, Tops and Bottoms

By using vertical-align the reference points mentioned in the list above are set into a certain relationship.

Aligning Relative To the Line Box’s Baseline

x baseline sub super -50% +10px
  • baseline: The element’s baseline sits exactly on top of the line box’s baseline.
  • sub: The element’s baseline is shifted below the line box’s baseline.
  • super: The element’s baseline is shifted above the line box’s baseline.
  • <percentage>: The element’s baseline is shifted with respect to the line box’s baseline by a percentage relative to the line-height.
  • <length>: The element’s baseline is shifted with respect to the line box’s baseline by an absolute length.

The Special case vertical-align: middle deserves its own figure:

x middle
  • middle: The midpoint between the element’s top and bottom edge is aligned to the line box’s baseline plus half of the x-height.

Aligning Relative To the Line Box’s Text Box

x text-top text-bottom

One could also list these two cases under aligning relative to the line box’s baseline, since the position of the text box is determined by the baseline.

  • text-top: The element’s top edge is aligned to the line box’s text box top edge.
  • text-bottom: The element’s bottom edge is aligned to the line box’s text box bottom edge.

Aligning Relative To the Line Box’s Outer Edges

x top bottom
  • top: The element’s top edge is aligned to the line box’s top edge.
  • bottom: The element’s bottom edge is aligned to the line box’s bottom edge.

The formal definition is found in, of course, the W3C Specs.

Why Vertical-Align Behaves The Way It Behaves

We can now take a closer look at vertical alignment in certain situations. Especially, we will deal with situations where things might have gone wrong.

Centering an Icon

One question bugging me was the following: I have an icon I want to center next to a line of text. Just giving the icon a vertical-align: middle didn’t seem to center it in a satisfying way. Have a look at this example:

Centered?
Centered!
<!-- left mark-up -->
<span class="icon middle"></span>
Centered?

<!-- right mark-up -->
<span class="icon middle"></span>
<span class="middle">Centered!</span>

<style type="text/css">
  .icon   { display: inline-block;
            /* size, color, etc. */ }

  .middle { vertical-align: middle; }
</style>

Here is the example again, but I drew in some auxiliary lines you already know from above:

x Centered?
Centered!

This sheds some light on our matter. Because the text on the left isn’t aligned at all, it sits on the baseline. The thing is, by aligning the box with vertical-align: middle we are aligning it to the middle of the lower case letters without ascenders (half of the x-height). So, characters with ascenders stand out at the top.

On the right, we take the whole area of the font and align its midpoint vertically, too. The text’s baseline shifts slightly below the line box’s baseline to achieve this. The Result is a nicely centered text next to an icon.

Movement Of the Line Box’s Baseline

This is a common pitfall when working with vertical-align: The position of the line box’s baseline is affected by all elements in that line. Let’s assume, an element is aligned in such a way, that the baseline of the line box has to move. Since most vertical alignment (except top and bottom) is done relative to this baseline, this results in an adjusted position of all other elements in that line, too.

Some Examples:

  • If there is a tall element in the line spanning across the complete height, vertical-align has no effect on it. There is no space above its top and below its bottom, that would let it move. To fulfil its alignment relative to the line box's baseline, the line box's baseline has to move. The short box has a vertical-align: baseline. On the left, the tall box is aligned text-bottom. On the right, it is aligned text-top. You can see the baseline jumping up taking the short box with it.

    <!-- left mark-up -->
    <span class="tall-box text-bottom"></span>
    <span class="short-box"></span>
    
    <!-- right mark-up -->
    <span class="tall-box text-top"></span>
    <span class="short-box"></span>
    
    <style type="text/css">
      .tall-box,
      .short-box   { display: inline-block;
                    /* size, color, etc. */ }
    
      .text-bottom { vertical-align: text-bottom; }
      .text-top    { vertical-align: text-top; }
    </style>

    The same behaviour shows up when aligning a tall element with other values for vertical-align.

  • Even setting vertical-align to bottom (left) and top (right) moves the baseline. This is strange, since the baseline shouldn't be involved at all.

    <!-- left mark-up -->
    <span class="tall-box bottom"></span>
    <span class="short-box"></span>
    
    <!-- right mark-up -->
    <span class="tall-box top"></span>
    <span class="short-box"></span>
    
    <style type="text/css">
      .tall-box,
      .short-box { display: inline-block;
                   /* size, color, etc. */ }
    
      .bottom    { vertical-align: bottom; }
      .top       { vertical-align: top; }
    </style>
  • Placing two larger elements in a line and vertically aligning them moves the baseline where it fulfils both alignments. Then the height of the line box is adjusted (left). Adding a third element, that does not go beyond the line box's edges because of its alignment, affects neither the line box's height nor the baseline's position (middle). If it *does* go beyond the line box's edges, the line box's height and baseline are adjusted, again. In this case, our first two boxes are pushed down (right).

    <!-- left mark-up -->
    <span class="tall-box text-bottom"></span>
    <span class="tall-box text-top"></span>
    
    <!-- middle mark-up -->
    <span class="tall-box text-bottom"></span>
    <span class="tall-box text-top"></span>
    <span class="tall-box middle"></span>
    
    <!-- right mark-up -->
    <span class="tall-box text-bottom"></span>
    <span class="tall-box text-top"></span>
    <span class="tall-box text-100up"></span>
    
    <style type="text/css">
      .tall-box    { display: inline-block;
                     /* size, color, etc. */ }
    
      .middle      { vertical-align: middle; }
      .text-top    { vertical-align: text-top; }
      .text-bottom { vertical-align: text-bottom; }
      .text-100up  { vertical-align: 100%; }
    </style>

There Might Be a Little Gap Below Inline-Level Elements

Have a look at this setting. It’s common if you try to vertical-align li elements of a list.

<ul>
  <li class="box"></li>
  <li class="box"></li>
  <li class="box"></li>
</ul>

<style type="text/css">
  .box { display: inline-block;
         /* size, color, etc. */ }
</style>

As you can see, the list items sit on the baseline. Below the baseline is some space to shelter the descenders of a text. This is causing the gap. The Solution? Just move the baseline out of the way, for example by aligning the list items with vertical-align: middle:

<ul>
  <li class="box middle"></li>
  <li class="box middle"></li>
  <li class="box middle"></li>
</ul>

<style type="text/css">
  .box    { display: inline-block;
            /* size, color, etc. */ }

  .middle { vertical-align: middle; }
</style>

This scenario does not occur for inline-blocks having text content, since content already moves the baseline up.

A Gap Between Inline-Level Elements Is Breaking the Layout

This is mainly a problem of inline-level elements themselves. But since they are a requirement of vertical-align, it is good to know about this.

You can see this gap in the former example between the list items. The gap comes from the white-space between inline-elements present in your mark-up. All white-space between inline-elements is collapsed into one space. This space gets in the way, if we want to place two inline elements next to each other and giving them width: 50%, for example. There is not enough space for two 50%-elements and a space. So the line breaks into two lines destroying the layout (left). To remove the gap, we need to remove the white-space, for example with html comments (right).

50% wide
50% wide... and in next line
50% wide
50% wide
<!-- left mark-up -->
<div class="half">50% wide</div>
<div class="half">50% wide... and in next line</div>

<!-- right mark-up -->
   <div class="half">50% wide</div><!--
--><div class="half">50% wide</div>

<style type="text/css">
  .half { display: inline-block;
          width: 50%; }
</style>

Vertical-Align Demystified

Yea, that’s it. It is not very complicated once you know the rules. If vertical-align does not behave, just ask these questions:

  • Where is the baseline and top and bottom edge of the line box?
  • Where is the baseline and top and bottom edge of the inline-level elements?

This will corner the solution to the problem.

Here is a more complex example how to vertically center an element in a <div> with vertical-align: middle.