'Auto' heights for block formatting context roots (inline-block) - html

My question is about the height of inline-block which contains only inline boxes
<div style="display: inline-block;
height: auto;
border: 1px solid red
"><span style="font-size: 16px;
line-height: 16px;
background-color: yellow;
">x</span></div>
In the example above I expect the following:
The height of the content area of the inline-block would be 16px because it contains only inline elements
The height of inline elements determined by "line-height" which equals 16px in my case but in reality, its height 18px
Inline-block element should create block formatting context (MDN):
A block formatting context is created by at least one of the following:
the root element or something that contains it
floats (elements where float is not none)
absolutely positioned elements (elements where position is absolute or fixed)
inline-blocks (elements with display: inline-block)
table cells (elements with display: table-cell, which is the default for HTML table cells)
table captions (elements with display: table-caption, which is the default for HTML table captions)
anonymous table cells implicitly created by the elements with display: table, table-row, table-row-group, table-header-group, table-footer-group (which is the default for HTML tables, table rows, table bodies, table headers and table footers, respectively), or inline-table
block elements where overflow has a value other than visible
display: flow-root
elements with contain: layout, content, or strict
flex items (direct children of the element with display: flex or inline-flex)
grid items (direct children of the element with display: grid or inline-grid)
multicol containers (elements where column-count or column-width is not auto, including elements with column-count: 1)
column-span: all should always create a new formatting context, even when the column-span: all element isn't contained by a multicol container (Spec change, Chrome bug).
https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context
The height of block formatting context roots which contains only inline elements defined by a height of line boxes (from spec):
10.6.7 'Auto' heights for block formatting context roots
In certain cases (see, e.g., sections 10.6.4 and 10.6.6 above), the height of an element that establishes a block formatting context is computed as follows:
If it only has inline-level children, the height is the distance between the top of the topmost line box and the bottom of the bottommost line box.
https://www.w3.org/TR/CSS2/visudet.html#root-height
Height of line box equals "line-height" (from spec)
On a non-replaced inline element, 'line-height' specifies the height that is used in the calculation of the line box height.
https://www.w3.org/TR/CSS2/visudet.html#leading
If I change "display: inline-block" on "display: inline-flex" it works as expected
Thanks

