Two elements (block and inline) show differences when their pseudo selectors have negative margins applied

Viewed 46

Yes, text cropping 2020.

Luckily a new CSS prop to deal with this mess is on its way, but before it is widely adopted in all browsers (ie11 no longer) will have to deal with trimming the white space above and bellow the text the traditional hacky way.

So in my examples I have 2 elements - a div (block) and a span (inline). Both have ::before and ::after selectors applied to. In one hand the ::before pseudo-element has a negative value margin-bottom (to pull up the main element itself). On the other hand the ::after pseudo-element has a negative value margin-top (to push it up). There is a differense in the results:

enter image description here

Probably it is because of the formatting context.

Anyone who could shed some light on why in the first example the inner (salmon) container remains "hidden behind" the pseudo-elements and in the second example the inner container overlaps them??

Code: https://codepen.io/pollx/pen/MWyvoXm

.box{
  box-sizing: border-box;
  background: salmon;
/*   border: 2px solid blue; */
}

.box::before,
.box::after {
  content: " ";
  display: block;
  height: 5px;
  width: 100%;
}

.box::before {
  background: red;
  margin-bottom: -8px;
}
.box::after {
  background: green;
  margin-top: -8px;
}
<div class="box">
  Box - div
</div>

<br>

<span class="box">
  Box - span
</span>

1 Answers

To understand what is happening you need to refer to the painting order algorithm.

The first case is the easiest one and following the painting order we will first paint the background of the div (step (2)) then the background of the pseudo elements (step (4)) then the content of the div (step (7))

  1. If the element is a block, list-item, or other block equivalent:

    1. background color of element unless it is the root element.

then

  1. For all its in-flow, non-positioned, block-level descendants in tree order: If the element is a block, list-item, or other block equivalent:

    1. background color of element.

Then

  1. Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order: ..

The (7) step is too long but there we will be painting the text. (7.2.1.4.1.1.3 the text.)


The second case is a bit tricky because we are dealing with block elements (our pseudo elements) inside an inline element and we need to consider this part of the specification to understand what's happening

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. When such an inline box is affected by relative positioning, any resulting translation also affects the block-level box contained in the inline box.

It's not trivial to understand but to resume, your span element will be splitted into 3 elements. One empty element before the ::before another one between the ::before and ::after (containing the text) and a last empty one after the ::after. The 3 elements will be enclosed in anonymous block boxes to have something like the following:

<anonymous_block><span></span></anonymous_block>
::before
<anonymous_block><span>Box - span</span></anonymous_block>
::after
<anonymous_block><span></span></anonymous_block>

adding border and some padding will make this more visible:

.box{
  box-sizing: border-box;
  background: salmon;
  border: 2px solid blue;
  padding:0 5px;
}

.box::before,
.box::after {
  content: " ";
  display: block;
  height: 5px;
  width: 100%;
  margin:5px 0;
}

.box::before {
  background: red;
}
.box::after {
  background: green;
}
<span class="box">
  Box - span
</span>

Now we get back to our painting border and the difference with our first case is that the background of the span will be painted later because it's not done in the step (4) but the step (7)

  1. Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order:

    1. Otherwise, for each line box of that element:

      1. For each box that is a child of that element, in that line box, in tree order:

        1. background color of element.

So we paint the background of both pseudo elements in the step (4) then the background of the span and it's content in the step (7) making the salomon color on the top and no more behind.

Related