Michael36
Michael36

Reputation: 124

Children don't inherit style despite declaration on ancestor

I declared display: inline on body but the elements are still displayed in block fashion. Please help me understand the concept.

Declaring display: inline directly on li works: They will display in inline fashion. But why doesn't inline apply to them when declaring on body?

body {display: inline}
<body>
  <ul>
    <li>1234</li>
    <li>5678</li>
  </ul>
</body>

I also tried with display: inherit on li, but it still displays in block fashion, and also removes the markers which I don't want:

body {display: inline}
li {display: inherit}
<body>
  <ul>
    <li>1234</li>
    <li>5678</li>
  </ul>
</body>

Upvotes: 1

Views: 71

Answers (1)

Oskar Grosser
Oskar Grosser

Reputation: 3434

Inherit display

Setting display on an element will not have its children inherit the value:

.wrapper {display: inline}
<div class="wrapper">
  <div>First text.</div>
  <div>Second text.</div>
</div>

While you can declare elements to inherit display from their ancestors, I do not recommend this (generally):

  • Declaring elements to inherit display requires a declaration just as much as declaring a specific display value.
  • If the selector is too unspecific, elements with sensible default values will lose those values.

It is important to choose good selectors for your use case. For your short example it is fine to have a generic selector, like body * (in words: "All descendants of BODY") which uses the universal selector * and a descendant combinator .

Note: Using a combinator generally does not select "sub-selections" itself, e.g. BODY of selector body * will not be selected. Also note that the descendant combinator is less specific than the child combinator >.

Here is an analogous example:

.wrapper * {display: inline}
<div class="wrapper">
  <div>First text.</div>
  <div>Second text.</div>
</div>

If however you still want to declare the elements to inherit display, here is an example:

.wrapper {display: inline} /*Set the value to inherit*/
.wrapper * {display: inherit}
<div class="wrapper">
  <div>First text.</div>
  <div>Second text.</div>
</div>

Lists and display

Lists' default display value is block, and the value of its children (LI) is list-item. If you simply want to get rid of the markers, declare the list list-style-type: none:

ul {list-style-type: none}
<ul>
  <li>First item.
  <li>Second item.
</ul>

But you can also declare the list and its children (ul, ul>*) as display: inline. Note that removing padding requires another declaration.

ul, ul>* {display: inline}
<ul>
  <li>First item.
  <li>Second item.
</ul>

Inline list with markers

If you want to inline the list-items with markers, there are several possibilities:

You can keep ::markers of inline list-items by declaring the list as display: inline-flex.

Sidenote: ::markers are by default outside the list-item's principal box, which means they can obstruct view of other boxes. Declare the list (or its items) as list-style-position: inside to keep the markers inside the list-item's box to prevent such overflow.

Note that flex-wrap: nowrap is its default, which is unlike regular text flow. Use flex-wrap: wrap to have the list-items (and their content) flow like regular text:

// Ignore; for handling radio inputs
const form = document.querySelector("form");
const list = document.querySelector("ol");
form.addEventListener("input", evt => {
  const className = evt.target.name === "position" ? "inside" : "wrap";
  // Classes for applying non-default values are named after the values
  const state = evt.target.value === className;
  
  list.classList.toggle(className, state);
});
ol {display: inline-flex}
.inside {list-style-position: inside}
.wrap {flex-wrap: wrap}

/*Ignore; for presentational purposes*/
ol {margin: 0; padding: 0}
li {border: 1px solid darkgray}
section {margin-block: 1.2rem}
<form>
  <div>
    List type:
    <label for="i-outside">
      <input id="i-outside" name="position" type="radio" value="outside" checked> Outside
    </label>
    <label for="i-inside">
      <input id="i-inside" name="position" type="radio" value="inside"> Inside
    </label>
  </div>
  <div>
    Wrapping:
    <label for="i-nowrap">
      <input id="i-nowrap" name="wrap" type="radio" value="nowrap" checked> No-wrap
    </label>
    <label for="i-wrap">
      <input id="i-wrap" name="wrap" type="radio" value="wrap"> Wrap
    </label>
  </div>
</form>

<ol>
  <li>A very long named item that hopefully overflows its box, and
  <li>An item, and
  <li>Another short item, and
  <li>another very long named item that too should overflow its box.
</ol>

You can also use custom "markers" via the ::before pseudo-element.

Sidenote: With a custom-styled CSS counter, you can recreate e.g. list-style-type: georgian counters:

ol {counter-reset: li-counter}
li {counter-increment: li-counter}
li::before {content: counter(li-counter)". "}

/*Ignore; for presentational purposes*/
ol, li {padding: 0; display: inline}
A list consisting of
<ol>
  <li>one item and
  <li>another item.
</ol>

List-items don't inherit display?

You asked why the following didn't result in the LIs to inherit the BODY's display value:

body {display: inline}
li {display: inherit}
<body>
  <ul>
    <li>First item.
    <li>Second item.
  </ul>
</body>

That is because—while BODY is declared as display: inline—the list in between has the default display value of block. Since inheritance happens from the element's immediate ancestor, the list-items will inherit the value block from the list.

Accessibility concern

Lists are semantic elements, and by default are interpreted as such by the browser. But changing their styles may change how the browser interprets them, potentially reducing their semantic down to a generic text-containing element.

Therefore it is generally good to ensure that lists are interpreted as lists by declaring the list and its items via the ARIA-attribute role as list and listitem, respectively. This overrules the browsers decision to interpret the element as a list, even if the browser would have decided not to interpret it as a list because of some CSS.

Another point is that pseudo-elements (e.g. ::marker, ::before) are technically not part of the document, and can therefore not be interacted with (e.g. selecting or copying). If you need them as part of the document, then literally include them in the document.

Upvotes: 2

Related Questions