flexbox height adjust to content - css

I use a "full design" flexbox.
I have a weird issue : I have a container that takes all the remaining space and I want in this container that the child, which is also flexbox, to have their height adjust to their content.
Here is the issue:
body, html {
width:100%;
height:100%;
display:flex;
}
.container {
display:flex;
flex:1;
flex-wrap:wrap;
}
.icon {
width:10vh;
margin:10px;
display:flex;
flex-direction:column;
}
.img {
width:10vh;
height:10vh;
display:flex;
align-items:center;
justify-content:center;
background-color:red;
}
.text {
text-align:center;
}
<div class="container">
<div class="icon">
<div class="img">
</div>
<div class="text">
action 1
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 2
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 3
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 4
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 5
</div>
</div>
</div>
As you can see, the icon takes the full height of the container : in fact, I don't want to specify a height because I don't know the text length and really want that, if the content is huge, the icon takes the height of its content ( don't want to cut the text). Moreover, if the page is resized, I really want the icon to be aligned (like on smartphone).
Also, I don't understand why the icon takes the height of its parent and not its content because I didn't specify "flex:1" on it. I assume that the default behaviour it's to fit the content size, but this seems not to be working.
image of the issue

.icon's are flex-column which makes .img's stretch by default unless .icon's have align-items. The reason why I didn't apply align-items to .icon's is because other nested flex-containers/flex-items started collapsing. Instead of adjusting down through the hierarchy, I went up and adjusted .container instead.
The relevant CSS:
.container {
display: flex;
flex: 1; /* If you remove this .container will shrink to wrap around .icon's */
flex-wrap: wrap;
justify-content: center; /* This centers .icon's along a horizontal axis. */
align-items: baseline; /* This aligns .icon's along a common baseline vertically. */
outline: 3px dashed blue; /* To show the size of .container */
}
.icon {
width: 10vh;
margin: 10px;
display: flex;
flex-direction: column;
outline: 1px dashed red; /* To show the size of .icon */
}
body,
html {
width: 100%;
height: 100%;
display: flex;
}
.container {
display: flex;
flex: 1;
flex-wrap: wrap;
justify-content: space-between;
align-items: baseline;
align-content: flex-start;
outline: 3px dashed blue;
}
.icon {
width: 10vh;
margin: 10px;
display: flex;
flex-direction: column;
outline: 1px dashed red;
}
.img {
width: 10vh;
height: 10vh;
display: flex;
align-items: center;
justify-content: center;
background-color: red;
}
.text {
text-align: center;
}
<div class="container">
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 1
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 2
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 3
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 4
</div>
</div>
<div class="icon">
<div class="img">
</div>
<div class="text">
Action 5
</div>
</div>
</div>

Related

Nested Flexbox - outer container doesn't flex

Update : I have edited the snippet to show better what I'm trying to achieve...
I have a number of tables of data, each of variable length, on a kiosk display. I want to fill the viewport as columns then overflow to pages below ie paging down would give me next screen of data. I thought Nested Flexbox would allow me to do this but the outer Container doesn't do what I hoped and data just flows to right - see below. Am I just inept or should I be doing it another way? Thx!
.container1 {
background: lightgrey;
display: flex;
width:300px;
flex-direction: column;
flex-wrap:wrap;
}
.container2 {
background: orangered;
display: flex;
flex-direction: column;
height:200px;
width: 300px;
flex-wrap: wrap;
}
.container2 > div{
font-size: 40px;
width: 100px;
}
.green {
background: yellowgreen;
}
.blue {
background: steelblue;
}
My effort doesn't work ...
<div class="container1">
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">5a<br>5b</div>
<div class="blue">6a<br></div>
<div class="green">7a<br>7b</div>
<div class="blue">8a<br>8b<br>8c</div>
<div class="green">9a<br>9b<br>9c</div>
<div class="blue">10a<br></div>
<div class="green">11a<br>11b</div>
<div class="blue">12a<br>12b<br>12c</div>
</div>
</div>
I want output like this but ...
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
</div>
<div class="container2">
<div class="green">5a<br>5b</div>
<div class="blue">6a<br></div>
<div class="green">7a<br>7b</div>
<div class="blue">8a<br>8b<br>8c</div>
</div>
<div class="container2">
<div class="green">9a<br>9b<br>9c</div>
<div class="blue">10a<br>10a<br></div>
<div class="green">11a<br>11b</div>
<div class="blue">12a<br>12b<br>12c</div>
</div>
You don't want to use flex-direction: column on the inner container2. You still want that to be row.
Setting flex-direction: column only establishes that the direction of children should flow from top to bottom (or reverse with column-reverse).
Setting flex-wrap: wrap on a parent with flex-direction: column wraps the elements on the cross axis (row in this case).
You don't even need the outer parent container, since there was only one flex child container2.
.container2 {
background: orangered;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 300px;
height: 200px;
overflow: scroll;
}
.container2 > div {
font-size: 40px;
flex: 0 0 33.33333%;
min-height: 200px;
}
.green {
background: yellowgreen;
}
.blue {
background: steelblue;
}
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
</div>

Fill vertical space with Flexbox in a list of items

I have a list of items, each one in display: flex to position its children elements. The height of the item is given by the image, and the button at the right position should fill the vertical space of the item. But I can't do that.
.Main-item {
display: flex;
border: 1px solid gray;
margin-bottom: 2px;
}
.Main-img {
height: 50px;
width: 50px;
overflow: hidden;
object-fit: cover;
object-position: center;
border-radius: 50%;
}
.Main-name {
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-start;
flex: 1 1;
padding: 0 10px;
}
.Main-buttonWrapper {
height: 100%;
width: 146px;
}
.Main-button {
height: 100%;
width: 100%;
}
<div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
</div>
Any idea will be welcome!
When you create a flex container various default flex rules come into play.
Two of these default rules are flex-direction: row and align-items: stretch. This means that flex items will automatically align in a single row, and each item will fill the height of the container.
If you change the value of align-items to for example flex-start it will change the default behavior 'stretch' as well if you set a specific height to the child of a flex container.
So just remove the height: 100% from .Main-buttonWrapper and it will have the default behavior.
For a better understanding look this answer: https://stackoverflow.com/a/33220564/4966662
I've given a height to the Main-item + align-items:stretch;. I hope this is what you need.
.Main-item {
align-items:stretch;
height: 50px;
}
.Main-element {
padding-top: 16px;
}
.Main-item {
display: flex;
border: 1px solid gray;
margin-bottom: 2px;
align-items:stretch;
height: 50px;
}
.Main-img {
width: 50px;
overflow: hidden;
object-fit: cover;
object-position: center;
border-radius: 50%;
}
.Main-name {
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: space-evenly;
align-items: flex-start;
flex: 1 1;
padding: 0 10px;
}
.Main-buttonWrapper {
width: 146px;
}
.Main-button {
width: 100%;
height: 100%;
}
<div class="Main-element">
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
<div class="Main-item">
<img class="Main-img" src="https://picsum.photos/200/300" />
<div class="Main-name">Lorem ipsum</div>
<div class="Main-buttonWrapper"><button class="Main-button">button</button></div>
</div>
</div>
A flex item stretches along the container cross axis by default, so the only thing you need to make the button stretch visually as well, is to make the button container a flex container by adding display: flex to .Main-buttonWrapper.

Maintain ratio of container and children while resize

I have modular grid of cards with and without image, based on flexbox.
Normally cards without image are 50% of the width, cards with image are 100% of the width, and those cards without image that doesn't have a pair are also 100%.
----------------
[[ txt ][ img ]] – card with an image
----------------
[ txt ][ txt ] – 2 cards without image
----------------
[ txt ] – card without image
----------------
[[ txt ][ img ]] – card with an image
----------------
Is it possible to maintain ratio of the cards and its content while resize, and also maintain modularity with css?
Here's the code
that let me maintain ratio, but when I try to apply this technique to cards children everything breakes
<div class="wrapper">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
.wrapper
display: flex
flex-wrap: wrap
justify-content: space-between
width: 620px
.card
height: 0
padding-bottom: 50%
display: flex
flex-direction: row
justify-content: space-between
flex: 1 1 300px
.has-card-image .card-text,
.has-card-image .card-image
height: 300px
width: 300px
I can see one way this could be done w/o script.
Either as in this case, using px values, or e.g. using vw, and the reason is that the card-text and card-image needs to know the wrapper's width to be able to size according to the expected output (which mean percent can't be used).
Fiddle demo
Stack snippet
.wrapper {
display: flex;
flex-wrap: wrap;
width: 600px;
}
.card {
display: flex;
flex: 1 300px;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 300px;
}
.card .card-text:only-child {
flex: 1 300px;
}
/* demo styles */
.card .card-text, .card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
If you need the margin, you'll need an extra wrapper
Fiddle demo
Stack snippet
.wrapper {
width: 620px;
overflow: hidden;
}
.inner {
display: flex;
flex-wrap: wrap;
margin-left: -20px;
}
.card {
display: flex;
flex: 1 300px;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 300px;
margin-left: 20px;
}
.card .card-text:only-child {
flex: 1 300px;
}
/* demo styles */
.card .card-text,
.card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="inner">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
</div>
Updated
Here is a version using vw and margin, and is responsive.
Note, the make up for margin set on the wrapper can also be set on each item, though I found it simpler like this.
Fiddle demo
Stack snippet
.wrapper {
width: calc(80vw + 20px); /* 20px extra for margin */
margin: 0 auto;
overflow: hidden;
}
.inner {
display: flex;
flex-wrap: wrap;
margin-left: -20px;
}
.card {
display: flex;
flex: 1 40vw;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 40vw;
margin-left: 20px;
}
.card .card-text:only-child {
flex: 1 40vw;
}
/* demo styles */
.card .card-text,
.card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="inner">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
</div>

