Why is width handled differently in flexbox? - html

I've been playing around with display:flex lately as I see it becoming more trending. In a quick test, using flex and non-flex CSS approaches, I realized, my widths, and margins are always respected when using flex. Is this a good approach considering that I will most likely need a gutter between elements anyway? Moreover, where is the margin between spans coming from? This is what I mean:
HTML
<div class="container">
<div class="box">
<span class="inner-box">This gives me a 30px X 60px</span>
<span class="inner-box">This gives me a 30px X 60px</span>
<span class="inner-box">This gives me a 30px X 60px</span>
</div>
<div class="box2">
<span class="inner-box">This gives me a width larger than the specified 30px</span>
<span class="inner-box">This gives me a width larger than the specified 30px</span>
<span class="inner-box">This gives me a width larger than the specified 30px</span>
</div>
</div>
CSS
.box{
background: orange;
display:flex;
flex-direction:row-reverse;
padding:0 10px;
}
.box2{
background: green;
text-align:right;
padding:0 10px;
}
.inner-box{
width:30px;
background:#fff;
margin:0 0px;
}
Notice the widths at runtime
DEMO

You are using <span> elements. They are inline by default. The width is automatically ignored for inline elements.
In the first div section (.box), however, the span's inline display value is overridden by the parent's display: flex, which establishes a flex formatting context. Hence, all children become flex items, which respect width and height.
Flex Items
A flex item establishes a new formatting context for its contents. The
type of this formatting context is determined by its display value,
as usual. However, flex items themselves are flex-level boxes, not
block-level boxes: they participate in their container’s flex
formatting context, not in a block formatting context.
source: https://drafts.csswg.org/css-flexbox-1/#flex-items
In the second div section (.box2), the span elements remain display: inline, as they are not removed from the block-formatting context, and any width and height assignments are ignored.
Try display: inline-block: http://codepen.io/anon/pen/yeggOy
References:
Setting the width of inline elements
How to set width of a inline element?
Does height and width not apply to span?
Inline Elements With Width

Your question is a little unclear but if, as MrLister commented:
If display:flex honors width, where display:inline doesn't? If so, the answer is yes,.
This is because, per the spec,
Flex items paint exactly the same as inline blocks
and so are affected by width statements.

Related

How to determine height of content-box of a block and inline element

