Why does width:auto behave differently than height:auto? - html

I don't get the auto value. If applied to height it will take on the child's height, but if applied to width it will take on the parent's width.
There are no MDN posts on the auto value itself, and Google yields "100% VS auto" hits rather than "width:auto VS height:auto" hits.
For my current needs I would like an element to expand to its child's width, but in general I wish to know what is the deal with auto.
.divXS {
width: 100px;
height: 100px;
background: green;
}
.divXXS {
width: 50px;
height: 50px;
background: yellow;
}
.divSM {
width: 200px;
height: 200px;
}
#Father {
background: blue;
border: 3px solid #20295E;
}
#Mother {
background: pink;
border: 3px solid #DB6DBE;
}
#Daughter {
width: 100%;
height: 100%;
}
#Son {
width: auto;
height: auto;
}
<div class="divSM" id="Mother">
<div class="divXS" id="Daughter">
<div class="divXXS" id="grandDaughter"></div>
</div>
</div>
<div class="divSM" id="Father">
<div class="divXS" id="Son">
<div class="divXXS" id="grandSon"></div>
</div>
</div>
jsFiddle / jsBin

'auto' doesn't even always behave the same way for the width property and the height property respectively. It can have different behaviors for the width property not only depending on the element's display type, but also depending on the value of other properties for the same display type. That's why it's called 'auto' — from my answer here,
The value of said property is adjusted automatically according to the content or the context of the element.
Based on your description I'm assuming your question is in the context of block layout. Block layout consists of a series of block-level boxes stacked vertically in normal flow.
So a block container box, by default, only has to grow tall enough to contain its descendants stacked vertically. And since block-level boxes never stack horizontally in normal flow, there's no reason they can't stretch to the full width of their containing block. (They don't even need to shrink to accommodate floats in the same block formatting context, though the line boxes inside them do, but that's a separate topic altogether.)
And that's why, in block layout, an auto height is based on the total height of descendants and an auto width is based on the containing block width.

Related

How is the margin-top percentage calculated?

I know this should be straightforward, but can anybody tell me why the child boxes in the following overflow their parent's container when a margin-top: 50% is applied to the child. How is the margin-top percentage calculated?
.container {
background: lightblue;
padding: 10px;
height: 200px;
}
p {
display: block;
border:1px solid red;
margin-top:50%;
}
<div class="container">
<p> Some Cool content</p>
</div>
Shouldn't the child element be placed 50% from 200px (set height on parent), i.e 100px from top?
From W3C Spec:
The percentage is calculated with respect to the width of the generated box's containing block. Note that this is true for 'margin-top' and 'margin-bottom' as well. If the containing block's width depends on this element, then the resulting layout is undefined in CSS 2.1.
There are two good reasons to base vertical margins on the width of the containing block:
Horizontal and Vertical Consistency
There is, of course, a shorthand property that lets you specify the margin for all four sides of a block:
margin: 10%;
This expands to:
margin-top: 10%;
margin-right: 10%;
margin-bottom: 10%;
margin-left: 10%;
Now, if you wrote either of the above, you’d probably expect the margins on all four sides of the block to be of equal size, wouldn’t you? But if margin-left and margin-right were based on the width of the container, and margin-top and margin-bottom were based on its height, then they’d usually be different!
Avoiding Circular Dependency
CSS lays out content in blocks stacked vertically down the page, so the width of a block is usually dictated entirely by the width of its parent. In other words, you can calculate the width of a block without worrying about what’s inside that block.
The height of a block is a different matter. Usually, the height depends on the combined height of its contents. Change the height of the content, and you change the height of the block. See the problem?
To get the height of the content, you need to know the top and bottom margins that are applied to it. And if those margins depend on the height of the parent block, you’re in trouble, because you can’t calculate one without knowing the other!
Basing vertical margins on the width of the container breaks that circular dependency, and makes it possible to lay out the page.
Example:
Here is the fiddle. And the code:
HTML
<div class="container">
<p id="element"> Some Cool content</p>
</div>
<p>
MORE TEXT
</p>
CSS
.container {
background: lightblue;
padding: 10px;
height: 100px;
width: 500px;
}
p {
display: block;
border: 1px solid red;
margin-top: 50%;
}
JS
window.onload = function(evt) {
var element = document.getElementById("element"),
style = element.currentStyle || window.getComputedStyle(element);
element.textContent = "the margin-top is : " + style.marginTop;
};
Percent-based margins are calculated using the width of the parent container, regardless of which side the margin is on.
So you're seeing a margin-top equal to 50% of the width.
if you want set percentage of height, give 'vh' a try..
{
margin-top: 5vh; /* should be 5% of vertical container */
}
discussed here

Why doesn't "margin: auto" center an element vertically?