Align flex children to top

How do I get flexbox children to line up vertically to the top edge of each row?
HTML:
#container {
align-items: flex-start;
align-content: flex-start;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.child {
margin: auto;
border: 1px dotted #CCC;
}
img, h3 {
width: 160px;
}
<div id="container">
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>The quick brown fox jumps over the lazy dog</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
</div>
Demo: https://codepen.io/anon/pen/GOBzOp
What I see is but I want it to look like
change margin:auto of .child to margin: 0px auto.
Give justify-content: space-around; to #container id instead of justify-content: space-between; and remove margin: auto; to .child class.
#container {
align-items: flex-start;
align-content: flex-start;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
}
.child {
border: 1px dotted #CCC;
}
img, h3 {
width: 160px;
}
<div id="container">
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>The quick brown fox jumps over the lazy dog</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
<div class="child">
<img src="http://via.placeholder.com/160x160">
<h3>Title</h3>
</div>
</div>
A simple fix would be changing margin: auto; to margin: 0 auto; this will prevent the box auto centralize vertically but retaining its horizontal alignment, like so:
.child {
border: 1px dotted #CCC;
margin: 0 auto;
}
you can try like
.child {
vertical-align: baseline; //either of them
margin: 0px auto; // any one
}

Flex Row space between wrapped items