I thought 'what determines height of content-box' and thought line-height might.
It seems to be the case you can determine it for block elements with line-height.
<p style="background-color: grey; font-size: 16px; line-height: 2em;">Is line-height same as height of p tag?</p>
<div style="background-color: lightsalmon; font-size: 16px; line-height: 2em;">Is line-height same as height of div tag?</div>
<span style="background-color: aquamarine; font-size: 16px; line-height: 2em;">Is line-height same as height of span tag?</span>
Inline elements seems a different story. Would someone be able to elaborate?
For block elements its quite easy but you need to distinguish between two different cases. When we have an IFC (inline formatting context) or BFC (block formatting context). From the specification you can read
If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:
The element's height is the distance from its top content edge to the first applicable of the following:
the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin does not collapse with the element's bottom margin
the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
zero, otherwise
Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset)
If we consider an IFC then the line boxes will define our height exactly like your examples where in the div and p you have one line box defined by the line-height.
If we consider a BFC then the (2) and (3) apply. Here, you can see it as a recursive definition because having a BFC means we have other blocks inside and those blocks will follow the same rules (either BFC or IFC and so on). In addition, we consider the margin collapsing rules to get the final height.
If the block is empty then it's zero.
Of course, this consider the case of height:auto. If you explicitely set a height then it's trivial.
For inline elements you can refer to this part of the specification:
The 'height' property does not apply. The height of the content area should be based on the font, but this specification does not specify how. A UA may, e.g., use the em-box or the maximum ascender and descender of the font. (The latter would ensure that glyphs with parts above or below the em-box still fall within the content area, but leads to differently sized boxes for different fonts; the former would ensure authors can control background styling relative to the 'line-height', but leads to glyphs painting outside their content area.)
Here is a bit tricky but the rule is simple: you cannot control or set the height of the content area. Only the font properties you will be using will define the final height.
You should also note that the content area is different from the line box.
The vertical padding, border and margin of an inline, non-replaced box start at the top and bottom of the content area, and has nothing to do with the 'line-height'. But only the 'line-height' is used when calculating the height of the line box.
<span style="background-color: aquamarine; font-size: 16px; line-height: 2em;">Is line-height same as height of span tag?</span>
<br>
<span style="background-color: aquamarine; font-size: 16px; line-height: 4em;">Is line-height same as height of span tag?</span>
<br>
<span style="background-color: aquamarine; font-size: 16px; line-height: 8em;">Is line-height same as height of span tag?</span>
If you increase the line-height you will clearly notice that the background won't cover a bigger area but only the line box will be bigger
Some related question to get more details and understand the difference between line box and content area:
Can specific text character change the line height?
Box Model for Inline Elements
Why is there space between line boxes, not due to half leading?
So line-height will indeed define the height of block element and not inline element since the block element height will depend on the height of the line boxes and the height of line boxes are defined with line-height1
Here is an example to show a block element having its height defined by the line box and the content area of the inline element inside will oveflow because they play no role in defining the line boxes:
p {
margin:50px;
font-size:35px;
border:1px solid red;
line-height:0;
}
span {
background:green;
line-height:10px;
}
<p>
<span>some text here and there</span>
</p>
<p>
<span style="font-family:cursive">some text here and there</span>
</p>
In both examples, we have a height equal to 12px for the p (10px of line-height + 2px of border) and our inline element is having a different height in each case because the font is not the same.
1: We should note that in practise it can be more complex and the value of line-height alone isn't enough.
If we read the specficiation we can see that:
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.
Then
On a non-replaced inline element, line-height specifies the height that is used in the calculation of the line box height.
That's why in the last example I made the line-height of the block element to be 0 so only the line-height of the inline elements inside will define the final height of the linebox
Even the vertical alignment play a role here because if we have multiple elements not aligned the same way and with different line-height, finding the result will be more complex:
p {
margin:50px;
font-size:35px;
border:1px solid red;
line-height:0;
}
span {
background:green;
line-height:1;
}
<p>
<span>AB</span> <span >AB</span>
</p>
<p>
<span>AB</span> <span style="vertical-align:super">AB</span>
</p>
To find the height of a linebox you need to consider 3 factors:
The line-height of the block container (the IFC)
The line-height of the inline elements inside
the vertical alignment of inline elements
Let's not forget that line-height is inherited so if we don't set it explicitely we need to either consider the value of the parent element or the default one.

Why do display: block and display: flex render the same element with a different height?

So I have a div with a span inside. And I'm setting display:block or display:flex on the div, and a small font-size on the span. Surprisingly, this is giving different heights on the div. See the example.
If I set the smaller font-size on the body or div then the height of both is equal. But if I set the smaller font-size on the span like in the example, then the divs get different heights. How come? And can I do anything about it?
span {
font-size: 0.8rem;
border: 1px red solid;
}
div {
border: 1px blue solid;
}
<div style="display: block;">
<span>test text 1</span>
</div>
<div style="display: flex;">
<span>test text 2</span>
</div>
In a block formatting context, the line-height property makes a difference.
This is because line-height establishes the minimum height for inline-level elements and line boxes inside block-level containers.
In a block formatting context a span is an inline-level element and line-height applies.
In the code sample, any font size on the span below 1rem will change the font-size, but not the line-height, which remains fixed. That's what you're seeing with font-size: .8rem. The height of the div doesn't change. And it won't change unless the font size exceeds 1rem.
In a flex formatting context, the span is a flex item. A flex item is always a block-level element (regardless of the element type). Flex items are "blockified", according to the flexbox spec. Because there are no inline-level elements, line-height doesn't apply.
Also, an initial setting of a flex container is align-items: stretch. This means that the span sets the height of the container.
In summary, with display: block the line-height keeps the div height fixed. With display: flex, there is no line-height interference and the div tracks the height of the span freely.
One solution: Add display: block to the span, which eliminates the line-height issue.