As you can see in the demo below, margin: auto; centers the blue div horizontally, but not vertically. Why not?
.box {
border: 1px solid red;
width: 100px;
height: 100px;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
}
<div class="box">
<div class="center"></div>
</div>
My question is not asking for workarounds.
As mentioned, this behavior is specified in section 10.6.2 of CSS2.1, and has remained unchanged from CSS2.
Block boxes are stacked vertically from top to bottom in normal flow. Furthermore, vertical margins may collapse, and only do so under certain circumstances (in your demo, the border on the parent element will prevent any margins on the child element from collapsing with its own). If you only have one such block box, and the height of the containing block is auto, then its top and bottom margins will be zero anyway. But if you have more than one block box in the same flow, or even out-of-flow boxes affecting the layout of in-flow boxes (in the case of clearance for example), how would you expect auto margins to resolve for those in-flow boxes?
This is why auto left and right margins are likewise zeroed out for inline elements (including atomic inlines) and floats (though horizontal margins never collapse). Inline-level boxes are laid along line boxes, and floats too obey unique layout rules.
Absolutely positioned boxes are a different story: since they are never aware of any other boxes in the same positioning context as themselves, auto top and bottom margins can be calculated for them with respect to their containing blocks without having to worry about any other boxes ever interfering.
Flexbox is also a different story: what sets flex layout apart from block layout is that flex items are by definition always aware of other flex items in the same flex formatting context, including the fact that there are none. In particular, neither can floats intrude into the flex container, nor can you float flex items to subvert this (although you can still remove a child element from flex layout completely with absolute positioning). Margins behave very differently with flex items due in part to this. See sections 4.2, 9.5 and 9.6.
Why...because the W3C spec says so.
If 'margin-top', or 'margin-bottom' are 'auto', their used value is 0.
As to the actual "why"...the query should really be addressed there.
It doesn't center the element vertically because it is a block-level element in the normal flow. Thus, the following rule applies:
If margin-top, or margin-bottom are auto, their used value is 0.
It's also worth pointing out that the rule above also applies to the following elements as well: (see points 10.6.2 and 10.6.3 for more information and conditions).
Inline replaced elements
Block-level replaced elements in normal flow
inline-block replaced elements in normal flow
Floating replaced elements
Block-level non-replaced elements in normal flow when overflow computes to visible
With that being said, absolutely positioned, non-replaced elements that don't have top, height, and bottom values of auto are an exception to this rule. The following applies from point 10.6.4:
If none of the three top, height, and bottom are auto and if both margin-top and margin-bottom are auto, solve the equation under the extra constraint that the two margins get equal values.
See the example below demonstrating how an absolutely positioned element is vertically centered using margin: auto. It works because none of the three properties top, height, and bottom have a value of auto:
.box {
border: 1px solid red;
width: 100px;
height: 100px;
position: relative;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
position: absolute;
top: 0; right: 0;
bottom: 0; left: 0;
}
<div class="box">
<div class="center"></div>
</div>
In addition, it's probably worth pointing out the following rule as well:
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.
This means that if the absolutely positioned element has a margin-top value of auto and a margin-bottom value of 0 (i.e., margin: auto auto 0), the element would be absolutely positioned at the bottom relative to the parent like in the example below:
.box {
border: 1px solid red;
width: 100px;
height: 100px;
position: relative;
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto auto 0;
position: absolute;
top: 0; right: 0;
bottom: 0; left: 0;
}
<div class="box">
<div class="center"></div>
</div>
Why doesn't margin:auto work vertically?
Actually, it does – just not for every display value.
If display is flex, margin: auto centers both vertically and horizontally.
The same applies to display: inline-flex, display: grid and display: inline-grid.
.box {
border: 1px solid red;
width: 100px;
height: 100px;
display: flex; /* new */
}
.center {
background: blue;
width: 50px;
height: 50px;
margin: auto;
}
<div class="box">
<div class="center"></div>
</div>
It's because of the actual possibility of knowing the true height of the element in which you want to center vertically in. To understand that, first think about how auto horizontal centering works. You have a div which you've given it a width (fixed or percentage). The width can be calculated to certain degree. If it's fixed width, great. If it's flexible or responsive (percentage) at least you have a range that the width will cover before it hits the next breakpoint. You take that width, minus whatever it's inside and split the remainder on both sides.
Now, with that information, how could the browser calculate the infinite amount of variations in which your div will grow vertically? Keep in mind the size of the element, wrapping of text, paddings, and responsiveness will also alter the width and force the text to wrap further, and on, and on it goes.
Is it an impossible task? Not really, has CSS spent time and effort covering this? Not worth their time, I guess.
And that is basically the answer I tell my students.
But....fret not! Bootstrap v4 alpha has figured out vertical centering!
EDIT
Sorry to edit this late but I thought you may want to consider this solutions to center vertically and it is pretty simple by making use of the calc function
<div class="foo"></div>
.foo {
background-color: red;
height: 6em;
left: calc(50% - 3em);
position: absolute;
top: calc(50% - 3em);
width: 6em;
}
See it HERE

100% is not equal to 100%?

