172

What is the best way to vertically center the content of a div when the height of the content is variable. In my particular case, the height of the container div is fixed, but it would be great if there were a solution that would work in cases where the container has a variable height as well. Also, I would love a solution with no, or very little use of CSS hacks and/or non-semantic markup.

alt text

sandyJoshi
  • 733
  • 9
  • 20
jessegavin
  • 74,067
  • 28
  • 136
  • 164

9 Answers9

181

Just add

position: relative;
top: 50%;
transform: translateY(-50%);

to the inner div.

What it does is moving the inner div's top border to the half height of the outer div (top: 50%;) and then the inner div up by half its height (transform: translateY(-50%)). This will work with position: absolute or relative.

Keep in mind that transform and translate have vendor prefixes which are not included for simplicity.

Codepen: http://codepen.io/anon/pen/ZYprdb

BlackCetha
  • 2,051
  • 1
  • 15
  • 15
  • 6
    Just wondered why this works on Chrome and Firefox, but doesn't for Safari. Then I noticed that most `transform` related stuff is `-webkit-` prefixed and now it works. So just as a reminder: Don't forget to add the `-webkit-` prefix too. – miho Mar 06 '15 at 19:20
  • 5
    Use a tool like https://github.com/postcss/autoprefixer to handle the prefixes for you. – Jamie Chong Mar 25 '15 at 19:45
  • 6
    This answer should be at the top. – Jose Faeti Aug 21 '15 at 11:51
  • 3
    This should have much more upvotes! By the way, it also works with `position: absolute`, doesn't it? – caw Feb 03 '16 at 05:40
  • I like this method but I don't think this works with multiple "inner" divs of different height, seems to use the tallest div as the reference, shorter ones are not aligned properly. – Daniel M. Mar 25 '16 at 13:30
  • 1
    Using 100% `width` and `height` for `html` and `body`, it also works very well in Chrome without have to set a outer element. Just using the body as outer element with `fixed` position! Great! – VaioWay Apr 05 '16 at 06:00
  • 4
    This method has an unfortunate side effect in Chrome - if your wrapper has an odd number of pixels then `translate` will render everything blurrily as it tries to handle .5px height on everything. – Keith Jun 27 '16 at 11:21
  • agree with @JoseFaeti. This should be on the top. – Janith Chinthana Sep 21 '16 at 13:52
  • A problem with this is that it prevents the div from dynamically growing larger than 50% of the parent/viewport: https://jsfiddle.net/9h838uaw/ – user2145184 Dec 08 '16 at 17:30
  • @BlackCetha This is what it looks like in Chrome 55.0.2883.75: http://i.imgur.com/fNp97jX.png - And this is closer to what I would like to happen (use up to 100% width): http://i.imgur.com/ynA6uJA.png – user2145184 Dec 08 '16 at 17:43
  • @user2145184 In the first screenshot it looks like the scrollbar is from jsfiddle and not the CSS. Could you explain your problem a little more or post screenshots showing the entire page? – BlackCetha Dec 08 '16 at 17:45
  • @BlackCetha I just realized this was a solution for vertical centering. My issue is with horizontal, sorry about that. – user2145184 Dec 08 '16 at 17:47
  • 1
    Literally shouted 'BOMB MOTHER F**KER' when I implemented this – user1380540 Dec 21 '17 at 20:51
  • If the content inside the div is not another div, for example an svg, it will also need to have display:block; on it – Travis J Apr 09 '19 at 03:48
  • Problem appears when content is loaded dynamicly and sometimes inner is bigger than outer – dbx Mar 06 '20 at 07:51
161

This seems to be the best solution I’ve found to this problem, as long as your browser supports the ::before pseudo element: CSS-Tricks: Centering in the Unknown.

It doesn’t require any extra markup and seems to work extremely well. I couldn’t use the display: table method because table elements don’t obey the max-height property.

.block {
  height: 300px;
  text-align: center;
  background: #c0c0c0;
  border: #a0a0a0 solid 1px;
  margin: 20px;
}

.block::before {
  content: '';
  display: inline-block;
  height: 100%; 
  vertical-align: middle;
  margin-right: -0.25em; /* Adjusts for spacing */

  /* For visualization 
  background: #808080; width: 5px;
  */
}

.centered {
  display: inline-block;
  vertical-align: middle;
  width: 300px;
  padding: 10px 15px;
  border: #a0a0a0 solid 1px;
  background: #f5f5f5;
}
<div class="block">
    <div class="centered">
        <h1>Some text</h1>
        <p>But he stole up to us again, and suddenly clapping his hand on my
           shoulder, said&mdash;"Did ye see anything looking like men going
           towards that ship a while ago?"</p>
    </div>
