Why don't inline-flex containers' margins collapse? - css

I understand that items within a flex container obey different rules for margin collapsing than they normally would (i.e. when not within a flex wrapper). However, the container itself's margins should still behave normally, right? In other words, why are the margins different in the two containers in this CodePen?
border collapse example
h1, h2 {
margin: 2rem 0;
}
.inline-flexed,
.flexed {
margin: 2rem 0;
align-items: center;
}
.inline-flexed {
display: inline-flex;
}
.flexed {
display: flex;
}
.left {
display: inline-block;
width: 2rem;
height: 2rem;
background-color: blue;
}
.right {
line-height: 1.5;
background-color: lightgray;
}
<h1>I have margins!</h1>
<span class="inline-flexed">
<span class="left"></span>
<span class="right">My container is inline-flex'ed. For some reason my parent's container's margins don't collapse.</span>
</span>
<h2>I also have margins!</h2>
<span class="flexed">
<span class="left"></span>
<span class="right">My container is flex'ed. My parent's container's margins collapse as you'd expect.</span>
</span>
<h2>Is this a browser bug?</h2>

inline-flex generates a flex container that is inline-level element. Margin of inline-* elements does not collapse.
It is documented here.
Inline-flex
This value causes an element to generate a flex container box that is inline-level when placed in flow layout. w3
Two margins are adjoining if and only if:
Both belong to in-flow block-level boxes that participate in the same block formatting context.
no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
both belong to vertically-adjacent box edges. w3
Similar question
Margin collapsing in flexbox
More info
https://www.joshwcomeau.com/css/rules-of-margin-collapse/
https://www.smashingmagazine.com/2019/07/margins-in-css/

Related

::before element doesn't show up without display:flex

With below Code
.quote {
display: flex;
/*?? without this, ::before doesn't show up*/
}
.quote::before {
content: "";
min-width: 8px;
background-color: #F7DF94;
}
<div class="quote">
Hello World
</div>
I am not sure why the ::before element won't show up if I remove the display: flex.
Code snippet in JSbin here
The :before element doesn't "show up" because the default display property acts like display: inline; and you cannot set the with or height of an inline element.
Definition of width on MDN :
Applies to: all elements but non-replaced inline elements, table rows,
and row groups
[reference] (emphasis mine)
And the same goes for height, see here
When you set display: flex; on the parent, the children act like flex items that can have a width / height.
The initial value of align-items for flex items is normal (behaves like stretch in this context see here). This value makes the items take 100% height.
If you don't want to use display: flex; on the parent, you could change the default property of the pseudo element to display: inline-block; but you will need to specify a height otherwise it won't display either. Note that there will be a white-space between the pseudo element and the content.
Here is an example :
.quote::before {
display:inline-block;
content: "";
min-width: 8px;
min-height: 8px;
background-color: #F7DF94;
}
<div class="quote">
Hello World
</div>

CSS Flexbox with position absolute confusion

