Vertically Center Multi-Lined Text

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

If you only have a single word or a single line of text, there is a clever way to vertically center it in a block with CSS. You set the line-height of that text to be equal to the height of the box. Works great, but is a major fail if that text needs to wrap.

A “speech bubble” is a classic example of somewhere we might want text to be centered both horizontally and vertically and be adaptable to multiple lines. There is a little, fairly simple CSS trick for this, using CSS tables. Here is the outcome:

View DemoDownload Files

The HTML is nothing fancy. The “area” is just the region we are dealing with, where we can set position: relative; so that we can absolutely position the text are inside the bubble.

<div class="area">
      <div class="bubble">
          <p>To look best, text should really be centered inside this bubble both vertically and horizontally.</p>
      </div>
</div>

The “bubble” we’ll set to display: table;, which really doesn’t do much by itself, but then we can set the <p> element inside to be a table-cell, which allows us to use the vertical-align property on it.

.area { 
  width: 300px; 
  height: 300px; 
  background: url(../images/abe-bg.png) no-repeat; 
  position: relative;
}

.bubble { 
  position: absolute; 
  left: 93px; 
  top: 21px; 
  width: 135px; 
  height: 84px; 
  display: table; 
}

.bubble p {
  display: table-cell; 
  vertical-align: middle; 
  text-align: center; 
}

Does the trick beautifully I think. This current version of CSS-Tricks has a little twitter speech bubble down by the footer I used this for.

What about IE <= 7 ?!

IE 8 is supporting CSS tables, but IE 7 and below do not. Too bad, so sad. Instead you get this:

… could be worse. I was hoping the Dean Edwards ie8.js would solve this but no dice (yet).

UPDATE 1

Boris Kuzmic comments below a perfect solution to make IE 6 and 7 work perfectly:

<!--[if lt IE 8]>
<style>
.bubble { position: absolute; left: 93px; top: 21px; width: 135px; height: 84px; text-align: center;}
.bubble p { position: relative; font-size: 11px;
margin-top: expression(this.offsetHeight < this.parentNode.offsetHeight ? parseInt((this.parentNode.offsetHeight - this.offsetHeight) / 2) + "px" : "0");
}
</style>
<![endif]-->

UPDATE 2

Boris again with a way to make the IE expression not leak memory (this way it only needs to be evaluated once, not continuously run).

.bubble p { position: relative; font-size: 11px;
margin-top: inherit;
*clear: expression(
style.marginTop = "" + (offsetHeight < parentNode.offsetHeight ? parseInt((parentNode.offsetHeight - offsetHeight) / 2) + "px" : "0"),
style.clear = "none", 0
);
}

UPDATE 3

James John Malcolm chimes in with another technique for IE. It’s slightly less semantic (requires and extra div), but it needs no expression.

First wrap the inside <p> in a new <div> and then:

<!--[if lt IE 8]>
<style>
.bubble div { position: absolute; top:50%;}
.bubble div p {position: relative; top: -50%}
</style>
<![endif]–>

UPDATE 4

Another method from Andy Howard.