CSS 2.2 Spec for 10.3.7 Absolutely positioned, non-replaced elements says:
If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0. Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
If none of the three is 'auto': If both 'margin-left' and 'margin-right' are 'auto', solve the equation under the extra constraint that the two margins get equal values, unless this would make them negative, in which case when direction of the containing block is 'ltr' ('rtl'), set 'margin-left' ('margin-right') to zero and solve for 'margin-right' ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', solve the equation for that value. If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.
I was playing with these rules. In one case I set left, right and width, not an auto value, but I am not getting how right value is affecting the layout. To me it seems it has no effect, like the used value of right is set to 0 (0 is my guess as browser devtool shows the value as 5px). Can anyone explain if my guess is correct or not? If not then how it is applying in the layout, because I am not able to justify it from the documentations of the spec above.
.parent {
width: 200px;
height: 200px;
background: #ffe;
position: relative;
border: 1px solid #999;
}
.child {
background: #ee2;
position: absolute;
left: 5px;
width: 100px;
padding: 5px;
right: 5px;
}
<div class="parent">
<div class="child">
I am positioned
</div>
</div>
the display is like:
Stickers has MDN's version. The relevant portion of the text from your citation itself is in the last sentence, which corroborates with MDN's interpretation (MDN simply uses different terminology that may be more accessible to web developers):
If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.
The term "over-constrained" is defined in section 10.3.3 however:
If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will have to be different from its computed value.
The MDN article explains it clearly:
When both left and right are defined, the position of the element is overspecified. When this is the case, the left value has precedence when the container is left-to-right (thus, the computed value of right is set to -left); the right value has precedence when the container is right-to-left (thus, the computed value of left is set to -right).
Related
actually I saw many questions like this but I can't found normal answer of this question because that I open this question again.
When we have block element(display: block) this element contain full width of parent component if element itself root element this element width take 100%.
But when we look block element(display:block) but position absolute elements there are work like inline-block elements(work like block element but width not full of parent component) even parent element position relative.
Can anyone explain me why position absolute and fixed elements width not work like display: block elements.
https://jsfiddle.net/epbkmzh3/28/
<div class="container">
<div style="background: red;"> 1 </div>
</div>
<div class="container" style="position: relative;">
<div style="position: absolute; background: red;"> 1 </div>
</div>
It's because absolute and fixed positioning removes the element from document flow.
And since those elements are removed from document flow, there is no reference for what width they should be, so they only take as much space as their content.
They are still "block" elements if they are inherently block elements (div, p, etc.), unless the display property is changed via CSS. Edit for clarity: The browser will still compute as display: block even if the display property is changed via CSS.
Here is some documentation on document flow:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flow_Layout/In_Flow_and_Out_of_Flow
The important part:
Taking an item out of flow
All elements are in-flow apart from:
floated items
items with position: absolute (including position: fixed which acts in the same way)
the root element (html)
Out of flow items create a new Block Formatting Context (BFC) and therefore everything inside them can be seen as a mini layout, separate from the rest of the page. The root element therefore is out of flow, as the container for everything in our document, and establishes the Block Formatting Context for the document.
Here is the Specification detailing how you can find the width/height of any element: https://www.w3.org/TR/CSS21/visudet.html
From there you can read for absolute element that:
The constraint that determines the used values for these elements is:
'left' + 'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' + 'right' = width of containing block
Then
If all three of 'left', 'width', and 'right' are 'auto': First set any 'auto' values for 'margin-left' and 'margin-right' to 0. Then, if the 'direction' property of the element establishing the static-position containing block is 'ltr' set 'left' to the static position and apply rule number three below; otherwise, set 'right' to the static position and apply rule number one below.
And the rule number three:
'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
The rule number one is also similar
And if you continue reading you will find how to calculate the shrink-to-fit width. You will also notice that the same shrink-to-fit algorithm apply to float and inline-block element
Also note that fixed element is a particular case of absolute so the same rule applies. The only difference is the containing block
Fixed positioning is a subcategory of absolute positioning. The only difference is that for a fixed positioned box, the containing block is established by the viewport. ref
You can also see that block elements follow almost the same constraint (without left/right) but the rules are different:
The following constraints must hold among the used values of the other properties:
'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
Then
If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.
This will make width = width of containing block
An important difference between inline-block element is that absolute element will not take the width of their content like we may think. This happen in most of the case due to the constraint explained above but check the below example:
.container {
clear:left;
margin:5px;
}
<div class="container" style="float:left;position:relative;">
<div style="display:inline-block; background: red;"> 1 1 1 1 1 1 </div>
</div>
<div class="container" style="float:left;position:relative;">
<div style="position: absolute; background: red;"> 1 1 1 1 1 1 </div>
</div>
Note how the absolute element will wrap to the smallest size possible unline the inline-block. This is due to the fact that the containing block is also a shrink-to-fit container.
Related: Why everything word-wrap inside an absolute element nested inside a float or inline-block element
If you have a div that sits at the 'bottom' like so:
<div id="box" style="position: absolute;width: 10px;height: 10px;bottom: 0px;"></div>
and then if you were to change the position using 'top'...
$('#box').css({'top':'0px'});
what happens to the 'bottom' css command and what decides who (top or bottom) wins?
Should I cancel bottom somehow at the same time as setting top?
Ideas:
$('#box').css({'top':'0px','bottom','none'});
$('#box').css({'top':'0px','bottom',''});
It never occurred to me before
The interactions between width, height, and box offsets in a variety of scenarios in CSS are all detailed in section 10 of the spec.
Since your element is absolutely positioned, refer to section 10.6.4, which says:
For absolutely positioned elements, the used values of the vertical dimensions must satisfy this constraint:
'top' + 'margin-top' + 'border-top-width' + 'padding-top' + 'height' + 'padding-bottom' + 'border-bottom-width' + 'margin-bottom' + 'bottom' = height of containing block
If all three of 'top', 'height', and 'bottom' are auto, set 'top' to the static position and apply rule number three below.
If none of the three are 'auto': If both 'margin-top' and 'margin-bottom' are 'auto', solve the equation under the extra constraint that the two margins get equal values. If one of 'margin-top' or 'margin-bottom' is 'auto', solve the equation for that value. If the values are over-constrained, ignore the value for 'bottom' and solve for that value.
In your case, because the values are over-constrained once you set a value for top, top wins.
Note that setting none won't work because it's not a valid value for bottom, and setting the empty string reverts it to its default value which for most if not all elements is auto, which does not result in over-constrained values.
Top "wins" when all three of top, bottom and height are present - MDN:
...the top property overrides the bottom property, so if top is not auto, the computed value of bottom is the negative of the computed value of top.
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
I sometimes find it convenient to give the size of an element in terms of the bottom, top, left and right properties, rather than using width and height. This is, for example, the accepted answer here:
CSS 100% height with padding/margin
However, for some reason this doesn't work with an svg element. I've tried the following example with the latest stable Firefox and Chrome. The svg element inexplicably wants to take a size of 300x150:
Fiddle
Why?
While it's not mentioned directly in the spec (at least in my knowledge)<svg> is considered as a replaced element (unlike <div> which is a non-replaced block level element).
For absolutely positioned replaced elements, if the values of top/bottom are over-constrained, once you set a value for top, bottom would be ignored. This is true for left/right properties as well.
10.3 Calculating widths and margins / 10.3.8 Absolutely positioned, replaced elements
If at this point the values are over-constrained, ignore the value for either 'left' (in case the 'direction' property of the containing
block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve
for that value.
10.6 Calculating heights and margins / 10.6.5 Absolutely positioned, replaced elements
If at this point the values are over-constrained, ignore the value for
'bottom' and solve for that value.
Hence the absolutely positioned <svg> element would be position with the respect to top and left offsets.
The Set-up for Context
I think you can tell by my resume on Stack Overflow that I am no stranger to css and its behaviors. However, I just came across something I have not experienced before that seems completely incorrect--yet Firefox 27 and Chrome 33 and IE 11 all render it the same.
Here is the code (just two empty nested div elements is the html), which can be seen in this fiddle example:
CSS
div {
width: 50%;
height: 100px;
margin: 10px;
border: 1px solid red;
position: relative;
}
div > div {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
border-color: cyan;
}
I would have expected the absolute positioned div to directly overlap the size of the relative parent div. I expected it would have had its positioning properties override the width and height and margin positioning from the straight div call. I would have expected this behavior (1) because it is more specific in its selector, and (2) because it is positioned absolute and given positioning calls to all four sides.
As you can see by the referenced fiddle above it in fact retains its width and height and seems to essentially "ignore" its positioning values all together. That is, I get the same positioning if the positioning values are taken out. I need to override the previously set properties like so...
width: auto;
height: auto;
margin: auto;
...to get the positioning to actually do something. This appears to be standard behavior given that all the main browsers are reacting the same. I suppose I have never noticed it before because normally my sizing of a wrapping div is done by a class and thus applies only to that div, while my positioning of the absolute child is done likewise, and I don't bother to set a width and height on it.
The Question Itself
My question is seeking quote(s) and link(s) from the W3C documentation (or a highly respected source, such as from one of the major browser developers) that discusses why or that the width or height settings should override a setting of top, right, bottom, left, etc. settings. From the main page discussing absolute positioning, all I found was that either can be used to set the width/height, but the crux of my issue is that I would expect the more specific/last in cascade to take precedence no matter which way was defining the sizing. Yet this is not happening here.
A Description of the Behavior I am Seeing
#pjp found this sitepoint reference, stating:
Absolutely positioned boxes with both right and left having a value
other than auto will effectively define the width of the element
assuming that the element's width is auto. This may result in the
element having no width at all if left and right positions occur at
the same point or perhaps overlap. If the values for left,right and
width are over-constrained and the direction property is ltr, right
will be ignored. If direction is rtl, left will be ignored. Note that
replaced elements with a width of auto will have the elements
intrinsic width substituted and the preceding rules applied.
This exactly articulates the behavior I am seeing. It does not address why (officially) it is that way. I would have expected instead that either:
(1) A higher specificity or equal specificity but following in cascade order setting of width and height (whether explicitly by those properties or by the positioning properites) to take precedence.
(2) In the case of defining both at once (an illogical thing to do, but...):
div {
position: absolute;
width: 50%;
left: 0;
right: 0;
}
or
div {
position: absolute;
left: 0;
right: 0;
width: 50%;
}
That the last defining of width would supersede, so the first the positioning would win, and the second the width would win. This would be just like two definitions in the same block, like so:
div {
width: 50%;
width: 75%;
}
Here, width: 75% wins because it is "last" defined for the property.
I think that your confusion comes from handling the specificity in an inteligent way.
It works on a mechanical way.
That is, every property that has a value is handled in a cascade way, without any consideration about what others properties are some how related.
The width property is inherited by the child as 50% because there is no width set on the child. forget about the posibility of calculating the width from the left and right properties, there is no width property defined, and that is it.
Then, you have also the left and right properties, both set to 0. (no discussion here, I think).
And then, in the w3c docs, you see
If the values are over-constrained, ignore the value for 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.
That is a cite with almost the exact words of the cite in your question, but coming from an authorative source