Inserting divs into flex regular columns: column width and overflow problems - html

I need to put 3 columns inside a container. They are supposed to have the same width and evenly distributed. I use flex for that and it works fine.
In the end I need to insert a collection of "cards" in each column ... Because the number of cards can cause the columns to have a greater height than what can be displayed in the browser, the container (the parent of the columns) has overflow set to scroll. So when that happens you can still scroll down to look at the cards that are at the bottom of the column.
I have two problems that I'd like to fix:
1) As soon as I insert a card in the left column, that columns becomes larger than the other two. If I set the column to flex and set flex-grow to 0 on the card, it makes no difference. I am not sure why?
EDIT:
I fixed #1 by setting width: 33%; on .column.
===========================================================================
2) The left column is not drawn passed the bottom boundary of the browser, even though it contains cards causing the container to potentially overflow. What I want to do is be sure that the column "contains" the cards. How could I achieve that (using flex or not)?
I created a fidle and illustrated this problem with the figure below.
JSFIDDLE is here

1) As soon as I insert a card in the left column, that columns becomes
larger than the other two. If I set the column to flex and set
flex-grow to 0 on the card, it makes no difference. I am not sure why?
flex-grow does not define the width, it define how the available space should be distributed.
You could use flex-basis: 33.333%
2) The left column is not drawn passed the bottom boundary of the
browser, even though it contains cards causing the container to
potentially overflow. What I want to do is be sure that the column
"contains" the cards. How could I achieve that (using flex or not)?
Change the height: 100% on container to min-height: 100%
Stack snippet
html, body
{
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.container
{
display: flex;
flex-direction: row;
border: 2px solid black;
overflow: scroll;
padding: 0;
margin: 0;
min-height: 100%; /* changed to 'min-height' */
width: 100%;
box-sizing: border-box;
}
.column
{
display: flex;
flex-direction: column;
flex: 1 1 auto;
background: grey;
margin: 5px;
}
.article
{
margin: 5px;
background: white;
height: 100px;
border: 1px solid orange;
}
<div class="container">
<div class="column">
<div class="article">article1</div>
<div class="article">article2</div>
<div class="article">article3</div>
<div class="article">article4</div>
<div class="article">article5</div>
<div class="article">article6</div>
<div class="article">article7</div>
</div>
<div class="column">col 2</div>
<div class="column">col 3</div>
</div>

To make the columns equal width, instead of flex: 1 1 auto, use flex: 1 1 0 (which is the same as flex: 1).
With flex-basis: auto the width of each column is based on its content, so equal width columns aren't guaranteed.
With flex-basis: 0, the width of all columns is an equal distribution of space in the container.
Here's a more complete explanation of flex-basis and the difference between auto and 0.
Make flex-grow expand items based on their original size
To make the divs respect their height: 100px, add flex-shrink: 0. Flex items have flex-shrink: 1, by default.
Here's a more complete explanation:
What are the differences between flex-basis and width?
To get the container to follow along, see the other answer to this question.
html,
body {
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-direction: row;
border: 2px solid black;
overflow: scroll;
padding: 0;
margin: 0;
min-height: 100vh;
box-sizing: border-box;
}
.column {
display: flex;
flex-direction: column;
background: grey;
margin: 5px;
flex: 1; /* adjustment */
}
.article {
margin: 5px;
background: white;
border: 1px solid orange;
flex: 0 0 100px; /* adjustment */
}
<div class="container">
<div class="column">
<div class="article">article1</div>
<div class="article">article2</div>
<div class="article">article3</div>
<div class="article">article4</div>
<div class="article">article5</div>
<div class="article">article6</div>
<div class="article">article7</div>
</div>
<div class="column">col 2</div>
<div class="column">col 3</div>
</div>

Related

Is it possible to make one flex line stretch without stretching the other one?

I am trying to implement a responsive sidebar, that changes to a fixed height header when there is not enough horizontal space.
The following code works, except that I'm unable to make the second flex line stretch and occupy maximum space, while giving the first line the minimum space needed.
As per the spec (https://www.w3.org/TR/css-flexbox-1/#flex-lines):
In a multi-line flex container (even one with only a single line), the cross size of each line is the minimum size necessary to contain the flex items on the line (after alignment due to align-self), and the lines are aligned within the flex container with the align-content property.
But I seem to be missing something, because the available vertical seem to be equally shared by the flex lines.
I tried giving .bar min-hieght: 5em, but it is still occupying half of the height when wrapping occurs.
Is it impossible to implement without using media queries?
Code:
body {
background: yellow;
color: white;
margin: 0;
padding: 0;
position: absolute;
width: 100%;
min-height: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.bar {
background: grey;
margin: 0;
padding: 1em;
min-width: 15em;
min-height: 5em;
flex-grow: 1;
}
.content {
background: darkgrey;
margin: 0;
padding: 1em;
flex-grow: 999;
flex-shrink: 1;
flex-basis: 40em;
}
<aside class="bar">
<h1>Bar</h1>
</aside>
<main class="content">
<h1>Content</h1>
</main>
JsFiddle: https://jsfiddle.net/074r8nhs/31/

Flexbox: div ignore height property

I'm building a modal with flexbox, that has a two row design:
|-------------------|
| A |
|-------------------|
| |
| B |
| |
| |
|-------------------|
Since both A and B are part of a modal with height express in percentage and not in px I use flexbox in order to give to A an height and then to make B fill the rest of the space with an optional scrollbar if needed.
In order to achieve this I use the following html + css :
<div class="wrapper">
<div class="A"></div>
<div class="B"></div>
</div>
.wrapper {
display: flex;
flex-direction: colum;
height: 100%;
}
.A {
height: 70px;
width: 100%;
}
.B {
margin: 10px 20px 10px 20px;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
The problem is that depending on the content of B, the height of A changes. With some content is fixed to 70px, with some other content is maybe 24px and if I inspect the css rules I can see that the height is 24px and when I click on the rule it highlights me the "height: 70px" line of code.
If I remove the "display: flex" in the wrapper the height remains correct with any kind of content but the div B overflow the modal and ignores completely the rule "overflow-y: auto".
How can I solve this?
If you use flex: 0 0 70px; combined with either min-height: 0; or overflow: hidden, it should make sure the A always stays 70px high.
The first 0 is flex-grow which prevent it from grow, the second 0 is flex-shrink which prevent it from shrink, and the min-height/overflow will allow it to also be smaller than its content, which by default it is not (min-height defaults to auto, which prevent a flex item be smaller than its content).
The last 70px is flex-basis which is the Flexbox version for height, and I recommend you use that instead.
Note, you are missing an n in colum for the flex-direction value.
.wrapper {
display: flex;
flex-direction: column;
height: 90vh;
background: lightgray;
}
.A {
flex: 0 0 70px;
width: 100%;
min-height: 0;
background: lightblue;
}
.B {
margin: 10px 20px 10px 20px;
flex-grow: 1;
overflow-y: auto;
overflow-x: hidden;
background: lightgreen;
}
<div class="wrapper">
<div class="A">A</div>
<div class="B">B</div>
</div>
If the contents of B is "taller" than the height of B, B grows in height to somehow accommodate the contents. This causes the height of A to decrease to adjust. In order to disallow A from shrinking, add flex-shrink: 0 to its styles.

column flex with height 0 padding trick

I have 2 divs that are placed on top of each other. For purposes of alignment, I am using display: flex and flex: column on the div containing these two divs. However, the first div uses the "height 0 padding" trick for videos. The problem I'm having, is that when using flex: column and change the width of the screen, the height doesn't change (and I want the height to change so that it matches the ratio for the video). What ends up happening is that the div stays the same, and the video shrinks within it and it looks ugly because there is extra background.
Plunker: https://plnkr.co/edit/TaeF5f8VufJWPU3GRZPr?p=preview
(in short, I want it such that when I change the width of the browser, the red div's height gets smaller)
CSS
/* Styles go here */
body {
height: 100%;
}
.container {
height: 80vh;
display: flex;
flex-flow: column;
}
.video {
flex: none;
height: 0;
padding-bottom: 30%;
background-color: red;
width: 80%;
}
.next-content {
flex: 1 0 auto;
width: 80%;
background-color: blue;
}
HTML:
<body>
<h1>Hello Plunker!</h1>
<div class="container">
<div class="video"></div>
<div class="next-content"></div>
</div>

How can I set a minimum amount of space between flexbox items?

Given this CSS:
div.container {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
border: 1px solid blue;
}
div.container div {
width: 200px;
border: 1px solid gray;
display: inline-block;
text-align: center;
}
This layout has the first item in each row aligned to the left, and the last item aligned to the right, as required.
As the browser window is made narrower, the distributed div elements will move closer together until they touch, at which point they are re-arranged over an additional row. Again, the first div on each row is aligned left, and the last aligned right with space between.
Is there any way of setting a minimum spacing so that the inner div elements always have a gap between them.
padding and margin will probably not work, as the alignment
<-- 1st left in row and last right in row --> will not hold.
Bit late the the party but I ran into the same issue. The way I solved it probably wont work for everyone but here it is for those who can use it.
The basic idea is that you have a min gap of x. You set the left and right margins of each item to x/2 so that the distance between the items will be x (margin + margin). Then you wrap all of the items in a container with a left and right margin of -x/2. This will hide the margin on the items at the edges of each row.
Here is a working example:
.box {
border: 1px solid black;
overflow-x: hidden;
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin: 0 -1em;
}
.item {
display: flex;
border: 1px solid grey;
padding: 1em;
width: 20%;
margin: 0 1em;
}
<div class="box">
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
</div>
</div>
The overflow-x: hidden; on .box is to prevent the horizontal scrollbar that shows up in some browsers because of the margin overflowing.
If you want the gap to always be consistent and for rows with only one item to have that item span the whole row then you can add flex-grow: 1 to .item.
You can add another div with flex style for holding the needed gap between inner divs. and for the minimum width for that gap use this property (as mentioned in W3Schools.com):
flex: flex-grow flex-shrink flex-basis|auto|initial|inherit;
which flex-shrink is :
flex-shrink: A number specifying how much the item will shrink relative to the rest of the flexible items
so, for example you set this css code for the gap div :
flex: 1 0 10px;
that tells gap div will have 10px width, and will grow relative to the rest of the flexible items, but WON'T SHRINK. so the minimum width will be 10px at the narrowest width of the screen.
In 2022 you can just use gap CSS property:
div.container {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
border: 1px solid blue;
gap: 20px;
}
To support older browsers you may use margin hack.
div.container > * {
margin: 12px 0 0 12px;
}
div.container {
display: inline-flex;
flex-wrap: wrap;
margin: -12px 0 0 -12px;
width: calc(100% + 12px);
}
Since April 2021 support for flexbox-gap has arrived in all major browsers (IE considered dead). Combining it w/ space-between solves your problem.
div.container {
display: flex;
gap: 10px; /* minimum gap between flex-items */
justify-content: space-between;
}
It's a couple of days passed since this question was asked, but I thought I should add my solution if anybody comes past and has the same issue.
I suggest using calc, width, and media to solve this issue. Yes, it's a little work but it's a visual clean solution in my opinion.
.main{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.main > div{
width: 100%;
height: 125px;
border: 1px solid red;
}
#media (min-width: 576px) {
.main > div{
width: calc(100% / 2 - 5px);
margin-bottom: 5px;
}
}
#media (min-width: 992px) {
.main > div{
width: calc(100% / 3 - 5px);
}
}
#media (min-width: 1140px) {
.main > div{
width: calc(100% / 6 - 5px);
margin-bottom: 0;
}
}
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
For the needed breakpoints I calculate the width I want the divs to use and subtract the space I want them to have.
I hope this helps someone and that I explained it understandable.
Regards.
Setting a flex-basis with percentage also will do the trick. Then the min space between will be also in percentage.
For instance, with 3 elements, flex: 0 0 30% will allow a fixed 10% space reparted between elements.
with 6 elements, flex: 0 0 15% and so on.

How can I have two fixed width columns with one flexible column in the center?

I'm trying to set up a flexbox layout with three columns where the left and right columns have a fixed width, and the center column flexes to fill the available space.
Despite setting up dimensions for the columns, they still seem to shrink as the window shrinks.
Anyone know how to accomplish this?
An additional thing I will need to do is hide the right column based on user interaction, in which case the left column would still keep its fixed width, but the center column would fill the rest of the space.
#container {
display: flex;
justify-content: space-around;
align-items: stretch;
max-width: 1200px;
}
.column.left {
width: 230px;
}
.column.right {
width: 230px;
border-left: 1px solid #eee;
}
.column.center {
border-left: 1px solid #eee;
}
<div id="container">
<div class="column left">
<p>Anxiety was a blog series that ran in the New York Times Opinion section from January 2012 to July 2013. It featured essays, fiction, and art by a wide range of contributors that explored anxiety from scientific, literary, and artistic perspectives.</p>
</div>
<div class="column center">
<img src="http://i.imgur.com/60PVLis.png" width="100" height="100" alt="">
</div>
<div class="column right">
Balint Zsako
<br/> Someone’s Knocking at My Door
<br/> 01.12.13
</div>
</div>
Here's a JSFiddle: http://jsfiddle.net/zDd2g/185/
Instead of using width (which is a suggestion when using flexbox), you could use flex: 0 0 230px; which means:
0 = don't grow (shorthand for flex-grow)
0 = don't shrink (shorthand for flex-shrink)
230px = start at 230px (shorthand for flex-basis)
which means: always be 230px.
See fiddle, thanks #TylerH
Oh, and you don't need the justify-content and align-items here.
img {
max-width: 100%;
}
#container {
display: flex;
x-justify-content: space-around;
x-align-items: stretch;
max-width: 1200px;
}
.column.left {
width: 230px;
flex: 0 0 230px;
}
.column.right {
width: 230px;
flex: 0 0 230px;
border-left: 1px solid #eee;
}
.column.center {
border-left: 1px solid #eee;
}
Despite setting up dimensions for the columns, they still seem to shrink as the window shrinks.
An initial setting of a flex container is flex-shrink: 1. That's why your columns are shrinking.
It doesn't matter what width you specify (it could be width: 10000px), with flex-shrink the specified width can be ignored and flex items are prevented from overflowing the container.
I'm trying to set up a flexbox with 3 columns where the left and right columns have a fixed width...
You will need to disable shrinking. Here are some options:
.left, .right {
width: 230px;
flex-shrink: 0;
}
OR
.left, .right {
flex-basis: 230px;
flex-shrink: 0;
}
OR, as recommended by the spec:
.left, .right {
flex: 0 0 230px; /* don't grow, don't shrink, stay fixed at 230px */
}
7.2. Components of
Flexibility
Authors are encouraged to control flexibility using the flex shorthand
rather than with its longhand properties directly, as the shorthand
correctly resets any unspecified components to accommodate common
uses.
More details here: What are the differences between flex-basis and width?
An additional thing I need to do is hide the right column based on user interaction, in which case the left column would still keep its fixed width, but the center column would fill the rest of the space.
Try this:
.center { flex: 1; }
This will allow the center column to consume available space, including the space of its siblings when they are removed.
Revised Fiddle
Compatibility with older browsers can be a drag, so be adviced.
If that is not a problem then go ahead.
Run the snippet. Go to full page view and resize. Center will resize itself with no changes to the left or right divs.
Change left and right values to meet your requirement.
Thank you.
Hope this helps.
#container {
display: flex;
}
.column.left {
width: 100px;
flex: 0 0 100px;
}
.column.right {
width: 100px;
flex: 0 0 100px;
}
.column.center {
flex: 1;
text-align: center;
}
.column.left,
.column.right {
background: orange;
text-align: center;
}
<div id="container">
<div class="column left">this is left</div>
<div class="column center">this is center</div>
<div class="column right">this is right</div>
</div>
.column.left {
width: 230px;
flex: 0 0 230px;
}
.column.right {
width: 230px;
flex: 0 0 230px;
border-left: 1px solid #eee;
}
.column.center {
border-left: 1px solid #eee;
}