Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mystery of the height of uls in positioned divs: How does it even work?

I have a reasonable understanding of css, the box-model, and positioning in general.

But there is one mystery I've never grokked, nor have I been able to google my way to an understanding of it on the many times it has slowed down my implementation of a layout:

The mystery of the ul inside a positioned div

Why must I set the ul font-size to 0, and line-height to 1, and then reset them back to what I want them to be in the li in order to get the ul to be the right height and (appear to) be contained in the div?

Where does the mysterious extra height come from if I set the ul line-height to the nominal line height I want? Why is this extra height different on every browser?

There are many other ways to end up with mysterious heights and offsets, but I feel if I understood these cases I would have a sufficient model.

Does anyone possess a mental model that will allow me to predict these behaviours? (and thus hopefully remember them, so that I don't waste time re-figuring it out every 6 months)?

Here is a playground demonstrating what I seek to understand.

section {
  background-color: gray;
  width: 500px;
  height: 700px;
  padding-top: 10px;
  font-size: 12px;
  line-height: 25px;
}
.reference {
  background-color: aquamarine;
  height: 25px;
  width: 75px;
  float: left;
  padding: 0;
  margin: 0;
}
.container {
  padding: 0;
  margin: 0;
  background-color: white;
  height: 25px;
  width: 423px;
}
ul {
  list-style-type: none;
  background-color: rgba(255, 50, 50, 0.25);
}
li {
  display: inline;
}
.case-one {
  display: inline-block;
}
.reference-two {
  position: absolute;
  top: 130px;
}
.reference-two-b {
  position: absolute;
  top: 200px;
}
.case-two {
  position: absolute;
  left: 85px;
}
.case-two-a {
  top: 130px;
}
.case-two-b {
  top: 200px;
}
.case-two ul {
  position: relative;
  font-size: 0px;
}
.case-two-a ul {
  line-height: 1;
}
.case-two-b ul {
  line-height: 25px;
}
.case-two li {
  display: inline;
  font-size: 12px;
  line-height: 25px;
}
<section>
  <div class="container case-one">this I fully understand</div>
  <div class="reference">case one</div>
  <br/>
  <br/>


  <div class="container case-one">
    <div style="background-color: rgba(50,50,200,0.4); width:8px;height: 12px; position: absolute;"></div>
    <ul>
      <li>but</li>
      <li>not this.</li>
      <li>why is the ul offset?</li>
      <li>why by font-size, not line-height?</li>
    </ul>
  </div>
  <div class="reference">case one b</div>


  <div class="container case-two case-two-a">
    <ul>
      <li>why does</li>
      <li>setting the ul font-size to 0 work?</li>
    </ul>
    <div style="line-height: 1.2;">also, why does the ul line-height have to be set to 1? If it is set to 25px, the ul grows, see below</div>
  </div>
  <div class="reference reference-two">case two</div>

  <div class="container case-two case-two-b">
    <div style="background-color: rgba(50,50,200,0.4);left:4px;width:8px; height: 30px; position: absolute;"></div>
    <div style="background-color: rgba(50,200,50,0.4);width:8px; height: 29px; position: absolute;left:16px;"></div>
    <div style="background-color: rgba(200,50,50,0.4);width:8px; height: 28px; position: absolute;left:28px;"></div>
    <ul>
      <li>why does</li>
      <li>setting the ul line-height to 25px result in it being 30px high?</li>
    </ul>
    <div style="line-height: 1.4;margin-top: 8px;">where does the 5px come from?? Why is it 4px on chrome? Why is it 3px on firefox? Anyone else get any other heights, lol?</div>
  </div>
  <div class="reference reference-two-b">case two</div>

</section>
like image 807
Michael Johnston Avatar asked Nov 01 '22 00:11

Michael Johnston


1 Answers

Yes. The mental model you need is the strut. It seems to be CSS's best kept secret, which is unfortunate as it's critical to understanding case two, and many similar behaviours. Work out where the strut is and how tall it is and many layout behaviours start to make sense.

First though, case one. Browsers define a default top (and bottom) margin for ul elements as a proportion of the font-size. Different browsers are free to set whatever proportion they want, hence some sort of reset or normalisation is commonplace.

Now case two, and back to the strut. Block boxes that contain only inline-level elements are made up of a stack of line boxes. Each line box starts with a zero-width virtual inline element - the strut. Now, line-heights only affect inline elements. but when you set the line-height of a block element, each child element, including the strut, inherits that line-height. You can override the line-height of the real child elements, but not the strut.

So when you say ul { font-size:0; line-height:1; } that means that the strut's height will be 0px multiplied by a factor of 1 = 0px, so no matter where the strut is, it will take up no space.

However, when you say ul { line-height:24px; }, that means that the strut's effective height will be 24px. Line-heights of inline elements affect the height of the line of text they are on by adding a space above and below the characters of the element. So if the character is 18px tall and the line-height is 24px, the extra space to add is 6px. This is called the leading. Half of the leading is added above the element, and half below. These are called the half-leading.

So if you have ul { font-size:0; line-height:24px; } the strut on each line of text has a 0 character size, but a half-leading above and below of 12px. But the other elements have a non-zero character size. If their font em size is 12px, that might, it depends on the typeface, give a used character height of 16px (say 13px above the baseline and 3px below). So they would have a half-leading of (24px - 16px) / 2 = 4px.

But as well as their heights, you also have to take into account their vertical alignments. A strut is always `vertical-align:baseline', and aligns on that basis to the other inline elements on that line of text.

Let's assume for simplicity that the other text elements are also vertical-align:baseline.

Then the total line-height for the line of text is the maximum height above the baseline max(13px + 4px (character), 12px (strut) plus the maximum height below the baseline max(3px + 4px (character), 12px (strut). Which is 17px + 12px = 29px, hence more than the 24px line-height specified.

Mr Lister provides some helpful diagrams in an answer to a related question

like image 123
Alohci Avatar answered Nov 15 '22 05:11

Alohci