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>
Related
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.
W3C's CSS2.1 specification, chapter 8.6 The box model for inline elements in bidirectional context, states:
For each line box, UAs must take the inline boxes generated for each
element and render the margins, borders and padding in visual order
(not logical order).
When the element's 'direction' property is 'ltr', the left-most
generated box of the first line box in which the element appears has
the left margin, left border and left padding, and the right-most
generated box of the last line box in which the element appears has
the right padding, right border and right margin.
Questions
It says "left-most generated box", which indicates that the inline-level element creates more than one inline-level box. Is that because at each line break it creates a new anonymous inline-level box?
Why does the padding/border of inline boxes behave as below? Insight into why it shows up the way it does, with basis in the CSS specification, would be greatly appreciated.
.test {
margin: 0 10px;
padding: 20px;
border: 5px solid blue;
}
<span class="test">test2test2test2test2test2test2test2 test2test2test2test2test2test2test2test2test2test2 test2test2test2test2test2test2test2test2test2test2test2test2test2test2test2test2test2</span>
For the second question you may 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.)
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.
.test {
margin: 0 10px;
padding: 20px;
border: 5px solid blue;
}
div {
border:1px solid red;
margin:50px 0;
}
<div>
<span class="test">test2test2test2test2test2test2test2 test</span>
</div>
<div>
<span class="test">test2test2test2test2test2test2test2 test st2test2test2test2 test st2test2test2test2 test st2test2test2test2 test</span>
</div>
<div>
<span class="test" style="line-height:50px;">test2test2test2test2test2test2test2 test2test2</span>
</div>
<div>
<span class="test" style="line-height:50px;">test2test2test2test2test2test2test2 test2test2 test2test2test2test2test2test2test2 test2test2</span>
</div>
To answer the first question:
It says "left-most generated box", which indicates that the inline-level element creates more than one inline-level box. Is that because at each line break it creates a new anonymous inline-level box?
Yes, but that's only one reason. An inline-level element can create zero, one or many inline-level boxes. So an inline element with no content or horizontal padding, border, or margin will create zero inline-level boxes. A different way in which an element can create multiple inline-level boxes is if the element contains child elements.
So if we have <span>foo <b>bar</b> baz</span>, then even if that all sits on one line, the span will create one inline-level box for foo, which will get the left margin, left border and left padding, and a separate inline-level box for baz, which will get the right margin, right border and right padding.
The b element creates the inline-level box for bar, which may have its own margins, borders, and paddings.
See Temani's answer for your second question.
vertical-align:bottom, means the bottom inline-box matches the bottom of its line-box, so in my case, the inline-box of span2 is the green one, whose line-height is 100px, inherited from its parent. Its line-box is the black one, also has line-height:100px. so they are bottom aligned.
see pic:
I've already learned that:
1.vertical-align works only for inline/inline-block element
2.vertical-align is based on line-height, not the height of its container!
3.in a line-box, its line-height is the line-height of the inline box(in my opinion, its either inline-element or inline-block element) which has the highest line-height. like pic:
Everything works fine on inline-block element, but it seems that there is a problem with inline-element.
explanation:
parent: height:200px, line-height;100px;
div.child:inline-block, vertical-align bottom;
span1: inline, line-height:inherit from parent, which is 100px
span2: inline, line-height:inherit from parent, which is 100px, vertical align: bottom.
To me, the line-box for the code below is like this(you can run the code first):
strange behavior for span2 !!! and it becomes even stranger if I set its vertical-align to text-top or text-bottom
another thing which I found interesting is, if I set display of span to inline block or set the line-height of span to normal (which is 1.16 of its font-size), everything works fine.
can someone explain it? Thanks
div.parent {
width: 300px;
background-color: coral;
/*key-part*/
height: 200px;
line-height: 100px;
}
div.child {
width: 50px;
height: 50px;
background-color: yellow;
/*key-part*/
display: inline-block;
vertical-align: bottom;
line-height: normal;
}
.span1 {
background-color: white;
font-size: 50px;
/*key-part*/
vertical-align: middle;
}
.span2 {
background-color: green;
font-size: 12px;
/*key-part*/
vertical-align: top;
}
<body>
<div class="parent">
<div class="child">inline-block div</div>
<span class="span1">Text1</span>
<span class="span2">Text2</span>
</div>
</body>
Let's try to cover it step by step:
1.vertical-align works only for inline/inline-block element
Vertical-align applies to inline-level elements. That's currently: inline, inline-block, inline-table, inline-flex, inline-grid. Vertical-align is also used, but in a different way, for table cells.
2.vertical-align is based on line-height, not the height of its container!
Except for table cells, correct.
3.in a line-box, its line-height is the line-height of the inline box(in my opinion, its either inline-element or inline-block element) which has the highest line-height.
That's correct for simple cases but not for complex alignment ones. A better approximation goes something like this. Remove all the elements that are vertical-align:top and vertical-align:bottom. Align all the other elements so that their vertical alignment lines are level with one another. Call the box that contains them from the highest top of the aligned elements to the lowest bottom of the aligned elements the proto-line box. The actual height of the line box is then the maximum of the height of the proto-line box and all of the heights of the elements that are aligned top and bottom.
Now the relevant part of the specification for your question is this:
... 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.
So for the span2, the green background area is the box's glyphs and above that are each glyph's upper half-leading, a value which is taken from the 100px line-height inherited from the container block element. It's the top of these half-leadings that aligns with the top of the line-box, not the top of the green background area.
On the other hand, the inline-block div aligns to the bottom of the line box, by the bottom of its bottom margin, and not by any half-leading.
I want to set margin-top to <small> tag. In other word I how can I increase the space of <small> tag with the upper element?
Note: I can increase the space using line-height, But I just want to set space from the top, not both top and bottom.
small{
margin-top: 100px;
color: #999;
}
<div>It is a test<div>
<small>this need to some margin-top</small>
How can I do that?
Box Model - 8.3 Margin properties
Margin properties specify the width of the margin area of a box. The 'margin' shorthand property sets the margin for all four sides while the other margin properties only set their respective side. These properties apply to all elements, but vertical margins will not have any effect on non-replaced inline elements.
A small tag is inline by default. As stated by the spec above, vertical margins do not apply to strictly inline level elements.
You could change the display to inline-block, and it would work as expected:
small {
margin-top: 100px;
color: #999;
display: inline-block;
}
<div>It is a test<div>
<small>this need to some margin-top</small>
In the fiddle - http://jsfiddle.net/dddaaLwL/ you can see that horizontal margins have an effect on the inline elements. and vertical margins do not have an effect. Is this right? why so?
#s1 {
margin-left: 40px;
}
#s2 {
margin-top: 40px;
}
Yes your fiddle is correct. why?
An inline element occupies only the space bounded by the tags that
define the inline element.
More info HERE
Examples of Inline Elements:
<a>,<span>,<b>,<em>,<i>,<cite>,<mark>, and <code>
More about Inline Elements
An inline element has, but may not be limited to, the following characteristics:
Flows along with text content, thus
Will not clear previous content to drop to the next line like block
elements Is subject to white-space settings in CSS
Will ignore top and bottom margin settings, but will apply left and
right margins, and any padding
Will ignore the width and height properties
If floated left or right, will automatically become a block-level
element, subject to all block characteristics
Is subject to the vertical-align property
More info HERE
Actually, vertical margins do have an effect on inline elements, but because the element above it isn't a block, it's actually using the margin from the top of the page instead of the previous element. Let's take a quick look at the box model:
Because inline elements don't handle bounding in the same way that block type elements do. They can only hold data, and other inline elements, and they follow the constraints of the parent element, and only exert influence over their individual space.
However, if they were inline-block elements, you'd see a different result:
As you can see, inline-block elements can actually influence the behaviour of other elements, whereas inline elements do not.
See Also:
MDN Documentation on Inline Elements
MDN Documentation on Block-level Elements
Have a look at below jsfiddle
#s1 {
margin-left: 40px;
}
#s2 {
margin-top: 100px;
}
<span id="s1"> span 1 </span>
<br>
<br>
<span id="s2"> span 2 </span>
<p id="s2"> p 3 </p>
p and div elements are block level elements where span is an inline element and hence margin on span wont work.