Thanks #TemaniAfif
He was right the reason of 18px for height was in line-height of div element. div's line-height specify minimal height of line boxes
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.
In my case div doesn't specify line-height property so it has default value equals normal.
Name: line-height
Value: normal | <number> | <length> | <percentage> |
inherit Initial: normal
https://www.w3.org/TR/CSS22/visudet.html#leading
If line-height value is normal then computed value depends from user agent.
But spec recommends to use value 1.0 to 1.2 of element's font-size. Looks like usually this value are equals to font-size * 1.2 but it's also depends from font's metrics like ascent, descent and em-box (so result would depends from font-family).
normal
Tells user agents to set the used value to a "reasonable" value
based on the font of the element. The value has the same meaning as
. We recommend a used value for 'normal' between 1.0 to 1.2.
The computed value is 'normal'.
If you know font's metrics like ascender and descender and font-size which you would use on the web you can calculate line-heigh by (ascender + Math.abs(descender)) * font-size / (units_per_em)
function lh(ascender, descender, unitsPerEm, fontSize) {
return (ascender + Math.abs(descender)) * fontSize / unitsPerEm;
}
In the pictures above ascender=5000, descender=-200, units_per_em=1000 and font-size=48px so line-height=(5000 + 200) * 48 / 1000 = 249.6 (lh(5000, 200, 1000, 48))
ascent, descent font's properties are shipped with font and defined by font's creator.
You can use FontDrop! service to get information about font
Some implementations details from Chromium(Webkit) relates to line-height
LayoutUnit ComputedStyle::ComputedLineHeightAsFixed() const {
const Length& lh = LineHeight();
// Negative value means the line height is not set. Use the font's built-in
// spacing, if avalible.
if (lh.IsNegative() && GetFont().PrimaryFont())
return GetFont().PrimaryFont()->GetFontMetrics().FixedLineSpacing();
https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/style/computed_style.cc#1835
FixedLineSpacing
float line_gap = SkScalarToFloat(metrics.fLeading);
font_metrics_.SetLineGap(line_gap);
font_metrics_.SetLineSpacing(lroundf(ascent) + lroundf(descent) +
lroundf(line_gap));
https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/fonts/simple_font_data.cc#123

Related

Why does margin-top and margin-bottom work for input? [duplicate]

According to MDN, a button is an inline element.
However, button elements have default styling with display: inline-block (See this question)
button, textarea,
input, select { display: inline-block }
So far so good.
However:
If I now set the button with display:inline - width still applies!!
DEMO
button,
div {
width: 200px;
border: 1px solid red;
display: inline;
}
<button>button</button>
<div>div</div>
Now, according to the spec: width does not apply to inline elements (which are non-replaced)
Applies to: all elements but non-replaced inline elements, table rows,
and row groups
That being the case:
Why does width still apply to an inline button element?
As mentioned in the comments, I'm pretty sure this has to do with browser-specific rendering behavior as is so typical of form elements. What I believe is happening when you set display: inline on the button is... nothing. Effectively, it's the same as the typical browser default display: inline-block, on which the width property does apply.
Refer to section 10.2, which describes the width property itself. In particular it explains why exactly the width property does not apply to inline elements (or inline boxes):
This property does not apply to non-replaced inline elements. The content width of a non-replaced inline element's boxes is that of the rendered content within them (before any relative offset of children). Recall that inline boxes flow into line boxes. The width of line boxes is given by the their containing block, but may be shorted by the presence of floats.
In short, it's because the content of inline elements resides in line boxes. The width of a line box cannot be controlled directly; it is determined entirely by the containing block and any incidental floats. You can see an example of line box rendering in section 9.4.2, which describes inline formatting contexts.
If display: inline actually made a button render as an inline box, all its contents would spill over and it would no longer look, or function, like a button. It makes sense to want to prevent that from happening, and I think that's just what browsers do.
So what exactly do they do to prevent this? Is a button a replaced element? I can't say for sure. But note, in section 9.2.2, it says:
Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
Section 10 does not explicitly mention atomic inline-level boxes, but it does have sections for calculating dimensions for inline replaced elements, as well as inline-block elements whether replaced or non-replaced, all of which are considered atomic inlines as mentioned above. In all of these cases, the width property applies as normal if it's not auto.
So, while it's still debatable whether or not a button is a replaced element, it probably doesn't matter at all for the purposes of this question. But it is still some kind of atomic inline element, since it still participates in an inline formatting context. For what it's worth, though, it appears to shrink to fit its contents if you don't set a width, so its behavior is probably closer to that of an inline-block in that case. One could say then that the actual value of display becomes inline-block, although this is never reflected in the developer tools because the computed value does not change (again a side effect of browser-specific rendering behavior).
Since like Boltclock, I don't think that there's a simple answer to this, this is as much a dump of my thoughts on the subject as an answer, but I hope it will be informative.
Although the CSS display property is superficially quite simple, it actually contains a multitude of aspects. The CSS level 3 draft spec css-display captures some of this complexity, but still doesn't seem to cover it adequately.
The HTML5 spec says for the rendering of <button> elements:
When the button binding applies to a button element, the element is
expected to render as an 'inline-block' box rendered as a button whose
contents are the contents of the element.
An inline-block box has a number of aspects to it:
1. An inline-level element
This means that it participates in a inline formatting context within a line box. It flows in sequence with other elements that are on the same line. The line box's content can be centre aligned with text-align:center property on its container, and the line box is shortened by avoiding floated elements.
2. Applies a width property and the auto value is shrink-to-fit
Unlike non-replaced display:inline elements, the width value applies. But also, if a width value is not specified, a shrink-to-fit algorithm is applied to determine the width. This is like floated elements, or display:table elements, but different from display:block elements which are as wide as possible if no width is specified. It's also unlike replaced inline elements and replaced inline-block elements which, if no width is specified, use their intrinsic width if they have one and a default value of 300px if they don't. Shrink-to-fit is a meaningless concept for replaced elements.
3. A block-container element
Block container elements are make up of a stack of line boxes. The content flows from one line box to the next and the height of the inline-block elements grows (subject to overflow) to fully contain all the line boxes.
4. The baseline is the baseline of the last contained line box
When the inline-block element contains multiple lines, its baseline is the last of those lines. This is unlike floats or display:table-cell elements which are also shrink-to-fit, block container elements. Floats are outside normal flow so they do not have a baseline, which display:table-cell elements have a baseline that is the baseline of their first line box. A button that has multiple lines does vertically align according this last line box rule.
Now, this is fine for the default display setting. and the HTML5 rendering requirement means that the used value of display for buttons is inline-block even when the specified value is inline. But it doesn't account for the behaviour when specified value is block. In this case, the element has a line-break before and after it, and margin:auto centres the box as a display:block element would, and is not what would be expected of inline-block.
However, its width for a specified value of auto is shrink-to-fit like inline-block, whereas the expected behaviour for display:block is as-wide-as-possible. As far as I know, the only display value that behaves like that is display:table, but there is nothing else to suggest that display:table is being used.
So there's nothing in the spec that I can find which matches this precisely. We can only hope that when the css-display spec gets completed, that it will cover this behaviour.
There are 2 types of element.
Non-replaced elements
Replaced elements
Button belongs to replaced element category.
You can find more on below link.
Littlewebhut
SitePoint
So, for button, according to spec, it becomes right.
Inline, non-replaced elements
The width property does not apply. A computed value of auto for margin-left or margin-right becomes a used value of 0.
Inline, replaced elements (This section applies to button)
A computed value of auto for margin-left or margin-right becomes a used value of 0.
If height and width both have computed values of auto and the element also has an intrinsic width, then that intrinsic width is the used value of width.
If height and width both have computed values of auto and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio; or if width has a computed value of auto, height has some other computed value, and the element does have an intrinsic ratio; then the used value of width is:
(used height) * (intrinsic ratio)
If height and width both have computed values of auto and the element has an intrinsic ratio but no intrinsic height or width, then the used value of width is undefined in CSS 2.1. However, it is suggested that, if the containing block's width does not itself depend on the replaced element's width, then the used value of width is calculated from the constraint equation used for block-level, non-replaced elements in normal flow.
If width has a computed value of auto, and the element has an intrinsic width, then that intrinsic width is the used value of width.
If width has a computed value of width, but none of the conditions above are met, then the used value of width becomes 300px.But, if 300px is too wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead.

Why a div with an empty inline-block has height?

div {
background:red;
}
<div><span style="display: inline-block"></span></div>
The w3c send
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.).
https://www.w3.org/TR/CSS2/visudet.html#inline-box-height
But when the span display is 'inline', why the div's height are 0?
I am confused?
But when the span display is 'inline', why the div's height are 0?
Not 100% correct because if the span has at least one character the height will be different from 0. Even an invisible zero width space:
div {
background: red;
}
<div><span>​</span></div>
In case of an empty span (having display:inline) the browser will generate 0 line box. So inside your div there is no line box thus you have a height equal to 0.
Adding one character will trigger the creating of one line box and the rule you quoted will be used and the line-height will define the height of the line box and the height of a div is the height of its line box (since we only have one).
Same logic if you add an empty inline-block element. Even empty, an inline-block will trigger the creation of a line box.
Same logic if you use an empty img:
div {
background: red;
}
<div><img></div>
To use easy words, an inline-block element is still considered as an existing element and we need to generate a line box to hold it. An empty inline element will be considered by the browser as a non-existing element so we don't need any line box to hold something that doesn't really exist.
Line boxes are created as needed to hold inline-level content within an inline formatting context. Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose. ref
line-height only applies when there are actual lines inside an element, in your case, <span>.
Since you did not fill the element with any text, the div does not expand.
If you want to leave the span empty, try specifying height or adding margin to the span, like this:
div {
background: red;
}
span {
display: inline-block;
margin: 1em;
}
<div>
<span></span>
</div>

