Maintaining height of element relative to its width [duplicate] - css

I want to have a square div inside a flexbox. So I use:
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50%;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
This works fine in Chrome. But in Firefox, the parent squeezes to just one line.
How do I solve this in Firefox? I use version 44.
You can also view the code at https://jsbin.com/lakoxi/edit?html,css

2018 Update
The flexbox specification has been updated.
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items, like those on block
boxes, are resolved against the inline size of their containing block,
e.g. left/right/top/bottom percentages all resolve against their
containing block’s width in horizontal writing modes.
Original Answer - applies to FF and Edge versions released before 2018
From the flexbox specification:
Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.
Here's some more:
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items can be resolved against either:
their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
the inline axis (left/right/top/bottom percentages all resolve against width)
A User Agent must choose one of these two behaviors.
Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.

In addition to Michael_B's answer, here is a possible workaround.
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be an option, since it works similar (responsive).
Stack snippet
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50vw;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
Updated based on a comment
If a square is a must, and viewport units or script can't be used, here is another trick using a dummy image.
Note, as image also a SVG or a Base64 could be used as a datauri to save an extra round trip to the server
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
}
.inner img {
display: block;
width: 100%;
visibility: hidden;
}
<div class="outer">
<div class="inner">
<img src="http://placehold.it/10" alt="">
</div>
</div>

Related

CSS height mysteries: Different on Chrome and Firefox