</div>
John
  • 1
  • 13
  • 98
  • 177
Fadi
  • 1,770
  • 4
  • 13
  • 10
  • 3
    Now -that- is a -BRILLIANT- solution. Thanks so much for posting this! – Troy Alford Oct 25 '12 at 19:42
  • As with the other answer, this one would be much better if it described the approach and included code from the link. – KatieK Jul 23 '13 at 19:30
  • 3
    +1, This solution is really quite ingenious! Btw you could add `.block { white-space: nowrap; font-size: 0; line-height: 0; }` and leave the negative right margin on the pseudo element, you just have to reset fs and lh on .centered... like this you ensure that the element will be correctly positioned even if it's wider than the viewport (and don't have to use the imo a bit messy negative margin). – Simon Aug 16 '13 at 10:26
  • @jessegavin - in your opinion ... how does the following stack up against the approach you've outlined: http://stackoverflow.com/questions/396145/whats-the-best-way-of-centering-a-div-vertically-with-css#6182661 – pulkitsinghal Aug 20 '13 at 20:37
  • This solution doesn't support IE6. And is therefore MUCH simpler. So it's a bit of an apples to oranges comparison. – jessegavin Aug 20 '13 at 20:52
  • Works like a charm. Thanks for posting this solution! – MikeTedeschi Sep 05 '13 at 20:15
  • In my case the `.block` is `position:absolute` which causes problems with **Firefox** and **IE8**. However, it seems to be fixed by removing the `margin-right` from `.block:before` and set `.block { letter-spacing: -0.25em }` `.center { letter-spacing: normal }`. Thank you @Fadi! – gyo Sep 26 '13 at 19:09
  • It is fine on IE12, Chrome and Firefox. Hoever it's not displayed correctly on Android Standard Browser... – Emaborsa Jan 17 '14 at 22:59
  • 1
    Also, this breaks down when the outer container .block has min-height instead of height. – Lincoln B Jul 30 '14 at 16:04
  • You beauty. I've been looking for this solution for years – Adam Moore Sep 24 '14 at 14:49
  • This even works with variable container height, this is brilliant! Thank you! – Pluc Aug 18 '15 at 15:05
16

This is something I have needed to do many times and a consistent solution still requires you add a little non-semantic markup and some browser specific hacks. When we get browser support for css 3 you'll get your vertical centering without sinning.

For a better explanation of the technique you can look the article I adapted it from, but basically it involves adding an extra element and applying different styles in IE and browsers that support position:table\table-cell on non-table elements.

<div class="valign-outer">
    <div class="valign-middle">
        <div class="valign-inner">
            Excuse me. What did you sleep in your clothes again last night. Really. You're gonna be in the car with her. Hey, not too early I sleep in on Saturday. Oh, McFly, your shoe's untied. Don't be so gullible, McFly. You got the place fixed up nice, McFly. I have you're car towed all the way to your house and all you've got for me is light beer. What are you looking at, butthead. Say hi to your mom for me.
        </div>
    </div>
</div>

<style>
    /* Non-structural styling */
    .valign-outer { height: 400px; border: 1px solid red; }
    .valign-inner { border: 1px solid blue; }
</style>

<!--[if lte IE 7]>
<style>
    /* For IE7 and earlier */
    .valign-outer { position: relative; overflow: hidden; }
    .valign-middle { position: absolute; top: 50%; }
    .valign-inner { position: relative; top: -50% }
</style>
<![endif]-->
<!--[if gt IE 7]> -->
<style>
    /* For other browsers */
    .valign-outer { position: static; display: table; overflow: hidden; }
    .valign-middle { position: static; display: table-cell; vertical-align: middle; width: 100%; }
</style>

There are many ways (hacks) to apply styles in specific sets of browsers. I used conditional comments but look at the article linked above to see two other techniques.

Note: There are simple ways to get vertical centering if you know some heights in advance, if you are trying to center a single line of text, or in several other cases. If you have more details then throw them in because there may be a method that doesn't require browser hacks or non-semantic markup.

Update: We are beginning to get better browser support for CSS3, bringing both flex-box and transforms as alternative methods for getting vertical centering (among other effects). See this other question for more information about modern methods, but keep in mind that browser support is still sketchy for CSS3.

Community
  • 1
  • 1