Why does width apply to a button with display inline?

According to MDN, a button is an inline element.
However, button elements have default styling with display: inline-block (See this question)
button, textarea,
input, select { display: inline-block }
So far so good.
However:
If I now set the button with display:inline - width still applies!!
DEMO
button,
div {
width: 200px;
border: 1px solid red;
display: inline;
}
<button>button</button>
<div>div</div>
Now, according to the spec: width does not apply to inline elements (which are non-replaced)
Applies to: all elements but non-replaced inline elements, table rows,
and row groups
That being the case:
Why does width still apply to an inline button element?
As mentioned in the comments, I'm pretty sure this has to do with browser-specific rendering behavior as is so typical of form elements. What I believe is happening when you set display: inline on the button is... nothing. Effectively, it's the same as the typical browser default display: inline-block, on which the width property does apply.
Refer to section 10.2, which describes the width property itself. In particular it explains why exactly the width property does not apply to inline elements (or inline boxes):
This property does not apply to non-replaced inline elements. The content width of a non-replaced inline element's boxes is that of the rendered content within them (before any relative offset of children). Recall that inline boxes flow into line boxes. The width of line boxes is given by the their containing block, but may be shorted by the presence of floats.
In short, it's because the content of inline elements resides in line boxes. The width of a line box cannot be controlled directly; it is determined entirely by the containing block and any incidental floats. You can see an example of line box rendering in section 9.4.2, which describes inline formatting contexts.
If display: inline actually made a button render as an inline box, all its contents would spill over and it would no longer look, or function, like a button. It makes sense to want to prevent that from happening, and I think that's just what browsers do.
So what exactly do they do to prevent this? Is a button a replaced element? I can't say for sure. But note, in section 9.2.2, it says:
Inline-level boxes that are not inline boxes (such as replaced inline-level elements, inline-block elements, and inline-table elements) are called atomic inline-level boxes because they participate in their inline formatting context as a single opaque box.
Section 10 does not explicitly mention atomic inline-level boxes, but it does have sections for calculating dimensions for inline replaced elements, as well as inline-block elements whether replaced or non-replaced, all of which are considered atomic inlines as mentioned above. In all of these cases, the width property applies as normal if it's not auto.
So, while it's still debatable whether or not a button is a replaced element, it probably doesn't matter at all for the purposes of this question. But it is still some kind of atomic inline element, since it still participates in an inline formatting context. For what it's worth, though, it appears to shrink to fit its contents if you don't set a width, so its behavior is probably closer to that of an inline-block in that case. One could say then that the actual value of display becomes inline-block, although this is never reflected in the developer tools because the computed value does not change (again a side effect of browser-specific rendering behavior).
Since like Boltclock, I don't think that there's a simple answer to this, this is as much a dump of my thoughts on the subject as an answer, but I hope it will be informative.
Although the CSS display property is superficially quite simple, it actually contains a multitude of aspects. The CSS level 3 draft spec css-display captures some of this complexity, but still doesn't seem to cover it adequately.
The HTML5 spec says for the rendering of <button> elements:
When the button binding applies to a button element, the element is
expected to render as an 'inline-block' box rendered as a button whose
contents are the contents of the element.
An inline-block box has a number of aspects to it:
1. An inline-level element
This means that it participates in a inline formatting context within a line box. It flows in sequence with other elements that are on the same line. The line box's content can be centre aligned with text-align:center property on its container, and the line box is shortened by avoiding floated elements.
2. Applies a width property and the auto value is shrink-to-fit
Unlike non-replaced display:inline elements, the width value applies. But also, if a width value is not specified, a shrink-to-fit algorithm is applied to determine the width. This is like floated elements, or display:table elements, but different from display:block elements which are as wide as possible if no width is specified. It's also unlike replaced inline elements and replaced inline-block elements which, if no width is specified, use their intrinsic width if they have one and a default value of 300px if they don't. Shrink-to-fit is a meaningless concept for replaced elements.
3. A block-container element
Block container elements are make up of a stack of line boxes. The content flows from one line box to the next and the height of the inline-block elements grows (subject to overflow) to fully contain all the line boxes.
4. The baseline is the baseline of the last contained line box
When the inline-block element contains multiple lines, its baseline is the last of those lines. This is unlike floats or display:table-cell elements which are also shrink-to-fit, block container elements. Floats are outside normal flow so they do not have a baseline, which display:table-cell elements have a baseline that is the baseline of their first line box. A button that has multiple lines does vertically align according this last line box rule.
Now, this is fine for the default display setting. and the HTML5 rendering requirement means that the used value of display for buttons is inline-block even when the specified value is inline. But it doesn't account for the behaviour when specified value is block. In this case, the element has a line-break before and after it, and margin:auto centres the box as a display:block element would, and is not what would be expected of inline-block.
However, its width for a specified value of auto is shrink-to-fit like inline-block, whereas the expected behaviour for display:block is as-wide-as-possible. As far as I know, the only display value that behaves like that is display:table, but there is nothing else to suggest that display:table is being used.
So there's nothing in the spec that I can find which matches this precisely. We can only hope that when the css-display spec gets completed, that it will cover this behaviour.
There are 2 types of element.
Non-replaced elements
Replaced elements
Button belongs to replaced element category.
You can find more on below link.
Littlewebhut
SitePoint
So, for button, according to spec, it becomes right.
Inline, non-replaced elements
The width property does not apply. A computed value of auto for margin-left or margin-right becomes a used value of 0.
Inline, replaced elements (This section applies to button)
A computed value of auto for margin-left or margin-right becomes a used value of 0.
If height and width both have computed values of auto and the element also has an intrinsic width, then that intrinsic width is the used value of width.
If height and width both have computed values of auto and the element has no intrinsic width, but does have an intrinsic height and intrinsic ratio; or if width has a computed value of auto, height has some other computed value, and the element does have an intrinsic ratio; then the used value of width is:
(used height) * (intrinsic ratio)
If height and width both have computed values of auto and the element has an intrinsic ratio but no intrinsic height or width, then the used value of width is undefined in CSS 2.1. However, it is suggested that, if the containing block's width does not itself depend on the replaced element's width, then the used value of width is calculated from the constraint equation used for block-level, non-replaced elements in normal flow.
If width has a computed value of auto, and the element has an intrinsic width, then that intrinsic width is the used value of width.
If width has a computed value of width, but none of the conditions above are met, then the used value of width becomes 300px.But, if 300px is too wide to fit the device, UAs should use the width of the largest rectangle that has a 2:1 ratio and fits the device instead.

