Search code examples
htmlcssblockinline

block <a> inside inline <li> behaviour


The following HTML / CSS result strikes me as odd and unexpected and I'm really looking for the logic behind the way this is parsed.

Suppose:

#nav {
  list-style-type-none
}
#nav li {
  display: inline;
}
/* make the LI's stack horizontally */

#nav li a {
  display: block;
  padding: 5px;
}
/* display as block to add padding */
<ul id="nav">
  <li><a href="#">Home</a>
  </li>
  <li><a href="#">About us</a>
  </li>
</ul>

This results in the <li>'s stacking vertically (like block elements) because of the <a>'s which are displayed as block elements. But I want the <li>'s to be stacked horizontally as I've defined them as inline elements.

The <li>'s are displayed as inline, why does the inner element <a> affect it?


Solution

  • This happens because inline elements wrapping around block level elements are split into block level by the child block. Here's a quote from the w3 spec:

    When an inline box contains an in-flow block-level box, the inline box (and its inline ancestors within the same line box) are broken around the block-level box (and any block-level siblings that are consecutive or separated only by collapsible whitespace and/or out-of-flow elements), splitting the inline box into two boxes (even if either side is empty), one on each side of the block-level box(es). The line boxes before the break and after the break are enclosed in anonymous block boxes, and the block-level box becomes a sibling of those anonymous boxes.

    http://www.w3.org/TR/CSS2/visuren.html#block-boxes

    If you have a look at that link, you'll see their diagram of the "anonymous box" that wraps inline text within a block that is followed by a block-level element (in their case, a <p>). So to try to further explain, in that quote above, basically they're saying that the inline box parent is getting split into block level boxes by the child with the display: block on it.

    Does that make sense?

    In order to get this to work right, you could set the li to display:inline-block

    For your convenience, I copied your code and made the change below.

    #nav {
      list-style-type-none
    }
    #nav li {
      display: inline-block;
    }
    /* make the LI's stack horizontally */
    
    #nav li a {
      display: block;
      padding: 5px;
    }
    /* display as block to add padding */
    <ul id="nav">
      <li><a href="#">Home</a>
      </li>
      <li><a href="#">About us</a>
      </li>
    </ul>