Prestaul
  • 83,552
  • 10
  • 84
  • 84
  • Yeah, I found that article today and haven't been able to make it work for my particular situation. Perhaps I need to look into that further. – jessegavin Sep 12 '08 at 15:39
  • Perhaps you can post the code you are using that doesn't work? – Chris Marasti-Georg Sep 12 '08 at 15:40
  • Is this still the best solution for this problem? – ripper234 Jan 03 '12 at 08:59
  • That article doesn't seem to work on Chrome 23, Firefox 16 and IE 9. [This answer](http://stackoverflow.com/a/6182661/376366) seems to be a better solution. – Fernando Correia Nov 18 '12 at 21:24
  • No, that article still DOES work in the latest Chrome and Firefox as of February 24th, 2013 – OMA Feb 24 '13 at 13:16
  • @KatieK, I don't like to copy code strait out of other people's writing and unless I've added value somehow I usually don't. This post was definitely ready for an update, however, so I've added some code. Thanks for bringing it to my attention again! – Prestaul Jul 24 '13 at 16:11
10

Using the child selector, I've taken Fadi's incredible answer above and boiled it down to just one CSS rule that I can apply. Now all I have to do is add the contentCentered class name to elements I want to center:

.contentCentered {
  text-align: center;
}

.contentCentered::before {
  content: '';
  display: inline-block;
  height: 100%; 
  vertical-align: middle;
  margin-right: -.25em; /* Adjusts for spacing */
}

.contentCentered > * {
  display: inline-block;
  vertical-align: middle;
}
<div class="contentCentered">
  <div>
    <h1>Some text</h1>
    <p>But he stole up to us again, and suddenly clapping his hand on my
      shoulder, said&mdash;"Did ye see anything looking like men going
      towards that ship a while ago?"</p>
  </div>
</div>

Forked CodePen: http://codepen.io/dougli/pen/Eeysg

John
  • 1
  • 13
  • 98
  • 177
dougli
  • 101
  • 1
  • 2
  • Cool. Thanks for sharing. One note of caution is that the `*` selector is going to perform worse than the accepted answer. Might not be an issue, but worth noting. http://www.stevesouders.com/blog/2009/06/18/simplifying-css-selectors/ – jessegavin Dec 06 '13 at 22:12
  • Thanks. Yeah, it'll probably perform worse, but nowadays CSS rules probably don't affect things much. We can probably improve this by selecting on `> div` instead. http://calendar.perfplanet.com/2011/css-selector-performance-has-changed-for-the-better/ – dougli Dec 13 '13 at 22:40
  • Simple and great. Works for me perfectly. – Lovro Jan 06 '18 at 14:46
8

you can use flex display such as below code:

.example{
  background-color:red;
  height:90px;
  width:90px;
  display:flex;
  align-items:center; /*for vertically center*/
  justify-content:center; /*for horizontally center*/
}
<div class="example">
    <h6>Some text</h6>
</div>
hassan yousefi
  • 239
  • 3
  • 14
4

Best result for me so far:

div to be centered:

position: absolute;
top: 50%;
transform: translateY(-50%);
margin: 0 auto;
right: 0;
left: 0;
3

You can use margin auto. With flex, the div seems to be centered vertically too.

body,
html {
  height: 100%;
  margin: 0;
}
.site {
  height: 100%;
  display: flex;
}
.site .box {
  background: #0ff;
  max-width: 20vw;
  margin: auto;
}
<div class="site">
  <div class="box">
    <h1>blabla</h1>
    <p>blabla</p>
    <p>blablabla</p>
    <p>lbibdfvkdlvfdks</p>
  </div>
</div>
Loïc G.
  • 41
  • 3
1

For me the best way to do this is:

.container{
  position: relative;
}

.element{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

The advantage is not having to make the height explicit

Leandro Castro
  • 518
  • 8
  • 14
0

This is my awesome solution for a div with a dynamic (percentaged) height.

CSS

.vertical_placer{
  background:red;
  position:absolute; 
  height:43%; 
  width:100%;
  display: table;
}

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

.inner_placer svg{
  position:relative;
  color:#fff;
  background:blue;
  width:30%;
  min-height:20px;
  max-height:60px;
  height:20%;
}

HTML

<div class="footer">
    <div class="vertical_placer">
        <div class="inner_placer">
            <svg> some Text here</svg>
        </div>
    </div>
</div>  

Try this by yourself.

honk
  • 9,137
  • 11
  • 75
  • 83