1

I am trying to create a custom style, and checking it with "HTML5 kitchen sink". The margin between nested <ul> elements confuses me.

When I inspect the elements: a <ul> has a top margin of 16px. A nested <ul> within a <ul> has a top margin of 0px. This leads me to believe that for the user agent there are not static defaults for each element as I used to believe, but a custom style sheet with a bunch of rules (e.g. testing for nested lists).

Question: How does the default user agent style the gap between nested <li> as zero, but between adjacent <li> items in side by side <ul> as NOT zero? Refer to below to make it more clear ("NO GAP BELOW HERE"/"GAP BELOW HERE"):

I created a code pen here: https://codepen.io/run_the_race/pen/JmqEER

body {
  background-color: #a3d5d3;
}
 <ul>
        <li>Unordered List item one
            <ul>
                <li>Nested list item, NO GAP BELOW HERE
                    <ul>
                        <li>Level 3, item one, NO GAP ABOVE HERE</li>
                        <li>Level 3, item two</li>
                        <li>Level 3, item three</li>
                        <li>Level 3, item four</li>
                    </ul>
                </li>
                <li>List item two</li>
                <li>List item three</li>
                <li>List item four</li>
            </ul>
        </li>
        <li>List item two</li>
        <li>List item three</li>
        <li>List item four, GAP BELOW HERE</li>
    </ul>
    <ul>
        <li>ul + ul: Item 1, GAP ABOVE HERE</li>
        <li>ul + ul: Il item 2</li>
        <li>ul + ul: Il item 2</li>
    </ul>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
run_the_race
  • 1,344
  • 2
  • 36
  • 62

2 Answers2

2

This leads me to believe that for the user agent there are not static defaults for each element as I used to believe, but a custom style sheet with a bunch of rules (e.g. testing for nested lists).

Yes, that's exactly the case. Every UA stylesheet contains defaults for a variety of situations. And many of these complex defaults are standardized in the HTML spec along with the simple (by element type) ones, although each UA is of course free to customize (as you put it) its defaults.

In this case, the default ul and ol margins are simply zeroed out for any such elements that are nested. The UA doesn't apply any margins directly to li elements or perform any non-CSS special-casing logic. Here's how the HTML spec expresses it in terms of CSS:

dir, dl, menu, ol, ul { margin-block-start: 1em; margin-block-end: 1em; }

:matches(dir, dl, menu, ol, ul) :matches(dir, dl, menu, ol, ul) {
  margin-block-start: 0; margin-block-end: 0;
}

Here's how Firefox does it:

/* nested lists have no top/bottom margins */
:-moz-any(ul, ol, dir, menu, dl) ul,
:-moz-any(ul, ol, dir, menu, dl) ol,
:-moz-any(ul, ol, dir, menu, dl) dir,
:-moz-any(ul, ol, dir, menu, dl) menu,
:-moz-any(ul, ol, dir, menu, dl) dl {
  margin-block-start: 0;
  margin-block-end: 0;
}

And how Chrome does it:

ol ul, ul ol, ul ul, ol ol {
    -webkit-margin-before: 0;
    -webkit-margin-after: 0;
}

The only implementation differences are that Firefox continues to use its experimental :-moz-any() pseudo-class, and Chrome hasn't implemented the standard logical properties yet so it continues to use the non-standard -webkit-margin-* properties, and it only does this with ul, ol but not dir, dl, menu. But the principle is the same.

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Thanks for the very detailed explanation, that is boss! – run_the_race Oct 30 '18 at 11:39
  • I'm keeping the code samples here for historical purposes, but note that :matches() has been turned into :is() and Chrome has shipped logical properties, so the HTML spec and the respective implementations have been updated accordingly. – BoltClock Apr 05 '21 at 09:29
2

The agent simply applies margins to ul and then 0 margin to ul ul

Here's a Firefox example:

ul {
  margin-block-start: 1em;
  margin-block-end: 1em;
}

:-moz-any(ul, ol, dir, menu, dl) ul {
  margin-block-start: 0;
  margin-block-end: 0;
}
<ul>
  <li>Unordered List item one
    <ul>
      <li>Nested list item, NO GAP BELOW HERE
        <ul>
          <li>Level 3, item one, NO GAP ABOVE HERE</li>
          <li>Level 3, item two</li>
          <li>Level 3, item three</li>
          <li>Level 3, item four</li>
        </ul>
      </li>
      <li>List item two</li>
      <li>List item three</li>
      <li>List item four</li>
    </ul>
  </li>
  <li>List item two</li>
  <li>List item three</li>
  <li>List item four, GAP BELOW HERE</li>
</ul>
DreamTeK
  • 32,537
  • 27
  • 112
  • 171