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>
Related
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>
Firstly, let me say that unfortunately I do have to support IE11 still and I don't believe this is a duplicate question, although I have found a few that were kinda similar.
I have a simple modal window which contains 3 flexible components in a column, header, footer and main.
The plan is that the outer box should grow as the content grows, until it is 80% of the height of the screen, at which point the middle section of the modal which is set to overflow-y:auto should get a scrollbar and the main modal will not get any taller.
Here is my markup
<div class="modal-wrapper">
<div class="modal">
<div class="modal-header">Header</div>
<div class="modal-main">
<div>Content goes here, could get very long</div>
</div>
<div class="modal-footer">Footer</div>
</div>
</div>
Fairly standard stuff. The modal is set to flex and the header and footer are fixed height. The middle section is set to grow and shrink as necessary. The main thing is that the .modal should never overflow the .modal-wrapper.
I have a jsfiddle set up and it's tested in Chrome, Firefox, Safari and iOS and it's working fine if you drag the bottom right box height up and down you'll see how it is supposed to behave. IE11 though is a mess.
https://jsfiddle.net/jonhobbs/sf6untnt/3/
Now, I have a feeling it may be related to the min-height bug here:
https://connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview
but I'm not convinced it's exactly that bug because none of the workarounds for that bug seem to work (e.g. using min-height:1px instead of 0, wrapping in another flexbox etc).
Hopefully somebody on SO can take a look at the jsfiddle and see an obvious problem
Maybe if you make it a flex child and use flex:0 1 80%; , it should fixe your trouble with IE :
example
html, body{
height: 100%;
display:flex;
flex-flow:column;
}
.modal-wrapper{
display: flex;
flex-direction: column;
width: 70%;
margin: 0 auto;
flex:0 1 80%;/* IE gets it , because the flow is column */
max-height:80%;/* others such as FF gets it */
background: white;
}
.modal{
display: flex;
flex-glow: 1;/* doesn't exist */
flex/*-shrink*/: 1; /* good enough */
flex-direction: column;
min-height: 1px;
}
.modal-main{
flex: 1;/* good enough */
min-height: 1px;
overflow-y: auto;
padding: 20px;
}
.modal-header, .modal-footer{
flex-grow: 0;
flex-shrink: 0;
height: 60px;
color: white;
line-height: 60px;
text-align: center;
background: dodgerblue;
}
<div class="modal-wrapper">
<div class="modal">
<div class="modal-header">Header</div>
<div class="modal-main">
<div>This content could get very long so I'm going to put a big long div in it</div>
<div style=" width:100px; height:1000px; background-color:red; opacity:0.1;"></div>
</div>
<div class="modal-footer">Footer</div>
</div>
</div>
https://jsfiddle.net/sf6untnt/7/
I have a centered div with a width of 700px, with a middle part that must become a right column when viewport is > to some width. I used absolute positioning for that purpose but like this column must be responsive, I don't know its width.
First, I would like to know what is the rule for how behave the width of absolute positioned elements which are out of their relative parent. Absolute positioning should use the width of their relative parent but when the element is out of that parent, the element is shrinked. If there is a word without space, it extends the element accordingly and everything follows. I don't understand how it works and how predict that behavior. It's the same when that element without width is supposed to start overflowing out of its parent.
Then, is there a way to make this column fills the right until it reaches the limit of the window without overflowing (with a little margin-right)? If I fix a big width on that column assuming it will be the max-width that column will achieve in the biggest viewport and use the overflow property to hide what is out of the window, of course, the absolute positioned element is just cut.
I really don't know how to make that responsive because it seems like absolute positioning removes the element from the flow, it is not made for my purpose. Of course, no JS, please. And it must support Internet Explorer since IE8.
The only solution that comes to my mind is to duplicate the content and use display:none/block to switch blocks with media queries but it means redundant code. I tried with a complicated display:table layout until I found that colspan doesn't exist.
(Just so you know, I have a left column too to take into consideration, the reason why I am using a three columns display:table layout. If that's relevant.)
Here is a simplified code:
I didn't put media queries but the aside-on-small-screen is obviously what it should look like on small screens, replacing the aside selector.
main{
overflow:hidden;
}
.colMain{
background-color:green;
margin-left:auto;
margin-right:auto;
position:relative;
width:300px;
}
.aside{
background-color:red;
position:absolute;
top:0px;
left:320px;
}
.aside-on-small-screen{
background-color:red;
}
<main>
<div class="colMain">
<div>stuff</div>
<div class="aside">aside that must extend all the way to the right until it reaches the window limit</div>
<div>stuff</div>
</div>
</main>
Thank you.
Used flexbox and assigned aside a percentage width. The details are in the CSS portion of Snippet.
Flexbox
justify-content: space-between
order
flex-shrink, flex-grow, flex-basis
Relative units of measurement
Viewport width/height vw and vh
Percentage
em
/* Optional Defaults and Resets */
* {
-ms-box-sizing: border-box;
box-sizing: border-box;
}
html {
font: 400 10px/1 Arial;
width: 100%;
height: 100%;
}
/*,
*:before,
*:after {
box-sizing: inherit;
margin: 0;
padding: 0;
border: none;
}*/
body {
font: inherit;
font-size: 160%;
/* background: rgba(0, 0, 0, .2);*/
line-height: 1;
overflow: visible;
width: 100%;
height: 100%;
}
/* Demo Styles */
/* All outlines and backgrounds are for presentational purposes */
/* vw/vh viewport width/height 1vw/vh = 1% of viewport width/height */
main {
overflow: hidden;
/* width: 100vw;
height: 100vh;
background: rgba(0, 0, 255, .2);*/
width: 100%;
height: 100%;
min-height: 35em;
display: table;
}
/* Flexbox layout will automatically keep .aside to the right with the */
/* property justify-content: space-between; which keeps the max amount */
/* of even space between flex-items (which is .stuff and .aside) */
.colMain {
/* display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between; */
background-color: green;
margin-left: auto;
margin-right: auto;
position: relative;
min-width: 99%;
min-height: 99%;
padding: 1em;
display: table-row;
width: 700px;
}
/* Removed absolute positioning in favor of flexbox and a percentage */
/* width. .aside will start dis-proportionally expanding while the viewport */
/* expands. The two columns on the right while begin to shrink in response */
/* to.aside's expansion. All this stretching and shrinking happens when the */
/* elements are at 210px or more (210 is 30% of 700px). This behavior is */
/* accomplished by using flex-shrink, flex-grow, and flex-basis */
.aside {
display: table-cell;
background-color: red;
position: absolute;
top: 1em;
right: 0;
left: 70%;
/* order: 3; */
min-width: 30%;
max-width: 500px;
min-height: 100%;
/* flex-grow: 1;
flex-basis: 210px;*/
outline: 2px solid #7c1b38;
padding: 5px;
}
.aside-on-small-screen {
background-color: red;
}
.stuff {
outline: 2px dotted white;
width: 30%;
max-width: 210px;
min-height: 100%;
position: absolute;
/* flex-shrink: 1;
flex-basis: 210px; */
display: table-cell;
}
#col1 {
left: 1em;
top: 1em;
}
#col2 {
left: 36%;
top: 1em;
}
/*.stuff:first-of-type {
order: 1;
}
.stuff:last-of-type {
order: 2; */
}
/* The HTML shows that the second column (the second .stuff) would be */
/* in-between .aside and the edge of .colMain. Instead of moving it out of */
/* the way in markup (HTML), I used the flexbox property, order. */
<main>
<div class="colMain">
<div id="col1" class="stuff">stuff</div>
<div class="aside">aside that must extend all the way to the right until it reaches the window limit</div>
<div id="col2" class="stuff">stuff</div>
</div>
</main>
There are three problems in one:
First problem.
How to transform a middle content
<div class="wrapper">
<div>stuff</div>
<div class="aside">Middle content</div>
<div>stuff</div>
</div>
in a right column that expends to the right without overflowing out of the window, when the rest of the past column "wrapper" must be a centered column of fixed width.
<div class="colLeft"></div>
<div class="wrapper" style="text-align:center; width:700px;">
<div>stuff</div>
<div>stuff</div>
</div>
<div class="aside">Middle content now to the right</div>
Absolute positioning doesn't help because without fixed sizes (% or px), it is out of the flow and the content of variable width won't adapt to the situation (and overflow).
This can be easily solved with display table.
Second problem.
Display table/table-cell leads to the second problem.
To make three "columns" with display:table-cell, order is really important. That means the "aside" div must be the last element of its column (the wrapper column in my first snippet) in order to make it an independent cell of a row put to the right. If you don't have to worry about this story of middle content and you just have to switch a content at the end of a div to the right or a content at the beginning to the left, it's already over.
You just have to style colLeft, wrapper and aside of my second snippet with display:table-cell and use another global wrapper with display:table and some other styles like table-layout:fixed and width:100% to do the trick. With a media queries for small screen, you just have to hide the colLeft with display:none.
But if you need that middle content to be a middle content nonetheless on small screens and a right column on large screens, it's a different case.
This can be solved with anonymous table objects and table-header/footer/row-group.
With table-header/footer/row-group, you can reorganize your rows so you can put the "aside" at the end to transform it in an independent cell on large screens and place it in the middle with table-row-group on small screens:
.header{
background-color:green;
display:table-header-group;
}
.footer{
background-color:green;
display:table-footer-group;
}
.aside{
background-color:red;
display:table-row-group;
}
<div class="header">stuff</div>
<div class="footer">stuff</div>
<div class="aside">Middle content</div>
Third problem.
The hardest problem is the centered "column" of fixed width. With table-xxx-group, it is forbidden to put a wrapper around the table-header-group and table-footer-group to set a width of 700px because table-group are row elements and the wrapper will automatically becoming a table object, excluding the "aside" that won't be able to insert itself in the middle with its table-row-group style on small screens.
Without putting a wrapper around the "stuff", you won't be able to control the width of the created anonymous cell on large screens because you can't style something anonymous. So it takes a width of 1/3 like each cell.
main{
display:table;
table-layout: fixed;
width:100%;
}
.colLeft{
background-color:yellow;
display:table-cell;
}
.header,.footer{
background-color:green;
/*no display style > it will create an anonymous cell
object around the header/footer elements*/
}
.aside{
background-color:red;
display:table-cell;
}
<main>
<div class="colLeft"></div>
<div class="header">stuff</div>
<div class="footer">stuff</div>
<div class="aside">Middle content now to the right</div>
</main>
The solution is to use table-column-group/table-column. You will be able to style your columns and set a width to the middle column even though it is determined anonymously.
The solution
Small screens
.rowTabled{
display:table;
table-layout: fixed;
width:100%;
}
.header{
background-color:green;
display:table-header-group;
}
.footer{
background-color:green;
display:table-footer-group;
}
.aside{
background-color:red;
display:table-row-group;
}
.colLeft, .colgroup{
display:none;
}
<main>
<div class="colgroup">
<div class="colCol left"></div>
<div class="colCol middle"></div>
<div class="colCol right"></div>
</div>
<div class="rowTabled">
<div class="colLeft"></div>
<div class="header">stuff</div>
<div class="footer">stuff</div>
<div class="aside">asideeeeeeeeeeeex eeeeee eeeeeeeee eeeeeeeeeeeeee</div>
</div>
</main>
Large screens
main{
display:table;
table-layout: fixed;
width:100%;
}
.colgroup{
display:table-column-group;
}
.colCol{
display:table-column;
}
.middle{
background-color:green;
width:100px;
}
.left,.right{
background-color:yellow;
}
.rowTabled{
display:table-row;
}
.colLeft{
display:table-cell;
}
.aside{
background-color:red;
display:table-cell;
}
<main>
<div class="colgroup">
<div class="colCol left"></div>
<div class="colCol middle"></div>
<div class="colCol right"></div>
</div>
<div class="rowTabled">
<div class="colLeft"></div>
<div class="header">stuff</div>
<div class="footer">stuff</div>
<div class="aside">asideeeeeeeeeeeex eeeeee eeeeeeeee eeeeeeeeeeeeee</div>
</div>
</main>
I'm trying to create a flexible layout in CSS3 with 3 boxes stacked on each other. The boxes are siblings and thus have the same parent element. The height of the first box must fit its content. The height of the following two boxes shall grow to fit their respective content until they're about to overflow their parent. In that case, they shall shrink so that they don't overflow.
The problem is that I can't figure out how to prevent one of the shrinking boxes from becoming crushed if its content is small in relation to the other shrinking box. I want those boxes to shrink down to a certain point where they won't shrink anymore – let's say the equivalent of two rows of text for example. Setting min-width isn't an option because I don't want the boxes to be taller than their content in case the content is only one row for example. If any of the boxes has come to the point where it shall not shrink anymore and the parent can't hold them without overflowing, the parent shall get a scrollbar.
I don't know the content in advance so the layout has to be dynamic. I want to solve this only with CSS, if possible.
Here's an example of the problem where box3 is too small:
p {
margin: 0;
}
.container, .box {
border: 1px solid black;
}
.box {
background-color: white;
margin: 1em;
overflow: auto;
}
#container {
background-color: yellow;
display: flex;
flex-direction: column;
height: 15em;
overflow: auto;
}
#box1 {
flex: 0 0 auto;
}
#box2 {
}
#box3 {
}
<div id="container" class="container">
<div id="box1" class="box">
<p>◼</p>
</div>
<div id="box2" class="box">
<p>◼◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◻◻◻◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◻◻◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◻◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◻◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◻◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◻◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◻◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◻◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◻◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◻◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◻◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◼◻◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◻◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◻◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◻</p>
<p>◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼◼</p>
</div>
<div id="box3" class="box">
<p>◼◻◻</p>
<p>◼◼◻</p>
<p>◼◼◼</p>
</div>
</div>
IF I understand your question correctly, the flex-shrink property should be what you are looking for.
Set #box1 to flex-shrink: 0
Set #box2 to flex-shrink: 1
Set #box3 to flex-shrink: 1
<div id="foo">
<div class="bar">
there are very many divs like this
</div>
</div>
CSS
#foo{
margin: 10px auto;
min-height: 400px;
min-width: 800px;
}
.bar{
float: left;
text-align: left;
width: 450px;
min-height: 280px;
}
The div #foo can have a variable width from 800px to 100% of containing media and that the div .bar can have a variable height. On wide displays I want three columns of .bar divs but since they are variable height it's like they slide to the left of the div above hence those on the left have spaces above them. How do I make them float upwards. I hope I made sense.
I suggest you look at this post: Does anyone know how Pinterest.com's layout works? and possibly these jQuery plugins: http://masonry.desandro.com/ and http://www.wookmark.com/jquery-plugin.
A CSS3 solution: http://cssdeck.com/labs/css-only-pinterest-style-columns-layout