Flex container overflow if previous sibling is floated

If a previous sibling is set to float with a width of 100% and the following sibling set to display: flex, the latter overflow the parent container instead of wrap to a new line.
With any other display value but flex (or grid) it wraps, as it should, so how come it won't when set to flex
.float-left {
float: left;
width: 100%;
}
.display-flex {
display: flex;
background: yellow;
}
/* Demo css */
.container {
max-width: 80%;
margin: auto;
background: white;
}
<div class="container">
<div class="float-left">I'm floating left, with a width of 100%.</div>
<div class="display-flex">'Floating left' takes up 100% of the space, but still i don't go onto a new line?</div>
</div>
The reason a block box appears to wrap when its previous sibling is a float with 100% width is because it's not actually the box that's wrapping, it's its inline content that's wrapping.
The reason this doesn't happen with a flex container is because floats cannot intrude into flex formatting contexts. In fact, the same thing happens with block formatting contexts — if you apply overflow: auto or overflow: hidden to the following sibling without display: flex the following sibling will seem to disappear altogether. (This implies that the first paragraph is true only when the block box does not establish a block formatting context.)
Since your float is 100% width, the flex container's (auto) width is reduced to 0. Its inline descendants don't wrap underneath the float, because those inline descendants are participating in an inline formatting context that's within an anonymous flex item, which itself doesn't wrap since it's the only flex item in the flex container. This flex item is overflowing the flex container; however the flex container itself doesn't overflow the containing block since its used width is 0, allowing it to sit next to the float.
The reason the flex container will wrap if it is display: inline-flex instead of display: flex is because an inline-level flex container behaves just like any other inline-level content, wrapping around a float. In this case, it's the flex container itself that wraps — its inline content is still formatted as an anonymous flex item, because flex layout is identical regardless of whether the flex container itself is inline-level or block-level.
The problem is that the element .display-flex is not a flex item. It is a child element in a standard block container.
Therefore, the flex shorthand property, and its longhand component properties, which apply only to flex items, are having no effect.
However, the width property works on both flex items and containers.
More details here: What are the differences between flex-basis and width?

Why don't containers expand to fit their inline elements

