Preface
The first part of this article is translated from Vertical-Align: All You Need To Know, which is the article mentioned in the reference materials section of [CSS Vertical and Horizontal Centering](/articles/css 上下左右居中/) that was waiting to be translated
The rest is a summary of techniques from the original article
.top{display:inline-block;vertical-align:top}.bottom{display:inline-block;vertical-align:bottom}.middle{display:inline-block;vertical-align:middle}.baseline{display:inline-block;vertical-align:baseline}.text-top{display:inline-block;vertical-align:text-top}.text-bottom{display:inline-block;vertical-align:text-bottom}.sub{display:inline-block;vertical-align:sub}.super{display:inline-block;vertical-align:super}strong{font-weight:400}figure{line-height:1;overflow:visible}figure ul,figure ol{margin:0 !important;;padding:0 !important;}figure.center{text-align:center}figure.bg-grey{background:#f0f0f0;padding:1em}figure .center{text-align:center;display:inline-block;width:100%}figure+pre{margin-top:1em}figure *+*{margin-top:0}figure figcaption{width:100%;margin:0;margin-top:1em;font-style:italic;font-size:.8em}figure .bg-grey{background:#d3d3d3}figure .bg-green{background:#c1cd89}figure .bg-yellow{background:#fcdb9a}figure .bg-blue{background:#8ab3bf}figure .border-grey{border:1px solid #d3d3d3}figure .font{white-space:nowrap;line-height:1}figure .font.small{font-size:.6666em}figure .font.smaller{font-size:.3333em}figure .font.large{font-size:1.5em}figure .font.larger{font-size:3em}figure .font.tall-line-height{line-height:2}figure .font.short-line-height{line-height:.5}figure .font.color-grey{color:#d3d3d3}figure .box{min-width:1em;min-height:1em}figure .box.shorter{min-height:.25em}figure .box.shorter.quad{min-width:.25em}figure .box.short{min-height:.5em}figure .box.short.quad{min-width:.5em}figure .box.tall{height:2em}figure .box.tall.quad{width:2em}figure .box.taller{height:4em}figure .box.taller.quad{width:4em}figure .box.quad.max-third-width{max-width:30%}figure .inline-overlay{display:inline-block;width:100%;margin-right:-100%;position:relative;z-index:10}figure .line{display:inline-block;width:100%;margin-right:-100%;position:relative;z-index:10;border-top:1px solid #000}figure .line.dashed{border-style:dashed}figure .line.dotted{border-style:dotted}figure .line.grey{border-color:#d3d3d3}figure .line.red{border-color:red}figure .line.blue{border-color:#00f}figure .line.green{border-color:#32cd32}figure .line.orange{border-color:#daa520}figure .show-box-model{background:#c1cd89;border-color:#fcdb9a}figure .show-box-model>.show-box-model-content{background:#8ab3bf;display:block;min-width:100%;min-height:100%} .columns.no-break>* { margin-right: 2.5%; width: 47.5% !important; display: inline-block; margin-top: 0; } .columns.no-break.thirds>* { margin-right: 2.5%; width: 30% !important; display: inline-block; margin-left: 0; }Often need to vertically align some elements displayed side by side
CSS provides some optional solutions, sometimes solved through float, sometimes with position: absolute, sometimes even using dirty methods like manually adding margin or padding, I don't really like these solutions. Floating just aligns them to the top, and requires manual clearing (of floating effects). Absolute positioning takes some elements out of the normal document flow, so they can no longer affect surrounding elements. And even the tiniest change will break fixed margin and padding
But there's another player: vertical-align. I think it's more trustworthy. Although technically, using vertical-align for layout is a hack, because it's not designed for layout, but for aligning text with elements next to the text. However, it can also use vertical-align to flexibly and fine-grainedly align elements in different environments. No need to know element sizes, elements remain in the normal document flow, other elements can respond to their size changes. These advantages make it a valuable option
Peculiarities of vertical-align
But vertical-align can sometimes be really annoying, using it can be frustrating, seems to have some mysterious rules. For example, might encounter, changing an element's vertical-align doesn't change its own alignment at all, but other elements on the same line (their alignment) changed! Still occasionally dive into these dark corners, making me tear my hair out
Unfortunately, most related resources are too superficial, especially when we want to use vertical-align for layout. They focus on the wrong idea of trying to vertically align everything inside an element, give basic introductions to the property, and explain element alignment in very simple scenarios, without explaining the technical parts
So, I set myself the goal of clarifying vertical-align behavior once and for all, diving deep into W3C's CSS specifications, and ended up trying some examples, the final result is this article
So, let's start with the game rules
Dependencies of vertical-align
vertical-align is used to align inline-level elements, that is, those whose display property's computed value is:
-
inline -
inline-block -
inline-table(not considered in this article)
Inline elements are text wrapped by basic tags
Inline-block elements are like what their name says: inline block elements (block elements living inline). They can have width, height (possibly determined by their content) and padding, border and margin
Inline-level elements are arranged one after another in a line, once the current line can't fit, a new line is created below it, all these lines have so-called line boxes, wrapping all content of that line. Content of different sizes means line boxes of unequal heights. In the diagram below the top and bottom boundaries of line boxes are marked with red lines:
A tall in a line of text.A short in a line of text.
This can happen.
Line boxes are our context (the line boxes trace out the field we are playing on), the vertical-align property in these line boxes is responsible for aligning various elements. So, what exactly is element alignment?
baseline and outer edge
The most important reference point for vertical alignment is the baseline of relevant elements, in certain cases, the top and bottom edges of element wrapper boxes are also important. Let's look together at where the baseline and outer edge are for various types of elements:
Inline Elements
-->
-->
Can see 3 lines of text side by side, the top and bottom edges of line height are indicated with red lines, font height with green lines, baseline with blue lines. Left text's line height is set same as font-size, green and red lines overlap. Middle text line height is 2 times font-size. Right line height is half of font-size
Inline element's outer edge aligns with the top and bottom edges of its line height, if line height is less than font height, then it doesn't matter. So, outer edge is the red lines in the above diagram
Inline element's baseline is the line characters sit on (baseline is the line, the characters are sitting on), i.e., the blue lines in the diagram. What's hard to understand is, baseline is sometimes below the font height, see W3C specification's detailed definition
Inline-Block Elements
-->
-->
From left to right: inline-block element containing in-flow content (that "c"), inline-block element containing in-flow content and overflow: hidden, and inline-block element without in-flow content (but content area has height). Margin boundaries are indicated with red lines, border is yellow, padding is green, content area is blue, each inline-block element's baseline is indicated with blue lines
Inline-block element's outer edge is the top and bottom edges of its margin-box, i.e., the red lines in the diagram
Inline-block element's baseline depends on whether the element contains in-flow content:
-
When containing in-flow content, inline-block element's baseline is the baseline of the last content element in normal flow (left example), the last element's baseline is determined according to its own rules
-
When containing in-flow content but having
overflowproperty with computed value non-visible, baseline is the bottom edge of margin-box (middle example), so, it's the same as the bottom edge of inline-block element -
When not containing in-flow content, baseline is also the bottom edge of margin-box (right example)
Line Boxes
x This can happen.In the above diagram, the top and bottom edges of the line box's text box (more information see below) are drawn in green, while baseline is still in blue, and text elements are set with gray background to highlight them
Line box's top edge aligns with the top edge of the tallest element in that line, and bottom edge aligns with the bottom edge of the lowest element in that line, which is the part indicated with red lines in the above diagram
Line box's baseline is variable:
CSS 2.1 does not define the position of the line box's baseline. — the W3C Specs
This is probably the most confusing part when using vertical-align. That is to say, where specifically to place baseline must satisfy all other conditions, such as vertical-align and making line box height minimal, it's a free parameter in the equation
Because line box's baseline is invisible, can't visually see where it is. But easy to make it visible, just add a character at the beginning of the line in question, like the "x" added in the diagram. If this character isn't aligned in any way, it will by default sit on the baseline
Around the baseline, line box contains what we call text box. Text box can simply be seen as an inline element in a line box without any alignment. Its height equals its parent element's font-size. Therefore, text box only wraps unformatted text in the line box, indicated with green lines in the above diagram. Because this text box is tied to baseline, when baseline moves it moves along with it (Note: this text box is called strut in W3C specification)
This is the hardest part. Now, we know the ins and outs. Quick summary of the most important points:
-
There's an area called line box, where vertical alignment happens. It has baseline, text box and top/bottom edges
-
Inline-level elements, are the things being aligned, they have baseline and top/bottom edges
Values of vertical-align
By using vertical-align to set certain associations between the reference points mentioned above and inline-level elements
Element's baseline aligns relative to line box baseline
x baseline sub super -50% +10px-
baseline: Element's baseline coincides exactly with line box's baseline -
sub: Element's baseline moves below line box baseline -
super: Element's baseline moves above line box's baseline -
<percentage>: Element's baseline moves relative to line box's baseline by percentage ofline-height -
<length>: Element's baseline moves relative to line box's baseline by an absolute length
Element's outer edge aligns relative to line box baseline
x middlemiddle: Midpoint between element's top and bottom edges aligns with line box's baseline plus half an x-height
Element's outer edge aligns relative to line box's text box
x text-top text-bottom-
text-top: Element's top edge aligns with top edge of line box's text box -
text-bottom: Element's bottom edge aligns with bottom edge of line box's text box
Element's outer edge aligns relative to line box's outer edge
x top bottom-
top: Element's top edge aligns with line box's top edge -
bottom: Element's bottom edge aligns with line box's bottom edge
Of course, official definitions can all be found in W3C specifications
Why vertical-align behaves this way
We can look closer at vertical alignment in certain scenarios, especially those scenarios where things might go wrong
Centered Small Icon
There's a problem bothering me: I have a small icon, want to center align it with a line of text next to it. Just giving the small icon vertical-align: middle doesn't look satisfactorily centered. Look at this example:
<!-- 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's another identical example, but I've drawn some auxiliary lines you've learned about from above:
This can reveal some clues, because text on the left doesn't have any alignment, it sits on baseline. Actually, setting vertical-align: middle to align the small square, we aligned it to the center position (half x-height) of lowercase letters without ascenders. So, characters with ascenders appear relatively higher
On the right, we also vertically aligned the midpoint of the entire font area, achieving the effect by moving text's baseline slightly down relative to line box baseline. Result is text and the small icon next to it are beautifully centered
Movement of Line Box Baseline
This is a common trap when using vertical-align: line box's baseline is affected by all elements in that line. We assume an element is aligned this way (relative to its own baseline), line box's baseline has to move. Because most vertical alignment (except top and bottom) is relative to its baseline, causing all other elements in that line to also adjust position
Some examples:
- If a line has a tall element spanning the entire height,
vertical-alignwon't work on it, there's no space above its top and below its bottom for it to move. To satisfy its alignment relationship relative to line box baseline, line box baseline has to move. Short square hasvertical-align: baseline, on left, tall square istext-bottomaligned, on right istext-topaligned, can discover baseline takes the short box jumping up 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>
Same behavior occurs when aligning a tall element with other vertical-align values
- Even setting
vertical-aligntobottom(left diagram) andtop(right diagram) will move baseline, this is strange, because baseline isn't 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>
- Put two large elements in a line, vertically aligning them will move baseline to position satisfying their alignment, then line box height will also adjust (left diagram). Adding a third element, if its alignment won't make it exceed line box boundaries, neither affects line box height nor baseline position (middle diagram). If it exceeds line box boundaries, line box height and baseline will adjust again, in this case, our initial two squares get pushed down (right diagram)
<!-- left mark-up -->
<span class="tall-box text-bottom"></span>
<span class="tall-box text-top"></span>
<!-- mark-up in the middle -->
<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 May Be Small Gaps Below Inline-Level Elements
Look at this situation, very easy to encounter when trying to vertical-align li in 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 shown in diagram, list items sit on baseline, below baseline is some space used to accommodate text descenders, causing gaps. Solution? Just move baseline further away, for example, use vertical-align: middle to align list items:
<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 won't appear in inline-block elements containing text content, because content has already moved to baseline
Gaps Between Inline-Level Elements Break Layout
This is mainly a problem with inline-level elements themselves, but because they're one of the dependencies of vertical-align, best to understand clearly
Can also see gaps between list items in previous example, gaps come from whitespace characters between inline elements appearing in markup code (HTML/XML etc.). All whitespace characters between inline elements are merged into one space, it's this space that gets in the way, for example if wanting two inline elements right next to each other and both set width: 50%, there's not enough space to accommodate two 50% elements and a space. So will split into 2 lines breaking layout (left diagram). To remove gaps, need to remove whitespace characters, for example using HTML comments (right diagram)
<!-- 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 Revealed
Well, that's it, not very complex once knowing the rules. If vertical-align doesn't work, just consider these questions:
-
Where are line box's baseline and top/bottom edges?
-
Where are inline-level element's baseline and top/bottom edges?
This will reveal the problem's solution
II. Techniques
1. How to Determine Line Box's Baseline?
Add a character without descenders to this line, general convention is to add x, the bottom edge of the character is the position of line box baseline
For example:
.baseline:before {
content: 'x';
}
2. How to Determine Line Box's Boundaries?
Use the "element's outer edge aligns relative to line box's outer edge" mentioned above:
.line-box-top {
border-top: 1px dotted red;
/* Let border-top coincide with line box's top edge */
vertical-align: top;
/* Width fills entire line */
display: inline-block;
width: 100%;
/* Make room, avoid affecting content layout */
margin-right: -100%;
/* Raise z, avoid being covered by content */
position: relative;
z-index: 10;
}
/* .line-box-bottom similar to this */
At the beginning of the line where you want to clarify line box boundaries (because using margin-right: -100%, so put at leftmost) add
<span class="line-box-top"></span><span class="line-box-top"></span>
and that's it
3. How to Determine Text Box's Boundaries?
Similar to method for determining line box boundaries, use vertical-align: text-top; and vertical-align: text-bottom;
Align relative to whom, then can draw this "whom"
4. Technique for Removing Whitespace Characters with HTML Comments
For example:
<figure>
<span class="large font">
<span class="green dotted line text-top"> </span><!--
--><span class="green dotted line text-bottom"> </span><!--
--><span class="red dotted line top"> </span><!--
--><span class="red dotted line bottom"> </span><!--
--><span class="blue dotted line baseline"> </span><!--
--><span class="font color-grey inline-overlay">x</span><!--
--><span class="center">
<span class="middle bg-grey">This</span>
<span class="tall box bg-grey text-top"> </span>
<span class="top bg-grey">can</span>
<span class="tall box bg-grey text-bottom"> </span>
<span class="bottom bg-grey">happen.</span>
</span>
</span>
</figure>
While removing whitespace characters, preserving tag indentation format, very interesting
No comments yet. Be the first to share your thoughts.