Grow your CSS skills. Land your dream job.

Vertically Center Multi-Lined Text

Published by Chris Coyier

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>

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).


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

<!--[if lt IE 8]>
.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");


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


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]>
.bubble div { position: absolute; top:50%;}
.bubble div p {position: relative; top: -50%}


Another method from Andy Howard.


  1. Snookerman

    I thought you didn’t like CSS tables?

    • Ha! Well, yes, that’s true, in general I do not. But I don’t like them because of how they don’t offer anything that table markup doesn’t already do. Wow I can make this div behave like a table cell! Why not just use a table cell?

      In this case, we save markup by using it AND exploit it’s unique property of allowing vertical-align. So it’s not deception, it’s just a nice trick.

    • Is it to my understanding that Css can basically strip html down to size?

      so lets say for example i have italic and bold text

      could i not use css to completely stip the style of these 2 tags and then make them into what ever i want? so a tag could now make something italic and a tag is now a paragraph tag?

      if so then theoretically you could turn any tag into a table tag :)

    • Well done Chris. I like how the above mentioned “I thought you didn’t like display:table?.” And irregardless of whether you like it or not you still dove into it to find how it actually can be useful. Definitely the signs of a css fanatic :) Well done.

    • So it’s not deception, it’s just a nice trick.

      Illusions, Michael! Tricks are what a whore does for money… or candy!” – GOB

      Couldn’t resist. ;-)

  2. As always very nice. Only hope the migration from IE7 to IE8 is much smoother and fast. Damn legacy software.

  3. Looks pretty good, if the Dean Edward’s script supports it, it will be ace!

  4. If you don’t mind different height boxes, equal padding on the top and bottom works, too.

  5. good tip but ie6 sucks….

  6. I haven’t had any luck dealing with with Dean Edwards script. Very frustrating!

    Nice trick though.

  7. Andy S

    I appreciate the tip, but with something like 40% still in IE6 and IE7 there are just too many users that would not see it correctly for me to move to it.

  8. This is one of my favorite tricks! Great write-up :)

  9. Hmmm. What if you use a bit of jQuery? You have 2 boxes, calculate the size of each one, than make 2 subtractions and you know where to place the paragraphs inside the bubble.

    • Dave B

      Bogdan, that would be using javascript for presentation. Whenever possible:

      xhtml should mark up all content, semantically
      css should handle all presentation of the content
      javascript should only assign behavior

  10. Gondilan

    Did you mean:
    What about IE <= 7 ?!

  11. Jonas

    This is a start, but only half of the solution – it’s useless in real world if it doesn’t work on the evil browsers IE6 and IE7…

    With conditional comments and the position trick (a relative positioned div with top/left -50% inside an absolute positioned div with top/left 50%) it works fine cross-browser.

  12. With IE 6 and 7 you can do this:

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

    Just put it in <head> element below this line:

    <link rel="stylesheet" type="text/css" href="css/style.css" />

    • You don’t need a “display: inline-block;” – I’ve been trying to make it work with span tag instead of p

    • Hooray! Boris gets three big fat gold stars for today!

      It works very well, and I updated the live example to include this.

      REMINDER: If you are going to copy and paste the code above, WordPress likes to make slanted quotes instead of straight ones, so fix the ones around “px” and “0”.

    • bkuzmic

      Chris, I think I’ve found an ultimate hack :-). How many stars do i get today?! :-)

      Check this out:

      .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

      This will force IE to evaluate expression only once and thus avoid memory and performance issues.

      Source of inspiration was this great post:
      One-time execution of IE CSS expressions

  13. Sweet tip, will have to put this one in my bag of tricks! :D


    Anthony Proulx

  14. DN

    Chris! Note that using IE expressions can cause major memory drain (it’s always running).

    • bkuzmic

      DN is right, expressions can cause memory leaks. In this case that is not happening, but there is a one performance issue: expression is running all the time (when you move your mouse, when you scroll, etc..). So to avoid this, you can use this small Javascript code for IE 6 and 7 (put it at the end of HTML body):

      <!--[if lt IE 8]>
      <script type="text/javascript">
      // make all bubbles vertically centered
      var ps = document.getElementsByTagName("p");
      for (var i=0;i<ps.length;i++) {
      if (ps[i].parentNode.className=='bubble') {
      ps[i].style.marginTop = ps[i].offsetHeight < ps[i].parentNode.offsetHeight ?
      parseInt((ps[i].parentNode.offsetHeight - ps[i].offsetHeight) / 2) + "px" : "0";
      ps = null;

      Also, remove the margin-top style from .bubble p class.

    • It’s starting to be a lot of code for a simple trick. Nice work though, but I’m personnaly tending to ignore these little display bugs of IE.. and push people to update their browsers :)

  15. Mariano

    great tip! thanks!

  16. I am loving that speech bubble.
    Do you mind if I use that graphic on my site?

  17. Yea, that is definitely a hack. I would prefer to use just a simple table. It’s more HTML, but less confusing code.

    • DN

      No, it’s not a hack. It’s using CSS for presentation…which is what it’s meant to be used for. Using a table in this instance would be a hack. And the code’s not confusing at all, unless you just don’t know CSS much yet.

    • Tomas

      I use table when its needed and works in all browsers. Why these people that walk over dead bodies to avoid tables and in the end make a more complex solution?

    • It’s not outright avoidance, just using it properly. Since a speech bubble consisting of a single paragraph can’t be considered tabular data, it would be inappropriate to use it in this context. On the other hand, tables are great for multi-column, multi-row data storage.

    • Vsync

      You are so very right.
      it is fairly knows that advanced CSS should not be used until all major browser supports it.

      this article makes nothing new to no-one , and there is no TRICK. here,
      its just CSS everyone ALWAYS knew was there, but wouldn’t use it, cause
      of IE’s lack of support. so.. i’m still not gonna use it. for now :)

  18. I personally don’t find it necessary for everything to look exactly the same in all browsers, and this is a good example of a place where a minor difference makes a modern browser look good and a legacy browser still function properly.

    To meet halfway, you could use display:table-cell and the vertical-align in conjuction with some vertical padding — that way even older browsers won’t look crowded and sad.

  19. grifone72

    Very nice tip! Thank you!

  20. Evan

    I was shocked the first time I discovered that there was no easy way in css to vertically center text like this. Horizontal centering is no problem but why did vertical have to be such a big deal?

    It should be as simple as vertical-align:middle or margin:auto 0

    The only options to get it to work properly in ie6 and ie7 are either to use a table or a weird mess of css.

  21. It’s great that we have a way to make it working on IE 6 and 7, but I think we cannot call it as “css friendly”, so we have to avoid it while we can :(

    • bkuzmic

      That’s why it’s called hack – ugly but necessary. Anyway, last solution that I’ve posted is IE css friendly and it makes developer’s life easier.

  22. Krzysztof

    good tip but it doesn’t look good in CHROME ;-)

    • bkuzmic

      Which version of Chrome are you using?- I’ve just tried with and it works ok. Maybe you are referring to text in first bubble (font size should be changed).

  23. Mr Lol

    What if you want to use images instead of text?

  24. bkuzmic

    I’ve created a simple centered Login page to show how this technique can also be used.

    Check it out: CSS Centered Login Page

  25. Brilliant solution to an oft problem. Block items of variable size can present quirky problems when centering both horizontally and vertically. CSS tables will provide a (not so ideal) workaround for these issues.

  26. Well, the regular way of vertically centring in css has been documented by Dušan Janovský. It needs an extra div for its IE solution, but it doesn’t require any expressions, only regular css.

    I’ve updated the example to use conditional comments instead of hacks. The HTML becomes:

    <div class="area" id="two">
    <div class="bubble">
    <p>Doing this may not be as easy or obvious as we would hope.</p>

    The stylesheet becomes:

    #page-wrap { width: 600px; margin: 0 auto;
    .area { width: 300px; height: 300px; background: url(abe-bg.png) no-repeat; position: relative; float: left; }
    .bubble { position: absolute; left: 93px; top: 21px; width: 135px; height: 84px; display: table; }
    .bubble div { display: table-cell; vertical-align: middle; text-align: center;}

    The IE specific css becomes:

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

  27. This is really cool and James John Malcolm’s technique is a great solution to handle IE. By the way.. wouldn’t that work cross-browser? Since the extra markup anyways will need to be there, might as well only use James CSS and skip the table technique?

    Chris, I like the speech bubble examples you have created :)

  28. Again, I wrote a similar topic on 4/7/09. In case you like Chinese version, here is a link:

  29. Dave

    It’s about time every developer put a huge horrible warning at the top of each page that showed on <=IE7 users’ machines. Then hopefully soon we can rid the world of those Microsoft internet crimes and save ourselves LOTS of time!

  30. Saleh

    I found out if you are going to use offsetHeight or offsetWidth in IE7 or IE8, make sure you set the HTML tag with some dimension … e.g or you set the tag in css;

    html { height: 100%; width:100%; }

    Hope this small info helps someone.

  31. PaleSaint

    So funny that we’re using CSS (IE8!) to mimic the behaviour of table cells when we were told that tables were the devil’s plaything. Amazing how many hoops and conditional statements you have to jump through to get the same result a v4 browser would’ve easily coped with using tables (and it was happily cross-browser). Have we all gone mad?

  32. Wow…just wish IE would figure out that they cause more issues than solutions and just allow Firefox to take them over….

  33. Awesome blog! Subscribed on rss. Regular will read it. Good job.

  34. Excellent blog! Very interesting posts. I will allways read it. Also subsribed on rss.

  35. Permalink to comment#

    Awesome post. Seems to me the CSS3 folks need to create a multi_center option. I understand there’s more rendering math needed for such an option, so it’s reasonable to distinguish it from simply ‘center’ for sake of performance. That said, this is my 3rd CSS3 project, and you’d think this wouldn’t be such a difficult task. Easily one of the top ‘harder than it looks’ styles.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".