Why does display:inline cancel height?

I was making a menu with id menu which had the following set:
display: inline;
height: 200px;
Once I removed display: inline;, height worked again.
Why?
display: inline;
Are usually used to refer to text elements;
From the w3c page:
[inline] Causes an element to generate one or more inline boxes.
Therefore, the height you must set is the line-height property. From the w3c page:
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.
Notice here that you can control only the minimal height of your inline elements
Because the spec says so:
'height' … Applies to: all elements but non-replaced inline elements, table columns, and column groups
line-height, however, does apply to inline elements.
By making something display inline you are effectively making it the same as a span. As such the only valid height value is line-height since you are working on an inline element.
To render height values you would need to use a div tag or force your existing tab to render like a div / object which accepts height attributes. You can do this by setting display:block.
In essence you can render a div to work like a span by setting display:inline, and conversely render a span as effectively a div via display:block.
Span tag are meant for inline styling such as font size, colour, decoration, etc.

Why does changing font-size and line-height screw up my HTML layout?

I could post the code if it would be helpful (but it's a lot). Basically, if I change line-height or font-size to a really big value, it breaks my html layout - specifically, my DIVs seem to be getting bigger...But I don't have text in those divs.
Any inline element will pay attention to 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.
In your case, you have <img> elements (which are inline elements by default) inside your <div> elements (which are block containers).
Changing the font-size implicitly alters the pixel value of line-height, the default is line-height: normal and that means:
Tells user agents to set the used value to a "reasonable" value based on the font of the element.
So, altering either the font-size or line-height will change the vertical space that your inline elements occupy.