I'm confused how this works could someone walk me through what exactly happened?
body,
html {
padding: 0;
margin: 0;
height: 100vh;
width: 100%;
}
.chat-break {
position: relative;
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}
.chat-break .line {
border-bottom: 1px solid #ddd;
width: 100%;
}
.chat-break .date {
color: #B5B5B5;
position: absolute;
background-color: #fff;
padding-left: 8px;
padding-right: 8px;
}
<div class="chat-break">
<div class="line">
</div>
<p class="date">Today</p>
</div>
My understanding:
.chat-break flexbox has two elements .line and .date.
...but after using position: absolute on .date its no longer its own element inside the flexbox?
Then why is "Today" being centered on the line?
Preface:
The top, right, bottom, and left properties are collectively referred to as "box inset" properties.
Keep in mind that all CSS properties that aren't explicitly set will have default values that are either "initial" values or inherited values.
So unless explicitly specified otherwise, every element has position: static;.
The initial value for all of the box-inset properties is auto, regardless of their element's position and display property.
But the behaviour of the auto value does depend on the element's position and/or formatting context.
CSS has a lot of surprising and counter-intuitive behaviour.
This is not intentional, but a consequence of how CSS evolved over time over the past ~25 years it's existed.
Originally CSS wasn't capable of any layout control at all, it was only useful for things like font and text styling.
In fact, CSS's main layout features (like flex, grid, etc) weren't added (let alone supported by browsers) until well into the 2010s.
For example, the original 2008 spec for flex was abandoned. It was reinvented in 2011, and not widely supported until 2014.
Why <div class="line"> fills the width of the page:
<div class="chat-break"> has display: flex;, which makes it a flex-parent.
All immediate child elements (with certain exceptions) of a flex-parent, that have position: static, are flex-items.
Therefore<div class="line"> is a flex-item.
Therefore<p class="date"> is not a flex-item because it has position: absolute;.
(Generally speaking) elements with position: absolute; are not subject to their layout-container's layout rules and are re-positioned with absolute-coordinates where the origin is in their-closet-ancestor-element-without-position: static; (yes, that's a mouthful).
This is why position: relative; is being applied to <div class="chat-break">.
Therefore <div class="chat-break"> is a flex-parent with only one flex-item, despite having two element children.
And because it has justify-content: center; it means that its single flex-item (<div class="line">) will be centered.
Also, because <div class="chat-break"> has display: flex; (rather than display: inline-flex) it means that <div class="chat-break"> is a block-level element, so it fills the width of its container, which is <body>, which fills the width of the viewport.
And because <div class="line"> also has width: 100%; (which becomes flex-basis: 100%;) it means the <div class="line"> will fill the width of <div class="chat-break">.
Therefore <body>, <div class="chat-break">, and <div class="line"> (in that order) will fill the width of the viewport.
Why <p class="date"> is centered:
As <p class="date"> uses auto for all its box inset properties (i.e. top, right, etc) with position: absolute; then the computed value of those properties is the same as if <p class="date"> was position: static;.
However, if <p class="date"> was position: static; then it would be a flex-item and would share its flex row with <div class="line"> - which would mean it would be located somewhere off to the right of the line (due to justify-content: center;).
But it's actually centered, because this is a special-case scenario that's specifically specified in the specification...
https://www.w3.org/TR/css-flexbox-1/#abspos-items
4.1. Absolutely-Positioned Flex Children
As it is out-of-flow, an absolutely-positioned child of a flex container does not participate in flex layout.
The static position of an absolutely-positioned child of a flex container is determined such that the child is positioned as if it were the sole flex item in the flex container, assuming both the child and the flex container were fixed-size boxes of their used size. For this purpose, auto margins are treated as zero.
It's well-worth reading the rest of the section as it also covers other brain-stretching scenarios, like "what if align-self: center; is used"?

alignment box for inline-block non-replaced elements

