1
votes

I'm puzzled about working vertical-align property. How does it work? If you explain me how it works I'll be thankful:) Why does that property work differently? In the first case I try to align my text by vertical-align: text-top, but... my text goes down. Why? In the second case I align my square by vertical-align: text-top and this property works properly. What's the difference? Why my text goes down instead of rising?

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  display: table-cell;
  background: yellow;
  width: 700px;
  height: 500px;
  vertical-align: baseline;
  line-height: 50px;
}


.three {
  vertical-align: text-top;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> background <span class="block"></span></p>
</div>
1
vertical-align: text-top; Aligns the top of the element with the top of the parent element's font.Rob Moll
@RobMoll and? what is the top of the element and what is the top of the parent element's font?Temani Afif
@RobMoll yes, it does. But it still doesn't explain the difference in behavior in these two casesEva

1 Answers

4
votes

Ref: https://www.w3.org/TR/CSS2/visudet.html#line-height

To understand this you need to first consider the definition of text-top:

The following values only have meaning with respect to a parent inline element, or to the strut of a parent block container element.

In the following definitions, for inline non-replaced elements, the box used for alignment is the box whose height is the 'line-height' (containing the box's glyphs and the half-leading on each side, see above).

Then

text-top

Align the top of the box with the top of the parent's content area

So we need to identify the top of the box and the top of the parent's content area

If we add some decorations, we can easily identify them

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  background:red;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> <span>background</span> <span class="block"></span></p>
</div>

The green coloration define the content area and we can clearly see that the square is aligned with that top. Until now it's trivial.

The tricky case is the text because we see that the red doesn't align with the green. This is due to the line-height. In the above it's said that we consider the box whose height is the line-height and we know that line-height is inherited so our span (the red one) will inherit the 50px line-height and this is our reference for the alignment.

Which is more tricky is that even if we change the line-height, the content area doesn't change (the red coloration will always stay the same)

The 'height' property does not apply. The height of the content area should be based on the font

and

The vertical padding, border and margin of an inline, non-replaced box start at the top and bottom of the content area, and has nothing to do with the 'line-height'. But only the 'line-height' is used when calculating the height of the line box.

To make it easy, imagine the text inside an invisible box with a height equal to 50px and this box is aligned at the top of the parent content area then inside that box you have your text and the background coloration will only cover the content area whataver the line-height.

If you use a line-height equal to the content area you will have a perfect alignment:

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  background:red;
  line-height:33px;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> <span>background</span> <span class="block"></span></p>
</div>

Increase the line-height and you will have a bigger invisible box and the text will move more down:

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  background:red;
  line-height:100px;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> <span>background</span> <span class="block"></span></p>
</div>

And logically with a small line-height, it will go up:

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  background:red;
  line-height:1px;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> <span>background</span> <span class="block"></span></p>
</div>

If you change the display to inline-block you will better see the issue because the coloration will cover the whole area defined by line-height

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  background:red;
  display:inline-block;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three">yellow</span> <span>background</span> <span class="block"></span></p>
</div>

And with more element we can better illustrate our invisible box:

body {
  font-family: sans-serif;
  font-size: 30px;
}

p {
  background: yellow;
  line-height: 50px;
  background:
   linear-gradient(blue,blue) 0 7px/100% 2px no-repeat
   yellow;
}

p span {
  background:green;
}

.three {
  vertical-align: text-top;
  outline:1px solid blue;
  background:transparent;
  display:inline-block;
}
.three > span {
  background:red;
}
  

.block {
  display: inline-block;
  width: 20px;
  height: 20px;
  background: pink;
  border: 2px solid black;
  vertical-align: text-top;
}
<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three"><span>yellow</span></span> <span>background</span> <span class="block"></span></p>
</div>

<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three" style="line-height:100px"><span>yellow</span></span> <span>background</span> <span class="block"></span></p>
</div>


<div>
  <p><span class="one">I'm</span> <span class="two">on the</span> <span class="three" style="line-height:1px"><span>yellow</span></span> <span>background</span> <span class="block"></span></p>
</div>