I do not understand when vertical-align
will and won't work.
Every time I run into a use case for vertical-align
it seems to be a coin toss as to whether it will actually work. I know it has to be applied to inline elements. I have read that I must specify a line-height
for elements that do not normally have one. I have read that the height
property must have a static (non-auto/non-%) value. I have read that some (modern) browsers do not handle vertical-align
correctly if the element they are used on is not naturally an inline element. I am unclear on whether vertical-align
should be on the containing element (like text-align
) or the element I want vertically aligned.
I've created this jsfiddle to try to work out the problem, but remain confused.
In the jsfiddle above I would expect #header
to be centered halfway between the top and bottom of #outer
and #inner
. Obviously, that's not the case.
Simply said: vertical-align
is only active/valid when the element it is applied to has display: inline-block
or ìnline
, which for example is useful if you want to align a bunch of images at their top border: You define them as inline-blocks
and apply vertical-align: top
to them
Here is an example:
.wrapper {
height: 250px;
border: 1px solid green;
}
.wrapper>img {
display: inline-block;
vertical-align: top;
}
<div class="wrapper">
<img src="https://placehold.it/120x40">
<img src="https://placehold.it/70x140">
<img src="https://placehold.it/80x60">
<img src="https://placehold.it/60x140">
</div>
In your fiddle, the elements are nested into each other, not next to each other, i.e. they are not siblings - there is only one child element each, so there is no alignment of siblings like in the example above.
The easiest way to understand why it's not working it to add many line of text because vertical-align will align on the respective line (the line-box) and not the whole container like you may think.
So if we add more text we have the following.
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
width:180px;
display: inline-block;
overflow:hidden;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
}
<div id="outer">
<div id="inner">
<h1 class="header">
Some Text Some Text Some Text
</h1>
</div>
<div id="inner">
<h1 class="header" style="vertical-align:middle;">
Some Text Some Text Some Text
</h1>
</div>
<div id="inner">
<h1 class="header" style="vertical-align:top;">
Some Text Some Text Some Text
</h1>
</div>
</div>
Basically nothing to align and all the alignments are almost equivalent simply because the text is defining the line-box thus its height is equal to the height of the line box and there is no room for alignment.
Now let's increase the height of the line (using line-height)
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
width:180px;
line-height:200px;
display: inline-block;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
line-height:1em;
}
<div id="outer">
<div id="inner">
<h1 class="header">
Some Text Some Text Some Text
</h1>
</div>
<div id="inner">
<h1 class="header" style="vertical-align:middle;">
Some Text Some Text Some Text
</h1>
</div>
<div id="inner">
<h1 class="header" style="vertical-align:top;">
Some Text Some Text Some Text
</h1>
</div>
</div>
See how each line is now bigger and each text is aligned on its respective line that has a height of 200px
and we can clearly see how the alignment are different.
In this case, the text has enough room to be aligned like you want and we see the magic if we keep only one line of text:
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
line-height: 200px;
display: inline-block;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
line-height:1em;
}
<div id="outer">
<div id="inner">
<h1 class="header">
Some Text
</h1>
<h1 class="header" style="vertical-align:middle">
Some Text
</h1>
<h1 class="header" style="vertical-align:top">
Some Text
</h1>
<h1 class="header" style="vertical-align:bottom">
Some Text
</h1>
</div>
</div>
You may also note how middle
and baseline
are very close because:
Aligns the middle of the element with the baseline plus half the x-height of the parentref
There is only a half x-height of difference.
Another important fact to note is that if we don't set a line height to the inline element this one will inherit the line-height of his parent and alignment like top
won't have any effect.
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
line-height: 200px;
display: inline-block;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
}
<div id="outer">
<div id="inner">
<h1 class="header">
Some Text
</h1>
<h1 class="header" style="vertical-align:middle">
Some Text
</h1>
<h1 class="header" style="vertical-align:top">
Some Text
</h1>
</div>
</div>
Aligns the top of the element and its descendants with the top of the entire line.
Having the same line-height means that the element is already at the top and also the bottom of the line box so both top
bottom
will behave like baseline (the default value).
The height of the line-box can also be controled by the height of elements. You may have a big font-size for one element that will make the height of the line bigger and have enough room to align small texts next to it in the same line box:
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
display: inline-block;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
}
@keyframes change {
from {font-size:20px;}
to {font-size:100px;}
}
<div id="outer">
<div id="inner">
<h1 class="header">
Text
</h1>
<h1 class="header" style="font-size:100px;animation:change 5s linear alternate infinite">
T
</h1>
<h1 class="header" style="vertical-align:middle;">
Text
</h1>
<h1 class="header" style="vertical-align:top;">
Text
</h1>
<h1 class="header" style="vertical-align:bottom;">
Text
</h1>
</div>
</div>
You can also constrol the line box by setting height of inline-block
element:
#outer {
box-sizing: border-box;
border: red 1px solid;
height: 200px;
text-align: center;
}
#inner {
border: blue 1px solid;
height: 200px;
display: inline-block;
}
.header {
display: inline;
border: green 1px solid;
margin: 0;
}
.elem {
display:inline-block;
background:red;
width:2px;
height:5px;
animation:change 5s linear alternate infinite;
}
@keyframes change {
from {height:20px;}
to {height:100px;}
}
<div id="outer">
<div id="inner">
<h1 class="header">
Text
</h1>
<div class="elem">
</div>
<h1 class="header" style="vertical-align:middle;">
Text
</h1>
<h1 class="header" style="vertical-align:top;">
Text
</h1>
<h1 class="header" style="vertical-align:bottom;">
Text
</h1>
</div>
</div>
As a conclusion: to align using vertical-align
you should have a line box that has a height bigger enough (explicitely set or set by other elements) where you can align your element. If your element is defining the line-box (which is the common case) then there is nothing to align.
Some good questions to have more detais:
Vertical align not working on inline-block
Inline elements and line-height
My inline-block elements are not lining up properly
Why is this inline-block element pushed downward?