What is the most dynamic way of having any amount of rows with any amount of items with a set margin between all the items? Right now the only thing that works for me is to wrap each item in a wrapper set the flex basis to the wrapper and the margin to the child. The issues with this is I loose the ability to have each row the same height of the tallest content in the row.
Case 1: Only margin bottom
https://jsfiddle.net/6oaney4e/6/
This works well because the content keep the height of the tallest item on each row
html
<div class="wrapper">
<div class="item">
text
</div>
<div class="item">
text
<br>
line2
</div>
<div class="item">
text
</div>
<div class="item">
text
</div>
</div>
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item{
flex: 0 0 50%;
background: orange;
margin-bottom: 10px;
}
Case 2: All around margin
https://jsfiddle.net/6oaney4e/7/
Here for some reason the rows break I'm guessing that's because the row cant fit the items in with the extra margin on the side.
html same as CASE 1
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item{
flex: 0 0 50%;
background: orange;
margin: 10px;
}
Case 3: Wrapping items and adding margin to the inner item
https://jsfiddle.net/6oaney4e/8/
That worked but now the items on each row arent really aware of each other and cant have the same height.
html
<div class="wrapper">
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br>
line2
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
</div>
css
.wrapper{
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item-wrap{
flex: 0 0 50%;
}
.item{
background: orange;
margin: 10px;
}
Is there a way to keep the HTML as in CASE 1 (without the div.item-wrap), have the items on each row the same height as in CASE 1 and have the spacing work like in CASE 3?
Ideally, you do want to use rows and make the .item-wrap divs flex-parents too.
.wrapper {
display: flex;
flex-flow: row wrap;
margin: -10px;
padding: 10px;
background: green;
}
.item-wrap {
flex: 0 0 50%;
display: flex;
}
.item {
background: orange;
margin: 10px;
flex: 1;
}
<div class="wrapper">
<div class="item-wrap">
<div class="item">
text
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br> line2
</div>
</div>
<div class="item-wrap">
<div class="item">
text
<br> line2
<br> line3
</div>
</div>
<div class="item-wrap">
<div class="item">
text
</div>
</div>
</div>
However if you must retain the existing structure, you'll need to use calc to adjust the flex-basis. Something like this:
.wrapper {
display: flex;
flex-flow: row wrap;
background: green;
justify-content: space-between;
}
.item {
flex-grow:0;
flex-shrink:0;
flex-basis:calc(50% - 10px); /* separate properties for IE11 upport */
background: orange;
margin: 5px;
}
<div class="wrapper">
<div class="item">
text
</div>
<div class="item">
text
<br> line2
</div>
<div class="item">
text
<br> line2
<br> line3
</div>
<div class="item">
text
</div>
</div>

Resources