When having two divs next to each other, with both a width set in percentages, 100% is just a bit too much, and causes the two divs to not be next to each other anymore.
99% then leaves a rather big gap between the two divs.
Is there a certain percentage at which the two divs do nicely fit on the page?
And what could be the cause of this problem?
And what could be the cause of this problem?
Most likely this is padding/border which adds up to element width according to default box model. To overcome it change box-sizing property of the respective elements you want to fill 100% width:
.inline-blocks {
box-sizing: border-box;
}
If you're using inline displaying, the new line between two separate nodes is included as whitespace. This results in the two elements wrapping despite their widths summing up to 100%.
.container {
width: 200px;
border: 1px solid blue;
}
.inl {
display: inline-block;
width: 50%;
height: 20px;
background: green;
}
<div class="container">
<div class="inl"></div>
<div class="inl"></div>
</div>
<div class="container">
<div class="inl"></div><div class="inl"></div>
</div>

Height of flex element is not respected unless display: table is set?

http://jsfiddle.net/u2zedzob/12/
<div class="wrapper">
<div class="main-content"></div>
<div class="footer"></div>
</div>
*, *:before, *:after {
box-sizing: border-box;
}
.wrapper {
background-color: red;
height: 300px;
width: 300px;
display: flex;
flex-direction: column;
}
.main-content {
height: 100%;
width: 100%;
background-color: green;
}
.footer {
height: 30px;
width: 100%;
background-color: blue;
display: table;
}
In this example, the footer's height is 30px and main-content grows to fill the rest of the space. Perfect!
However, if I remove the display: table property of footer -- its height becomes 27.266px. This can also be prevented by setting the footer's min-height to 30px.
I'm not too familiar with flex logic. Why is this happening? Is there a more elegant solution where the height of footer will be respected? Maybe a flex property I am missing?
When working with flex box model you do not need to set height property for the child elements which should scale. Instead you would use e.g. the flex-grow property. In the given case where just one element should be scaled a value of 1 would be appropriate (as no proportions need to be defined here).
Have a look at the updated example where the height of the .main-content element is scaled according to the available space and the height of the .footer element is retained correctly by 30px without setting the display property:
http://jsfiddle.net/u2zedzob/21/
For further information maybe check this guide:
http://css-tricks.com/snippets/css/a-guide-to-flexbox/
The flex-grow property is explained as:
This defines the ability for a flex item to grow if necessary. It
accepts a unitless value that serves as a proportion. It dictates what
amount of the available space inside the flex container the item
should take up.
If all items have flex-grow set to 1, every child will set to an equal
size inside the container. If you were to give one of the children a
value of 2, that child would take up twice as much space as the
others.

Why does 100% not mean 100% height?

I am trying to get some divss to expand to fill the screen, but I am struggling. I have broken down the issue on this jsfiddle.
What I really want to know is why does the div, with its 100% min-height, not expand to that height (or at all) when its parent has the same attribute and does expand?
<body>
<div>
stuff
</div>
</body>
body {
min-height: 100%;
background: red;
}
div {
min-height: 100%;
background: grey;
}
The issue is covered in the CSS 2.1 spec:
<percentage>
Specifies a percentage height. The percentage is
calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly (i.e., it depends on content height), and this
element is not absolutely positioned, the value computes to 'auto'. A
percentage height on the root element is relative to the initial
containing block. Note: For absolutely positioned elements whose
containing block is based on a block-level element, the percentage is
calculated with respect to the height of the padding box of that
element. This is a change from CSS1, where the percentage was always
calculated with respect to the content box of the parent element.
So, to clarify, a percentage height will reference the height of its containing block (unless it is position: absolute or position: fixed). If that containing block does not have a specified height, then the percentage will refer to auto, and it won't really do much.
position: absolute changes the referenced containing block to the nearest positioned (absolute, relative, or fixed) element.
position: fixed changes the referenced containing block to the viewport.
So, if you specify a height on your containing block, specify a position other than static on your containing block, or don't mind using the viewport as your containing block, then you can use percentage heights effectively.
Please see my demonstration at jsFiddle
You need to also set the height of the html so that 100% refers to the viewport height instead of the document height (demo):
html,body {
height: 100%;
background: red;
padding: 0;
}
div {
height: 100%;
background: grey;
}
because you can't really use 100% height on a static element. Changing the position attribute from static to absolute will give you 100% height. demo
posted as answer per the request of the the PO.
Percentage heights in CSS don't make a lot of sense to me. I would argue that it doesn't work the way it should, but CSS enthusiasts would insist that it does.
This article discusses both the issue and solution in detail:
http://matthewjamestaylor.com/blog/equal-height-columns-cross-browser-css-no-hacks
This might help too:
<style>
#outer {position:absolute; height:auto; width:200px; border: 1px solid red; }
#inner {position:absolute; height:100%; width:20px; border:1px solid black; }
</style>
<div id="outer">
<div id="inner"></div>
text
</div>
See here for more details on the above:
How to make a floated div 100% height of its parent?
There are two issues, you'll want to specify the height of the html as well, as in:
html, body {
min-height: 100%;
}
Also there appears to be an issue in IE where min-height doesn't do the trick for the div but specifying height on the div does the trick. As such:
html, body {
min-height: 100%;
background: red;
}
div {
height: 100%;
background: grey;
}
This will work
body, html {
height: 100vh;
}
aside {
background: green;
width: 200px;
height: 100vh;
}