Please run the demo:
* {
margin: 0;
padding: 0;
}
.body {
font-family: Microsoft Yahei;
font-size: 16px;
background-color: lightblue;
height: 400px;
width: 400px;
line-height: 2;
vertical-align: baseline;
}
.body span {
background-color: pink;
}
.body .inline-block {
display: inline-block;
background: orange;
height: 50px;
}
.inline-block.text {
vertical-align: text-top;
}
<div class="body">
<span>
words-g words words-g
<span class="inline-block text">with inline-block box child</span> words-g w
</span>
</div>
The point is that I set
.inline-block.text {
vertical-align: text-top;
}
According to the specification:
In the following definitions, for inline non-replaced elements, the box used for alignment is the box whose height is the 'line-height' (containing the box's glyphs and the half-leading on each side, see above). For all other elements, the box used for alignment is the margin box.
and in the section 'line-height':
On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut." (The name is inspired by TeX.).
So,in this case,.inline-block.textis a
block container element whose content is composed of inline-level elements
whose height is 50px and line-height is 32px
also is an inline non-replaced elements
And here is my question:
the box used for alignment is the box whose height is the 'line-height'
What is the above box point at in this case for .inline-block.text?
As the demo shows,I think it is the box with height 50px. However,the box's height is not the line-height which conflicts with the specification above. So,I was confused and don't understand the above sentence in the specification.
And if you think the above box is the box with height 50px,how do you explain the fact that height 50px is not the line-height 32px?
Please notice:
I just want to understand this sentence which is the box used for alignment is the box whose height is the 'line-height',so I can understand the vertical-align better.
I am not asking for a specific solution.
Whatever thanks for your help!
The statement
for inline non-replaced elements, the box used for alignment is the box whose height is the 'line-height'
does not apply to inline-blocks. Inline-blocks are not inline elements. Inline elements are elements with display: inline, and generate inline boxes. Inline-blocks are not inline boxes, but inline-level (the "-level" part is important!) block container boxes. Therefore, the statement
For all other elements, the box used for alignment is the margin box.
applies instead, which results in vertical-align: text-top causing the top outer edge of the inline-block to align with the top of the line box.
Any part of the specification that is said to apply to inline elements does not apply to inline-blocks.
I guess you are confusing about the reference of the alignment (it aligns relatively to what?).
I will try to explain this with easy words. When using vertical-align with an element a you align it relatively to its parent element b whataver is the height of a (b is the reference). Using correct words it's like this:
The vertical-align property can be used in two contexts:
To vertically align an inline element's box inside its containing line
box. For example, it could be used to vertically position an <img> in
a line of text.ref
So the a element is the inline element's box and the b element is the containing line box and the height of b is defined by it's line-height as you already read in the specification.
Now let's consider your code and add properties step by step.
Initially let's remove the inline-block
.body {
font-family: Microsoft Yahei;
font-size: 16px;
background-color: lightblue;
}
.body span {
background-color: pink;
}
.body .inline-block {
background: orange;
}
.inline-block.text {
vertical-align: text-top;
}
<div class="body">
<span>
words-g
<span class="inline-block text">inline-block</span> words-g w
</span>
</div>
As you can see the inner span has the same height/line-height as the outer span and both are using the same font-family. So logically we see nothing when using text-top as vertical alignment.
Now let's add line-height:2 to the container:
.body {
font-family: Microsoft Yahei;
font-size: 16px;
background-color: lightblue;
line-height:2;
}
.body span {
background-color: pink;
}
.body .inline-block {
background: orange;
}
.inline-block.text {
vertical-align: text-top;
}
<div class="body">
<span>
words-g
<span class="inline-block text">inline-block</span> words-g w
</span>
</div>
In this situation, both span will inherit the line-height:2 thus the computed value will be 32px (2 * font-size) and this will make the top reference different from text-top. To remind about this, here is a figure I shared with your beforeref:
And if we read the definition about the value text-top of vertical-align:
Aligns the top of the element with the top of the parent element's
font.
So the top of the inner span will align with the text-top of the outer span, that's why it moved to the bottom. Then the height of the main container .body will not be equal to 32px but it will be bigger because it will consider the movement of inner span (we will have 37px).
Now let's add inline-block to the inner element:
.body {
font-family: Microsoft Yahei;
font-size: 16px;
background-color: lightblue;
line-height:2;
}
.body span {
background-color: pink;
}
.body .inline-block {
background: orange;
}
.inline-block.text {
vertical-align: text-top;
display:inline-block;
}
<div class="body">
<span>
words-g
<span class="inline-block text">inline-block</span> words-g w
</span>
</div>
The first thing you will notice is that the text didn't move BUT the orange background is covering a bigger height. This is because our element will behave as block container and this height is the line-height of the text (32px) which is also the distance between the top and bottom in the image above (Initially it was covering from text-bottom to text-top).
It's also like the blue background of the .body element since this one is a block element. Try to make the .body element inline and see what will happen.
Now you can also add a specific height to the element and nothing will change because we align relatively to the parent element. You can also play with all the vaues of vertical-align to see the different behaviors:
.body {
font-family: Microsoft Yahei;
font-size: 16px;
background-color: lightblue;
line-height:2;
margin:5px;
}
.body span {
background-color: pink;
}
.body .inline-block {
background: orange;
}
.inline-block.text {
display:inline-block;
height:50px;
}
<div class="body">
<span>
Align the
<span class="inline-block text" style="
vertical-align: text-top;">top of this</span> with text-top
</span>
</div>
<div class="body">
<span>
Align the
<span class="inline-block text" style="
vertical-align: top;">top of this</span> with top
</span>
</div>
<div class="body">
<span>
align the
<span class="inline-block text" style="
vertical-align: text-bottom;">bottom of this</span> with text-bottom
</span>
</div>
<div class="body">
<span>
align the
<span class="inline-block text" style="
vertical-align: bottom;">bottom of this</span> with bottom
</span>
</div>

In flexbox, why do we define the container and not the elements themselves?

I'm trying to understand the flex property.
Why do we apply float and display: inline-block to the elements that are nested inside a container, while with display: flex, we define the container itself?
How can the elements inside be display:block and still be positioned on the same row?
.flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
flex-direction: row;
}
#media (max-width: 600px) {
.flex {
flex-direction: column;
}
}
#aaa {
border: 3px solid black;
flex: 2;
}
#aa {
border: 3px solid black;
flex: 1;
}
#a {
border: 3px solid black;
flex: 1;
}
#bbb {
width: 300px;
}
#ccc {
float: left;
border: 5px solid yellow;
width: 200px;
}
#ddd {
clear: both;
}
.one {
background: red;
height: 50px
}
.two {
background: green;
height: 50px
}
.tree {
background: blue;
height: 50px
}
#eee {
display: inline-block;
width: 200px;
}
<div class=flex>
<div id=a class="one">flex</div>
<div id=aa class="two">flex</div>
<div id=aaa class="tree">flex</div>
</div>
<div class=block>
<div id=bbb class="one">block</div>
<div id=bbb class="two">block</div>
<div id=bbb class="tree">block</div>
</div>
<div class=float>
<div id=ccc class="one">float</div>
<div id=ccc class="two">float</div>
<div id=ccc class="tree">float</div>
</div>
<div class=block>
<div id=ddd class="one">block</div>
<div id=ddd class="two">block</div>
<div id=ddd class="tree">block</div>
</div>
<div class=block>
<div id=eee class="one">block</div>
<div id=eee class="two">block</div>
<div id=eee class="tree">block</div>
</div>
CodePen demo
In flexbox, why do we define the container and not the elements themselves?
The reason is twofold:
Flex containers are the only things flex items can appear as children of, since only flex containers generate flex layout.
Unlike anonymous block boxes and anonymous table boxes, there are no such things as anonymous flex container boxes.
So, a hypothetical display: flex-item wouldn't work unless the element's parent was a flex container, and since every child of a flex container automatically becomes a flex item anyway, this makes such a display type completely redundant.
Block-level and inline-level boxes, on the other hand, exist in many, many forms. Even flex containers can appear in block-level and inline-level forms, as display: flex and display: inline-flex respectively. Furthermore, display: block and display: inline-block actually have a lot in common, as they are both block containers. The only difference is that one is block-level and the other is inline-level (and the latter always generates a block formatting context, but that's not pertinent here).
So display: block and display: inline-block are actually very similar to display: flex and display: inline-flex respectively in that regard (see Difference between display:inline-flex and display:flex), the difference being that the former pair handles block layout or inline layout (see section 9.2 of the CSS2 spec), and the latter pair handles exclusively flex layout.
If you're asking why flexbox was designed this way, that's something only the CSSWG can answer with certainty, but I can provide an educated guess based on what I've stated above: Since block-level and inline-level boxes can exist in so many different forms for various internal layout types (block, table, flex, grid, etc), defining flex-level counterparts for every single layout type would become extremely unwieldy, even if they did introduce a concept of anonymous flex container boxes which would allow elements to exist as flex items in their own right. This is why css-display-3 redefines the display property to take the form of <display-outside> <display-inside> along with special and legacy values — to accommodate new layout types without having to redefine entire sets of keywords to go along with them.
Your flex items are display: block simply because that's their specified value of display. But they are laid out as flex items, which always obey a set of rules in flex layout, which are loosely based on a combination of various elements of block and inline layout without falling squarely within the domain of either one. This is similar to how a float or an absolutely positioned element cannot be inline even if you specify display: inline or display: inline-block — because floats and absolutely positioned elements always participate in block layout, never inline layout.
Speaking of floats, FYI, floats participate in block formatting contexts, and are therefore part of a certain subset of block layout. They follow a float model, but the float model is integrated with the rest of block layout rather than existing as a completely separate layout type.

Unknown offset. (inline-flex element + clearfix) [duplicate]

I have a weird behaviour of an inline-flex element when applying a clearfix to it. When I set a clearfix to an element which has an inline-flex display property the strange white space appears before it:
But when the inline-block is used the behaviour is different:
I don't understand why inline-flex has a different behaviour than inline-block.. and why it has that weird space.
.a,
.b {
border: 1px solid red;
}
.a {
text-align: center;
}
.b {
display: inline-flex;
height: 20px;
width: 20px;
}
.cf:before,
.cf:after {
content: " ";
display: table;
}
.cf:after {
clear: both;
}
<div class="a">
<div class="b cf"></div>
</div>
JSFiddle Demo
Try set a vertical-align: top to your inline-flex | inline-block element to fix this offset.
https://jsfiddle.net/jeca65my/2/
Thank's to #NenadVracar on this solution
display: inline-flex
When you use display: inline-flex, you establish a flex container.
An initial setting of a flex container is flex-direction: row.
This means that all in-flow child elements of the container (including in-flow pseudo-elements) will line up in a row. The display value of these children (table, in this case) is overridden/ignored, in accordance with the rules of a flex formatting context.
Your flex container has two flex items (the pseudo-elements) in one line:
.a,
.b {
border: 1px solid red;
}
.a {
text-align: center;
}
.b {
display: inline-flex;
height: 20px;
width: 20px;
}
.cf:before,
.cf:after {
content: "x";
display: table;
}
.cf:after {
clear: both;
}
<div class="a">
<div class="b cf"></div>
</div>
display: inline-block
When you use display: inline-block, you establish a block formatting context.
The display property of child elements is respected.
Your pseudo-elements with display: table are block elements which, by default, occupy the full available width. Hence, the pseudos are creating two rows:
.a,
.b {
border: 1px solid red;
}
.a {
text-align: center;
}
.b {
display: inline-block;
height: 20px;
width: 20px;
}
.cf:before,
.cf:after {
content: "x";
display: table;
}
.cf:after {
clear: both;
}
<div class="a">
<div class="b cf"></div>
</div>
vertical-align: baseline
Because both versions of your code use inline-level display values, this calls into play the vertical-align property, who's initial value is baseline.
The white space you are seeing below div.b when set to display: inline-flex is due to baseline alignment.
The white space you are seeing below div.b when set to display: inline-block is due to baseline alignment in combination with the effects of two block element children.
Here is a more detailed explanation:: https://stackoverflow.com/a/36975280/3597276
The clear property
.cf:after {
clear: both;
}
Your clearfix method is not the source of any of the white space. In fact, it's having no effect on your layout and can be safely removed.
You use the clear property only when dealing with floats.
From the spec:
9.5.2 Controlling flow next to floats: the clear
property
This property indicates which sides of an element's box(es) may not be
adjacent to an earlier floating box.
Not only are there no floated elements in your layout, but if there were, the float and clear properties are nonetheless ignored in a flex formatting context.
3. Flex Containers: the flex and inline-flex display
values
float and clear do not create floating or clearance of flex item, and do not take it out-of-flow.
You have to imagine your page as a flow. Every element of your page is in the flow (DOM). You are using the position property to change the position in the flow.
block
A block element will Always start a new line. (ie: div)
inline-block
Inline blocks elements are blocks like div but with inline properties in it. (ie: span)
inline-flex
This is used the same way as inline-block in the flow. It makes a container that is inline but with the flex layout.
For your example, an interesting thing to do in order to focus on the difference between inline-block and inline-flex is to add text in your child div. You'll see that the comportment of your child div will once again change because it has text in it. JSFiddle example
EDIT : I found a sentence on SO that resumes well the situation. thank's to #BoltClock on This post :
display: inline-flex does not make flex items display inline. It makes the flex container display inline.

Resources