This codepen looks different on Chrome than on Firefox. The html and css is:
<div class="outer">
<div>header</div>
<div class="grid">
<div class="central">central</div>
</div>
</div>
.outer {
min-height: 200px;
background: lightsalmon;
display: flex;
flex-direction: column;
}
.grid {
display: grid;
flex: 1 1 0%;
}
.central {
/*height: 98%;*/
background: lightcoral;
}
On Chrome, even though the central element's height is always relative to the intermediate elements actual height (when placed with grid or when using a percentage height like the one in comments), but only if is grandparent's height was explicitly set (and even though that height is not what the central element's is calculated in relation to).
(Grid layout seems to have no such issue: If you change the outer element's display from flex to grid it works on Chrome too.)
Is this a bug or is the behaviour not clearly enough defined?
I can't say I understand that I understand's Mozilla's formal definition of percentage heights on this page anyway and I don't know if there's a more rigorous definition.

CSS Grid auto rows height issue in Firefox ESR [duplicate]

I want to have a square div inside a flexbox. So I use:
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50%;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
This works fine in Chrome. But in Firefox, the parent squeezes to just one line.
How do I solve this in Firefox? I use version 44.
You can also view the code at https://jsbin.com/lakoxi/edit?html,css
2018 Update
The flexbox specification has been updated.
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items, like those on block
boxes, are resolved against the inline size of their containing block,
e.g. left/right/top/bottom percentages all resolve against their
containing block’s width in horizontal writing modes.
Original Answer - applies to FF and Edge versions released before 2018
From the flexbox specification:
Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.
Here's some more:
4.2. Flex Item Margins and Paddings
Percentage margins and paddings on flex items can be resolved against either:
their own axis (left/right percentages resolve against width, top/bottom resolve against height), or,
the inline axis (left/right/top/bottom percentages all resolve against width)
A User Agent must choose one of these two behaviors.
Note: This variance sucks, but it accurately captures the current state of the world (no consensus among implementations, and no consensus within the CSSWG). It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended.
In addition to Michael_B's answer, here is a possible workaround.
When using percent we often relate that to the viewport width, so with that in mind, viewport units vw/vh can be an option, since it works similar (responsive).
Stack snippet
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
padding-bottom: 50vw;
}
<div class="outer">
<div class="inner">
<a>hehe</a>
</div>
</div>
Updated based on a comment
If a square is a must, and viewport units or script can't be used, here is another trick using a dummy image.
Note, as image also a SVG or a Base64 could be used as a datauri to save an extra round trip to the server
.outer {
display: flex;
width: 100%;
background: blue;
}
.inner {
width: 50%;
background: yellow;
}
.inner img {
display: block;
width: 100%;
visibility: hidden;
}
<div class="outer">
<div class="inner">
<img src="http://placehold.it/10" alt="">
</div>
</div>

How set full screen width background on fixed width element?

I have simple structure of element container of dynamic height and fixed width (Markup below). On one hand the element's background should span the whole window width, on the other the children's size must be limited by the container (Desired layout below). The number of children and their sizes (which are equal on the image only for simplicity) are dynamic.
Is that possible without adding extra container? I want to avoid achieving the desired element content width by setting width on the children, because their number is dynamic and the size relationships become complicated to write unless their total width is already limited by container's width.
Here's a pen to experiment;
Markup
<div class="container">
<div class="child">
<div class="child">
...
</div>
.container {
width: <fixed-width>px;
}
Desired layout (the whitespace between children and container is irrelevant)
One route we can take to solve this is by using viewport width on the parent container padding, to force the children into a box that is only 500px wide (as per your codepen).
The important thing to remember when doing this is that box-sizing:border-box; will need to be set on the container, otherwise the padding goes ballistic.
We do this by using calc, vw and padding.
padding: 20px calc(50vw - /*half of container width*/);
Here's the full expanded code of your container on the linked codepen:
.container {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-left: auto;
margin-right: auto;
height: 300px;
width: 100%;
padding: 20px calc(50vw - 250px);
background-color: #acffac;
background-size: 100vw auto;
background-position: center top;
box-sizing: border-box;
}
html {
overflow-y:scroll; /* fixes potential calculation errors caused by scroll bar - thanks to Roberts comment */
}
Here's a working version of the codepen, and for the sake of keeping all my eggs in one basket, here's an expandable code snippet:
.container {
display: flex;
flex-flow: row nowrap;
justify-content: center;
margin-left: auto;
margin-right: auto;
height: 300px;
width: 100%;
padding: 20px calc(50vw - 250px);
background-color: #acffac;
background-size: 100vw auto;
background-position: center top;
box-sizing: border-box;
}
.child {
flex: 1 0 auto;
width: 100px;
height: 100%;
background-color: #ff4444;
}
.child+.child {
margin-left: 20px;
}
<div class="container">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
I will finish off by pointing out that if someone else has a better solution, you may want to look at that for time being instead as there is some issues with using vw inside calc on older versions of Chrome and Safari.
EDIT:
As noted in the comments by Vadim and Robert there are a few things that can cause some snags.
Firstly, assuming you are working with a bare minimum template (i.e. no normalize/reset.css), your body will most probably still have the inherent margins that would mess with this kind of layout. You can fix this with:
body {
margin:0;
}
Secondly, depending on your OS (Yes I'm looking at you Microsoft!) your scrollbars can push your content to the side whilst simultaneously still being included in the calculation for vw.
We can fix this one of two way. The first being an adjustment on the padding calculation to include the scrollbar side, but you would have to write a script to ensure that scrollbar is actually present, and scrollbars differ in sizes (I.E -> 17px, Edge -> 12px).
The other alternative would be to use a custom content scroller, which would do a full overflow:hidden; over the content, thereby removing the scroll bar, before implementing it's own version of a scrollbar (which generally lies on top of the content with a position:fixed;) it.
Using vw and flex we can center the child elements and achieve exactly what you require. I have written a JSfiddle where you can check it out.
Basically what I have done is created a container with display set to flex. Using margin property of the first child element, I have centered all of the other child divs and then the regular properties were added to other divs.
Here's the code
body{
margin: 0;
padding: 0;
}
#container{
display: flex;
width: 100vw;
height: 40vw;
background-color: #333333;
align-items: center;
}
.child{
width: 4vw;
height: 80%;
background-color: red;
margin-right: 10vw;
}
.child:first-child{
margin-left: 28vw;
}
<div id="container">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>

Css column layout - inline block elements, what is percent width relative to?

I'm not sure I understand how css column layout is supposed to work.
I have a straightforward html
<div class="container">
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
<div class="block"></div>
</div>
and css
.block {
background-color: blue;
width: 50em;
height: 10px;
margin: 2px;
display: inline-block;
}
.container {
border: 1px solid black;
display: inline-block;
column-count: 2;
-moz-column-count: 2;
-webkit-column-count: 2;
}
This works as expected with the elements flowing downwards and then over into a second column.
Now what if I change the width value of .block to a percentage width? All my widths are tiny. They still are clearly relative to something since shrinking and growing the percentages changes the width proportionately but I have no idea what 100% corresponds to.
What is the percentage width relative to?
The width is relative to the column pseudo-element inserted by the browser:
http://www.w3.org/TR/css3-multicol/
In the traditional CSS box model, the content of an element is flowed into the content box of the corresponding element. Multi-column layout introduces a new type of container between the content box and the content, namely the column box (or column for short). The content of a multicol element is flowed into its column boxes.
(Emphasis mine.)
So, the percentage width you're asking for is relative to the implicit column boxes.
Browser Support
As I noted in my comment support for column-count etc is poor today. Except where you really need a specific feature of CSS multicolumn, I find it easier to just use the old inline-block approach. Works in every browser in use today, easy to figure out.
http://jsfiddle.net/b9chris/nt83M/
.block {
background-color: blue;
width: 45%;
height: 10px;
margin: 2px;
display: inline-block;
vertical-align: top;
}
.container {
border: 1px solid black;
width: 200px;
display: inline-block;
}
<span class=container>
<span class=block></span>
<span class=block></span>
<span class=block></span>
<span class=block></span>
<span class=block></span>
</span>
Two important caveats:
For old IE support to work properly you need to use tags that are inline by default, like span. Using a tag that's block by default, like div, will look great in Chrome etc but fail in IE8.
You need to set vertical-align: top in the inner tags to get things to work the way you'd expect them (otherwise everything acts like vertical-align: bottom).
Essentially how column layouts work is you have a container that a width and you split that container into columns. When you decide how many columns you want for your design you assign each of those columns a percentage. Then you float those columns next to each other.
So if you have a 12 column layout the column classes would be as follows:
.col-1 { width: 4.8076923077% }
.col-2 { width: 13.4615384615% }
.col-3 { width: 22.1153846154% }
.col-4 { width: 30.7692307692% }
.col-5 { width: 39.4230769231% }
.col-6 { width: 48.0769230769% }
.col-7 { width: 56.7307692308% }
.col-8 { width: 65.3846153846% }
.col-9 { width: 74.0384615385% }
.col-10 { width: 82.6923076923% }
.col-11 { width: 91.3461538462% }
.col-12 { width: 100%; margin: 0 }
The above columns take into account margins and padding, but here's a fiddle that lays it out.
Parent element width within a content column refers to the column width, a property that defaults to auto. A columnar element is either defined by column-width or column-count (but not both, column-count overrides any column-width value if they are both non-auto). So in your example, providing column-count is causing an automatically generated width to be presented as container width.
source - right above example IX here: http://www.w3.org/TR/2011/CR-css3-multicol-20110412/
Column boxes act as the containing block for their content. That is,
column boxes behave like block-level, table cell, and inline-block
boxes as per CSS 2.1, section 10.1, item 2 [CSS21]. However, column
boxes do not establish containing blocks for elements with ‘position:
fixed’ or ‘position: absolute’.

How do you set a floating div's width to take up remaining space without pushing other divs down?

For part of a layout I want to make, I want to use three divs, all floating next to each other. The Left and Right have a max-width set, which works fine, but I want the middle div to expand its width to fill the remaining space. To clarify, the left and right divs may have a width of anywhere from 0px to the max-width, depending on what is in each, and I want the middle div to expand its width so that it takes up the rest of the space not used by the divs on either side.
The problem it's having now is that if there is a lot of content in the middle div, it's expanding and pushing the right div off to the next line instead of keeping it up with the other two.
Here's the css I have so far:
#left-column {
width: auto;
max-width: 200px;
height: auto;
float: left;
}
#middle-column {
float: left;
width: auto;
}
#right-column {
width: auto;
max-width: 200px;
height: auto;
float: right;
}
...and the HTML:
<div id="left-column">...</div>
<div id="middle-column">...</div>
<div id="right-column">...</div>
I think that this can be accomplished using a three-column, single-row table, but I absolutely do NOT want to use tables - I want to accomplish as much as possible by using pure css.
Thanks!
Classic Floats
If you order it:
<div id="left-column"></div>
<div id="right-column"></div>
<div id="middle-column"></div>
and you float the left column left, and the right column right, the middle column should fill in the remaining space. You will have some issues with margins, borders and paddings though.
Flexbox
If you don't need to support older browsers, you can use flexbox. With flexbox, this sort of structure becomes much simpler, and the markup doesn't need to change.
You will need to be able to select the parent element, so for the purposes of this demo, the code will be wrapped by <div class="wrapper">.
.wrapper {
display: flex;
flex-direction: row;
height: 200px;
}
.left {
background-color: red;
width: 100px;
}
.middle {
background-color: green;
flex: 1;
}
.right {
background-color: blue;
width: 100px;
}
<div class="wrapper">
<div class="left"></div>
<div class="middle"></div>
<div class="right"></div>
</div>
The height and widths are added explicitly so that the <div>s are visible. With actual content, the columns would automatically adjust.
I don't want to dredge up an old thread here but I was looking for a solution to my own problem and came across this and I thought I'd better share with Francisco...
Tables are a terrible idea for positioning layout, the main problem is that before a table will show/render in the browser it has to render it's </table> tag.
Could you imagine if Facebook's column content used a table for it's layout, it would take ages for it to render anything to the screen when checking your timeline for instance!
Another issue is that tables behave extremely differently in each browser.
Basically: <table> for layout = NO!, <table> for listing out rows of data or information = YES!

Resources