To better understand this, let's first add the missing cases.
.flexy {
display: inline-flex;
}
.noname::before {
content: ' has ';
}
.noname-after::after {
content: ' has ';
}
span {
border:1px solid;
}
<div class="flexy">
A girl <span class="noname">no name</span>
</div>
<br>
<div class="flexy">
A girl <span class="noname"><span>no name</span></span>
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"><span>no name</span></span> some text after
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"></span><span>no name</span> some text after
</div>
<br>
<div class="flexy">
Jon Snow
<span> knows </span>
<span>nothing</span>
</div>
We can clearly see that we are missing the leading space when where are using before and the trailing one when using after and both of them if only after (or before) is inside the span.
In other words, we are missing the spaces at the edges of all the <span> elements (with or without the use of pseudo element) and this is logical because it's the default behavior of white-space. From the specification:
As each line is laid out,
- If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
- All tabs (U+0009) are rendered as a horizontal shift that lines up the start edge of the next glyph with the next tab stop. Tab stops occur at points that are multiples of 8 times the width of a space (U+0020) rendered in the block's font from the block's starting content edge.
- If a space (U+0020) at the end of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is also removed.
- If spaces (U+0020) or tabs (U+0009) at the end of a line have 'white-space' set to 'pre-wrap', UAs may visually collapse them.
Now you will tell me that we are dealing with span elements and we don't have block and new lines but we are inside a flex container thus each element will get blockified and the above rules applies here.
The display value of a flex item is blockifiedref
Worth to note that the pseudo element aren't direct child of the flex container so they are still inline element inside block element .If we change them to block element (using inline-block1 for example) you will see that the spaces will always disappear:
.flexy {
display: inline-flex;
}
.noname::before {
content: ' has ';
display:inline-block;
}
.noname-after::after {
content: ' has ';
display:inline-block;
}
span {
border:1px solid;
}
<div class="flexy">
A girl <span class="noname">no name</span>
</div>
<br>
<div class="flexy">
A girl <span class="noname"><span style="display:inline-block;">no name</span></span>
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"><span>no name</span></span> some text after
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"></span><span>no name</span> some text after
</div>
<br>
<div class="flexy">
Jon Snow
<span> knows </span>
<span>nothing</span>
</div>
Another important thing to note is how flexbox handle spaces:
Each in-flow child of a flex container becomes a flex item, and each contiguous sequence of child text runs is wrapped in an anonymous block container flex item. However, if the entire sequence of child text runs contains only white space (i.e. characters that can be affected by the white-space property) it is instead not rendered (just as if its text nodes were display:none).
Now we can clearly understand what is happening. Inside our flex container we first create the flex items by removing all the extra spaces between them. Then each flex item become a block level element and inside we apply the white space algorithm that removes the trailing and leading spaces inside that block.
You can change the white-space property if you want to keep the trailing and leading spaces:
.flexy {
display: inline-flex;
}
.noname::before {
content: ' has ';
}
.noname-after::after {
content: ' has ';
}
span {
border:1px solid;
white-space:pre;
}
<div class="flexy">
A girl <span class="noname">no name</span>
</div>
<br>
<div class="flexy">
A girl <span class="noname"><span>no name</span></span>
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"><span>no name</span></span> some text after
</div>
<br>
<div class="flexy">
A girl <span class="noname-after"></span><span>no name</span> some text after
</div>
<br>
<div class="flexy">
Jon Snow
<span> knows </span>
<span>nothing</span>
</div>
Here is another example to better understand what is happening step by step:
span {
border:1px solid;
}
div {
border:1px solid red;
margin:10px 0;
}
Spaces are removed from the start and the end, spaces inside are kept (contiguous spaces are collapsed to only one space)
<div>
<span> text inside span </span> text without span<span> text inside span </span>
</div>
I made the last span inline-block so its first space inside is removed
<div>
<span> text inside span </span> text without span<span style="display:inline-block"> text inside span </span>
</div>
Spaces between flex items are removed (We have 3 items and one inside an anonymous block) then first and last space inside each flex items are also removed
<div style="display:flex;">
<span> text inside span </span> text without span<span> text inside span </span>
</div>
We change the white space algorithm and now all the spaces are kept
<div style="display:flex;white-space:pre;">
<span> text inside span </span> text without span<span> text inside span </span>
</div>
1: This value causes an element to generate an inline-level block container. The inside of an inline-block is formatted as a block box, and the element itself is formatted as an atomic inline-level box.