Set flexbox children to have different heights to use up available space - css

Using a two-column flexbox layout, how can different-sized children be made to fill all available space, instead of all children having the height of the tallest child of the row?
I set up a demo on jsbin that illustrates the problem. I'd like for all the children to be the size of their wrapped contents.
#container {
width: 800px;
display: flex;
flex-wrap: wrap;
}
.cell {
width: 300px;
flex; 1 auto;
}
<div id="container">
<div class="cell">
Cells with arbitrarily long content.</div>
<div class="cell">
</div>
<div class="cell">
</div>
<div class="cell">
</div>
<div class="cell">
</div>
</div>
</body>
</html>

This is how Flexbox rows are expected to behave. Flexbox is not meant to recreate Masonry with pure CSS: items in one row cannot occupy space allocated for a preceding/following row (same goes for columns if you're using column orientation). You can use align-items to prevent them from stretching, but that's about it:
http://cssdeck.com/labs/9s9rhrhl
#container {
width: 800px;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
.cell {
width: 300px;
flex: 1 auto;
padding: 10px;
border: 1px solid red;
}
Otherwise, you should be using the column orientation or the multi-column module (see this SO answer: https://stackoverflow.com/a/20862961/1652962)

Related

Make flex children width the same and also fit-content

My scenario
I have these two flex containers (the difficulty options and the max-score options):
I want the 'easy', 'medium' and 'hard' button to share the same width, but also to fit they're content (in this case, because 'medium' is the longest, they should all equal its width).
I want the same behavior with the bottom buttons (but for them to have a smaller width since they need to accommodate for smaller content).
Right now the flex containers for both of them is set to:
display: flex;
flex-direction: wrap;
flex-wrap: wrap;
And the flex children are each set to their default flex values, with a set height and an auto width.
Approaches I've tried
First approach - flex-basis and flex-grow
Setting the children to flex-basis: 0 and flex-grow: 1, as I've seen in past questions, but then my wrapped child fills the entire width, and the top buttons aren't the same width:
Second approach - -- hardcoded flex-basis
Setting all children to flex-basis: 90px (90px to accommodate for the biggest button, 'medium') which does make them all the same width, but then the width is fixed and doesn't adjust to only fit the content (specifically this is desired so the score buttons can fit in two rows instead of three).
Third approach - max-width
The closest I've got to is to set the children to:
```
max-width: 90px;
flex-basis: 0;
flex-grow: 1;
```
Which makes them behave as wanted:
But when the screen width shrinks, the buttons start to differ in width (the obvious one is the '200' button bigger than the other scores, but also 'medium' is bigger than 'easy' and 'hard'):
My code:
.flex-col,
.flex-row {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: center;
}
.flex-col {
flex-direction: column;
}
.flex-row {
flex-direction: row;
}
.button {
border-style: solid;
padding: 4px;
}
.parent {
height: auto;
width: auto;
}
<div class="parent flex-col">
<div class="flex-col">
<div class="flex-row">
DIFFICULTY
</div>
<div class="flex-row">
<div class="button">EASY</div>
<div class="button">MEDIUM</div>
<div class="button">HARD</div>
</div>
</div>
<div class="flex-col">
<div class="flex-row">
MAX SCORE
</div>
<div class="flex-row">
<div class="button">50</div>
<div class="button">75</div>
<div class="button">100</div>
<div class="button">150</div>
<div class="button">200</div>
</div>
</div>
</div>
Help appreciated, thanks!
The closest way to do this with CSS only, is to use a grid instead of a flexbox for reasons well explained here.
The only way to truly do what you are asking (make all children have the same width as the widest child), is with JavaScript. Loop through the elements to find the biggest width and set them all to have the found width.
Here is a snippet demonstrating both concepts:
const equalizers = document.querySelectorAll('.equalize')
let r = 0
equalizers.forEach(equalizer => {
const widths = []
for (const btn of equalizer.children) {
const w = btn.getBoundingClientRect().width
// Math.ceil() is optional to avoid long floats
widths.push(Math.ceil(w)) // 82
// widths.push(w) // 81.31945037841797
}
const biggest = Math.max(...widths)
console.log(`biggest width found in row[${r++}]:`, biggest)
for (const btn of equalizer.children) {
btn.style.width = `${biggest}px`
}
})
.flex-col,
.flex-row {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: center;
}
.flex-col {
flex-direction: column;
}
.flex-row {
flex-direction: row;
}
.button {
border-style: solid;
padding: 4px;
}
.parent {
height: auto;
width: auto;
}
.grid-row {
display: grid;
gap: 4px;
}
.grid-row>* {
text-align: center;
}
#media (min-width: 25em) {
.grid-row {
grid-auto-flow: column;
grid-auto-columns: 1fr;
}
}
.flex-row>* {
text-align: center;
}
<hr>
<strong>JavaScript</strong> (only ever as wide as the widest sibling, with wrapping)
<hr>
<div class="parent flex-col">
<div class="flex-col">
<div class="flex-row">
DIFFICULTY
</div>
<div class="flex-row equalize">
<div class="button">EASY</div>
<div class="button">MEDIUM</div>
<div class="button">HARD</div>
</div>
</div>
<div class="flex-col">
<div class="flex-row">
MAX SCORE
</div>
<div class="flex-row equalize">
<div class="button">50</div>
<div class="button">75</div>
<div class="button">100</div>
<div class="button">150</div>
<div class="button">200</div>
</div>
</div>
</div>
<hr>
<strong>Grid</strong> (always as wide as posible and no wrapping, either all stacked, or all inline with breakpoint)
<hr>
<div class="parent flex-col">
<div class="flex-col">
<div class="flex-row">
DIFFICULTY
</div>
<div class="grid-row">
<div class="button">EASY</div>
<div class="button">MEDIUM</div>
<div class="button">HARD</div>
</div>
</div>
<div class="flex-col">
<div class="flex-row">
MAX SCORE
</div>
<div class="grid-row">
<div class="button">50</div>
<div class="button">75</div>
<div class="button">100</div>
<div class="button">150</div>
<div class="button">200</div>
</div>
</div>
</div>

how to make columns the same width with css flex

hello I need to basicaly display a table but with flex (only because I need to adjust the look for mobile)
.container {
display: flex
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>
how can I make each column have the same width? or at least the width of the largest item.
If you apply flex-grow: 1 to each div in the .row then it will expand accordingly. Note that the .row divs need the display flex (flex only apply to the DIRECT children of the flexed element.
The width can be set by using flex-basis and calc() and dividing the full-width by the number of columns you want (2 in this case). I added a border to demonstrate.
I would be remiss if I didn't suggest that the best tool to display a table structure - is a ... table... then you can work out how to modifiy for responsive layout - but the semantic structure of a table is correct for tabulr content.
.container {
width: 100%;
}
.row {
display: flex;
width: 100%;
}
.row div {
flex-grow: 1;
flex-basis: calc(100% / 2);
padding: 4px 8px;
border: solid 1px red;
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>
Use flex-basis to set the 'default' width first.
Use flex-grow and/or flex-shrink to allow it to grow /shrink
Use max-width' and min-width` to define by how much it can grow/shrink
.container{ #Stack the div inside the conainer
display:flex;
flex-flow: columns nowrap;
}
.row{ # Set div as cell in the row.
display:flex;
flex-flow: row nowrap;
flex-basis: 33%; # Calculate width from there.
flex-grow:1; #Allow resizing bigger
flex-shrink:1; #Allow resize smaller.
max-width:50%; #Adjust to your liking
min-width:25%; #Adjust o your liking
}
Please consider this ressource https://css-tricks.com/snippets/css/a-guide-to-flexbox/
You can add a flex-basis to the .row items by doing this:
.row {
flex-basis:calc(100% / 3);
}
.container {
width: 100%;
}
.row {
display: flex;
width: 100%;
}
.row div {
flex-grow: 1;
flex-basis: calc(100% / 2);
padding:10px;
margin: 5px;
background: #eee;
}
<div class="container">
<div class="row">
<div>1000</div>
<div>mary</div>
</div>
<div class="row">
<div>1</div>
<div>john</div>
</div>
<div class="row">
<div>11</div>
<div>mike</div>
</div>
</div>

CSS span or p goes outside of div

I am trying to replicate stackoverflow-like design and ran into problem.
<div class="flex-grow-0 pd-around-m"> # line 1
<div class="flex-col fill-row mr-around-s"> # line 2
<div class="flex-row fill-row"> # line 3
<div class="flex-col justify-center mr-around-m"> # line 4
//Buttons
</div>
<span>
//Long Text!!
</span>
</div>
<div class="answer-bottom"></div>
</div>
</div>
.fill-row {
width: 100%
}
.flex-col {
display: flex;
flex-direction: column;
}
.flex-row {
display: flex;
flex-direction: row;
}
.mr-around-m {
margin: 1rem;
}
.justify-center {
justify-content: center;
}
When I enter long text in <span>, <div> in line 2, line 3 goes out of div box in line 1.
I tried adding white-space: pre-line to div in line 2 and directly at span but still text goes out of the box.
How can I keep the text inside parent div?
navigation bar on the left has property width:20% but gets squashed. Is this because of the textbox problem I asked above?
EDIT
https://jsfiddle.net/pzcu2yjn/
Here's a replication of my problem. if you make the text in span short enough, navbar and menu will have some empty space in the left maintaining 20% of the screen. however, if you leave the long text as it is, it gets squashed and 20% gets ignored
Few things:
On using flex it is good to provide width for left and right container since container will not know what it should when content increases.
Once you have the width assigned to the right container that is when you can use wrap functionality so the wrap works only for right container and it doesn't have no impact on less container. overflow-break-word;
NOTE:
I have removed unwanted code from the code, you can put it back it has no impact if those are needed.
.flex-row {
display: flex;
width: 100%;
}
.navbar {
display: flex;
flex-direction: column;
justify-content: flex-start;
border-right: 0.05rem solid var(--main-border-color);
align-items: flex-end;
width: 20%;
border: 1px solid red;
}
.pd-around-m {
width: 80%;
border: 1px solid blue;
display: inline-block;
overflow-wrap: break-word;
}
<div class="flex-row">
<div class="navbar">
<div>
menu1
</div>
</div>
<div class="flex-grow-0 pd-around-m">
<div class="flex-col fill-row mr-around-s">
<div class="fill-row">
<span>
AaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaaAaaaaaaaaaaaaaaaaaa
</span>
</div>
<div class="answer-bottom"></div>
</div>
</div>
</div>
You can add overflow: hidden; to prevent the text from going outside of your div.
There's a very similar question with a precise answer:
<span> element going outside of <div> element

Set width of child elements in flex container/parent div

Is it possible, using flex, to set the width of children elements in the style of the their parent div?
For example I want each element in a flex container to stretch 1/3 of the container, or 1/2, or 100% width. They would all have equal widths. The width is a dynamic value which can change.
This issue is, I can add dynamic style values only to the .flex-container div. I can't add anything dynamic to .element divs.
So I can't do this...
<div class="flex-container">
<div class="element" style="width: 33%"></div>
<div class="element" style="width: 33%"></div>
etc...
</div>
Only..
<div class="flex-container" style="some value that will set children divs to 1/3 width of container, or 1/2, etc)">
<div class="element"></div>
<div class="element"></div>
etc...
</div>
My CSS for the flex container:
.flex-container {
display: flex;
flex-wrap: wrap;
}
.element {
// I can't add dynamic styles to this like width
}
Yes you can dynamically set the width.
Then use media queries to change them depending on your window size.
html, body {
height: 100%;
}
.flex-container {
display: flex;
height: 50%;
}
.element {
width: 33.33%;
align-self: stretch;
}
.element:nth-of-type(1) {
align-self: flex-start;
background: red;
}
.element:nth-of-type(2) {
align-self: flex-end;
background: green;
}
.element:nth-of-type(3) {
align-self: stretch;
background: blue;
}
<div class="flex-container">
<div class="element">
Test Element 1
</div>
<div class="element">
Test Element 2
</div>
<div class="element">
Test Element 3
</div>
</div>

Using calc() with a dynamic value?

I am wondering if this is possible: I have a header that can contain a variable amount of text. Below that I have another element which I want to take up the remaining height of the page.
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
Normally I would do this using calc, eg:
.content {
height: calc(100vh - 75px);
}
Where 75px is the set height of .header.
But in this example, the .header element is dynamic and does not have a set height. Only a padding and font-size are set.
To complicate things, this also uses the Foundation Grid layout, which makes me nervous about using display: table (.title and .menu sit side by side on desktop, but stacked on mobile) .
Is there anyway to get the height of the dynamic header element (without resorting to JQuery)?
You can use flexbox and set .content to flex-grow: 1 so that it will fill to grow the available space.
body {
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
}
.content {
flex-grow: 1;
background: #eee;
}
<div class="header row">
<div class="title column large-5">Potentially very long text</div>
<div class="menu column large-7">Menu items</div>
</div>
<div class="content">
</div>
<div class="footer">
</div>
I made a small pen to show the way to do this using flex box, it involved changing your markup a bit:
css:
.container {
display: flex;
flex-direction: column;
height: 250px; // whatever you want here
}
.header {
width: 100%;
background: red;
padding: 10px;
}
.content {
background: yellow;
width: 100%;
flex-grow: 1;
}
So the content will always take the available space inside the content div.
check the whole pen: http://codepen.io/anshul119/pen/yMYeLa
hope this helps.

Resources