I simply have a container like a div or a button which contains an inline element with padding - the container doesn't grow to fit the inline element.
button, div {
background: blue;
}
span {
background: orange;
/*display: inline-block; */ /* toggle to see container element grow */
padding: 4em;
}
hr {
margin:100px;
}
<div>
<span class="btn__content">
I'm div inline content
</span>
</div>
<hr>
<button class="btn" type="button">
<span>
I'm button inline content
</span>
</button>
Why does this happen?
NB: I'm not looking for a fix - (for instance, I already know that I could display:flex on the container and that would make the container expand to the content)
Rather, I'm looking for an explanation from a credible source like the spec which explains this behavior of inline elements.
The reason is explained in the Visual Formatting model documentation (emphasis mine):
In an inline formatting context, boxes are laid out horizontally, one
after the other, beginning at the top of a containing block.
Horizontal margins, borders, and padding are respected between these
boxes. The boxes may be aligned vertically in different ways: their
bottoms or tops may be aligned, or the baselines of text within them
may be aligned. The rectangular area that contains the boxes that form
a line is called a line box.
The width of a line box is determined by a containing block and the
presence of floats. The height of a line box is determined by the
rules given in the section on line height calculations.
The line height is then calculated as:
The height of a line box is determined as follows:
The height of each inline-level box in the line box is calculated. For
replaced elements, inline-block elements, and inline-table elements,
this is the height of their margin box; for inline boxes, this is
their 'line-height'. (See "Calculating heights and margins" and the
height of inline boxes in "Leading and half-leading".)
The
inline-level boxes are aligned vertically according to their
'vertical-align' property. In case they are aligned 'top' or 'bottom',
they must be aligned so as to minimize the line box height. If such
boxes are tall enough, there are multiple solutions and CSS 2.1 does
not define the position of the line box's baseline.
The line box height is the distance between
the uppermost box top and the lowermost box bottom. Empty inline elements
generate empty inline boxes, but these boxes still have margins,
padding, borders and a line height, and thus influence these
calculations just like elements with content.
And as MDN notes about the box model:
Note that, for non-replaced inline elements, the amount of space taken
up (the contribution to the height of the line) is determined by the
line-height property, even though the border and padding appear
visually around the content.
and reiterated by the W3 for inline non-replaced elements:
The 'height' property doesn't apply, but the height of the box is
given by the 'line-height' property.
https://www.w3.org/TR/REC-CSS2/visudet.html#q15
Where the line height is "the height that is used to calculate line box height." So while padding or borders might give the look of height to an inline element, only the line-height property will affect parent nodes. As you can see in the example below, the line height on the spans is affecting the height of the parent div.
button,
div {
background: blue;
}
span {
background: orange;
line-height: 10em;
}
hr {
margin: 100px;
}
<div>
<span class="btn__content">
I'm div inline content
</span>
</div>
<hr>
<button class="btn" type="button">
<span>
I'm button inline content
</span>
</button>
See also Inline elements and line-height
I believe it's because of these two sections:
9.4.2 Inline formatting contexts
The height of a line box is determined by the rules given in the section on line height calculations.
10.8 Line height calculations: the 'line-height' and 'vertical-align' properties
The height of each inline-level box in the line box is calculated. For replaced elements, inline-block elements, and inline-table elements, this is the height of their margin box; for inline boxes, this is their 'line-height'.
Setting a line-height does change the height of the containing box
button, div {
background: blue;
}
span {
background: orange;
/*display: inline-block; */ /* toggle to see container element grow */
padding: 4em;
line-height: 8em;
}
hr {
margin:100px;
}
<div>
<span class="btn__content">
I'm div inline content
</span>
</div>
<hr>
<button class="btn" type="button">
<span>
I'm button inline content
</span>
</button>

CSS increase width using calc() does not affect parent container

Take a look at the following snippet. How can I increase the width of the red container with 10px?
Is there a way to increase the width of an element in CSS, still affecting the parent container? Or do I have to use a pseudo-element placeholder to accomplish this instead?
<div style="
background:blue;
display:inline-block;
height:50px">
<div style="
font-size:30px;
width:calc(100% + 20px);
background:red">
Inner container</div>
</div>
In the snippet above the blue container is not affected, and the red overflows.
Is there a way to increase width of an element by a certain amount, where the width of the element itself is not known until runtime?
You can only reliably do this using padding; however, padding will affect the layout of the element's contents, which may not be what you want. In your example, padding the child element will affect the layout of the text inside it; the content width is never really increased at all.
You can't change an element's content width by a definite amount when it is not known in advance and depends on external factors such as the amount of content it actually has, which is usually the case with floats, absolutely positioned elements, and inline-blocks. The closest you can get is an auto width, and that only has a meaningful effect on in-flow block boxes — which are none of the above — with respect to their parents, not their children.
In fact, the behavior that you see with calc(100% + 20px) is not governed by the spec, although it's as far as browsers generally go without falling into the cyclic dependency trap:
<percentage>
Specifies a percentage width. The percentage is calculated with respect to the width of the generated box's containing block. If the containing block's width depends on this element's width, then the resulting layout is undefined in CSS 2.1.
(calc() is CSS3, but the box model hasn't changed much from CSS2.1.)
If I understood correctly, you want to increase the child's initial width and add another 10px.
But when you set this property:
The width: 100% will always be relative to its parent, so if you add another 10px in relation to the 100% it will always be 10px bigger than its parent.
You may want to set just a min-width and a max-width instead of setting explicitly its width so that the child will be within some specified ranges, but to increase the width by 10px from the starting witdh using JUST CSS its not possible.
Note: Keep in min that using calc you have to add the according browser-engine property extension i.e:
-webkit-calc()
-moz-calc()
calc()
<div style="
background:blue;
display:inline-block;
height:50px">
<div style="
font-size:30px;
width: 100%;
background:red">
Inner container</div>
</div>
Hope this helps in some way, gl.