Flex items are not equal width [duplicate] - css

It seems that the content inside a flex div affects its calculated size concerning the flex-grow property. Am I doing something wrong?
In the fiddle provided below, you'll see a number pad. All the rows contain 3 numbers except the bottom row. That row should have the '0' be the width of 2 numbers, hence flex-grow: 2 and the ':' (colon) be the size of 1 number, hence flex-grow: 1.
Am I missing something here?
The right side of the '0' should be aligned with the 8, 5, and 2 above it. It's a bit off.
.numbers {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
}
.button {
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
margin: 5px;
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.2);
cursor: pointer;
}
.button#number0 {
flex-grow: 2;
}
.button#colon {
flex-grow: 1;
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
https://jsfiddle.net/0r4hemdu/

Short Analysis
The problem is that rows 1-3 have two horizontal margins and row 4 only has one.
With horizontal margins at 10px each, row 4 has 10px more free space than the other rows. This throws off the alignment of the columns.
Because flex-grow applies only to free space, and is heavily influenced by content and margins, it's not the most secure way to size flex items.
Try flex-basis instead. Add this to your code:
.button { flex-basis: 33.33%; }
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
.numbers {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
}
.button {
display: flex;
flex-basis: 33.33%;
justify-content: center;
align-items: center;
margin: 5px;
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.2);
cursor: pointer;
}
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
Extended Analysis
You wrote:
It seems that the content inside a flex div affects its calculated size concerning the flex-grow property. Am I doing something wrong?
The source of your problem is not the content inside the flex item.
You wrote:
In the fiddle provided below, you'll see a number pad. All the rows contain 3 numbers except the bottom row. That row should have the '0' be the width of 2 numbers, hence flex-grow: 2 and the ':' be the size of 1 number, hence flex-grow: 1. Am I missing something here?
Yes. Your interpretation of the flex-grow property is incorrect. flex-grow is not intended for defining the size of a flex item. Its job is to distribute free space in the flex container among items.
By applying flex-grow: 1 to a group of flex items, you are telling them to distribute free space evenly among themselves. This is why, in your demo, rows 1, 2 and 3 have equally sized flex items.
When you apply flex-grow: 2, you are telling the flex item to consume twice as much free space as items with flex-grow: 1.
But where does the second 10px margin from the rows above factor into the layout of row 4?
The reason the alignment is off on row 4 is that row 4 has one less margin than the other rows, meaning that row 4 has 10px more free space than the other rows.
You'll notice that if you remove the margin rule you get your desired alignment.
.numbers {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
}
.button {
display: flex;
flex-grow: 1;
justify-content: center;
align-items: center;
/* margin: 5px; */
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.2);
cursor: pointer;
}
.button#number0 {
flex-grow: 2;
}
.button#colon {
flex-grow: 1;
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
So what happens on row four to that second 10px margin?
It gets absorbed by the two flex items.
Here's how flex-grow distributes the extra space on row four:
Flex item left (with content "0") has flex-grow: 2. (.button#number0 in your code.)
Flex item right (with content ":") has flex-grow: 1. (.button#colon in your code.)
The second inter-item margin, which appears only on rows with three flex items, is 10px wide. (The code says 5px around each item, but in
CSS horizontal margins never
collapse.
Moreover, in flexbox, no margins
collapse.)
The sum total of the flex-grow values is three. So let's divide 10px by 3. Now we know that the proportion of 1 is 3.33px.
Hence, flex item left gets 6.66px of the extra space, and flex item right gets 3.33px.
Let's say that flex item left had flex-grow: 3 instead. Then flex item left would get 7.5px, and flex item right would get 2.5px.
The last part of your question says:
The right side of the '0' should be aligned with the 8, 5, and 2 above it. It's a bit off.
Because flex-grow applies only to free space, and is heavily influenced by content and margins, it's not the most secure way to size flex items.
Try flex-basis instead. Add this to your code:
.button { flex-basis: 33.33%; }
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
.numbers {
display: flex;
flex-direction: column;
}
.row {
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
}
.button {
display: flex;
flex-basis: 33.33%;
justify-content: center;
align-items: center;
margin: 5px;
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.2);
cursor: pointer;
}
#number0 { flex-basis: calc(66.67% + 10px); }
* { box-sizing: border-box; }
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
jsFiddle demo
References:
flex-grow definition ~ MDN
flex-basis definition ~ MDN
7.2 Components of Flexibility ~ W3C
EXTRA: CSS GRID SOLUTION
With the advent of CSS Grid, the code for this entire layout can be greatly simplified.
.numbers {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(26%, 1fr));
grid-gap: 10px;
}
#number0 {
grid-column: span 2;
}
/* non-essential decorative styles */
.button {
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
border: 1px solid gray;
}
<div class="numbers">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>

Update 3:
I figured out yet another way to get rid of the misalignment.
This version, together with the 2:nd update, works with the original html untouched, and is using pseudo elements to create the buttons, button hover/click effects included.
flex only version
.row {
width: 60%;
margin: auto;
display: flex;
}
.button {
flex: 0 0 33.3%;
text-align: center;
position: relative;
padding: 10px 5px;
box-sizing: border-box;
pointer-events: none;
}
.button#number0 {
flex: 0 0 66.6%;
}
.button:before,
.button:after {
content: " ";
border-radius: 5px;
border: 1px solid gray;
cursor: pointer;
position: absolute;
left: 5px;
top: 5px;
right: 5px;
bottom: 5px;
pointer-events: auto;
}
.button:before {
background: rgba(255, 255, 255, 0.9);
z-index: -1
}
.button:hover:before {
background: rgba(0, 0, 0, 0.1);
}
.button:hover:after {
border: 2px solid red;
}
.button:active:before {
background: rgba(255, 0, 0, 0.5);
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
flex version with a display: table fallback for browsers that does not support the new flexbox model.
.row {
display: table; /* remove for flex only */
width: 60%;
margin: auto;
display: flex;
}
.button {
display:table-cell; /* remove for flex only */
width: 33.3%; /* remove for flex only */
flex: 0 0 33.3%;
text-align: center;
position: relative;
padding: 10px 5px;
box-sizing: border-box;
pointer-events: none;
}
.button#number0 {
width: 66.6%; /* remove for flex only */
flex: 0 0 66.6%;
}
.button:before,
.button:after {
content: " ";
border-radius: 5px;
border: 1px solid gray;
cursor: pointer;
position: absolute;
left: 5px;
top: 5px;
right: 5px;
bottom: 5px;
pointer-events: auto;
}
.button:before {
background: rgba(255, 255, 255, 0.9);
z-index: -1
}
.button:hover:before {
background: rgba(0, 0, 0, 0.1);
}
.button:hover:after {
border: 2px solid red;
}
.button:active:before {
background: rgba(255, 0, 0, 0.5);
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
Update 2:
In addition to Michael_B's answer, which by the way has a very good explanation, is here an updated version, that actually does give the desired alignment without the, in this case, 1-2 px off.
Here is a fiddle sample, and an image, of both mine and Michael_B versions, where the border has been increased a little to make it easier to see the misalignment.
It all comes down to how flexbox calculates sizes when border/padding is present, which you can read more about in this post, where box-sizing: border-box needs to be set along with a few more adjustments, which is commented in the code.
Here is my fiddle and snippet
.row {
display: flex;
width: calc(100% - 30px); /* 30px = the sum of the buttons margin: 5px
to avoid horizontal scroll */
}
.button {
display: flex;
flex-basis: 33.33%;
flex-shrink: 0; /* we need flex-grow/shrink to be 1/0 to make
it calculate the size properly */
box-sizing: border-box; /* to take out the borders when calculate the
flex shrink/grow factor */
justify-content: center;
align-items: center;
margin: 5px;
border-radius: 5px;
border: 1px solid gray;
background: rgba(0, 0, 0, 0.1);
cursor: pointer;
}
#number0 {
flex-basis: calc(66.66% + 10px);
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1">1</div>
<div class="button number" id="number2">2</div>
<div class="button number" id="number3">3</div>
</div>
<div class="row">
<div class="button number" id="number4">4</div>
<div class="button number" id="number5">5</div>
<div class="button number" id="number6">6</div>
</div>
<div class="row">
<div class="button number" id="number7">7</div>
<div class="button number" id="number8">8</div>
<div class="button number" id="number9">9</div>
</div>
<div class="row">
<div class="button number" id="number0">0</div>
<div class="button" id="colon">:</div>
</div>
</div>
Update:
flex only version, with a minor change of the existing html structure, using pseudo elements.
.row {
display: flex;
}
.button {
flex: 0 0 33.3%;
}
.button:after {
content: attr(data-nr);
display: block;
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.9);
text-align: center;
padding: 3px;
margin: 5px;
cursor: pointer;
}
.button#number0 {
flex: 0 0 66.6%;
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1" data-nr="1"></div>
<div class="button number" id="number2" data-nr="2"></div>
<div class="button number" id="number3" data-nr="3"></div>
</div>
<div class="row">
<div class="button number" id="number4" data-nr="4"></div>
<div class="button number" id="number5" data-nr="5"></div>
<div class="button number" id="number6" data-nr="6"></div>
</div>
<div class="row">
<div class="button number" id="number7" data-nr="7"></div>
<div class="button number" id="number8" data-nr="8"></div>
<div class="button number" id="number9" data-nr="9"></div>
</div>
<div class="row">
<div class="button number" id="number0" data-nr="0"></div>
<div class="button" id="colon" data-nr=":"></div>
</div>
</div>
flex version, with a minor change of the existing html structure, using pseudo elements, and has a display: table fallback for browsers that does not support the new flexbox model (like IE8/9).
.row {
display: table;
width: 100%;
}
.button {
display: table-cell;
width: 33.3%;
padding: 5px;
}
.button:after {
content: attr(data-nr);
display: block;
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.9);
text-align: center;
padding: 3px;
cursor: pointer;
}
.button#number0 {
width: 66.6%;
}
#supports (display: flex) {
.row {
display: flex;
}
.button {
display: block;
width: auto;
flex: 0 0 33.3%;
padding: 0;
}
.button#number0 {
flex: 0 0 66.6%;
}
.button:after {
margin: 5px;
}
}
<div class="numbers">
<div class="row">
<div class="button number" id="number1" data-nr="1"></div>
<div class="button number" id="number2" data-nr="2"></div>
<div class="button number" id="number3" data-nr="3"></div>
</div>
<div class="row">
<div class="button number" id="number4" data-nr="4"></div>
<div class="button number" id="number5" data-nr="5"></div>
<div class="button number" id="number6" data-nr="6"></div>
</div>
<div class="row">
<div class="button number" id="number7" data-nr="7"></div>
<div class="button number" id="number8" data-nr="8"></div>
<div class="button number" id="number9" data-nr="9"></div>
</div>
<div class="row">
<div class="button number" id="number0" data-nr="0"></div>
<div class="button" id="colon" data-nr=":"></div>
</div>
</div>

I think everything that Michael_B said is correct. Only the solutions is a bit awkward. I personsally don't like calc. It just doesn't feel right.
The problem you have is a more general one. You put too many responsibilities onto one element. In this case it the .button class. Flex and Margin with flex-grow is too much responsibility. Try to break that apart. It means more DOM elements, but it saves you a lot of pain.
.numbers {
display: flex;
flex-direction: column;
max-width: 200px;
margin: 0 auto;
}
.row {
display: flex;
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
}
.row > .box {
display: flex;
flex-basis: 33.3333%;
justify-content: center;
align-items: center;
}
.row > .box.box-2 {
flex-basis: 66.6667%;
}
.button {
border-radius: 5px;
border: 1px solid gray;
background: rgba(255, 255, 255, 0.2);
cursor: pointer;
width: auto;
text-align: center;
margin: 5px;
width: 100%;
}
<div class="numbers">
<div class="row">
<div class="box"><div class="button number" id="number1">1</div></div>
<div class="box"><div class="button number" id="number2">2</div></div>
<div class="box"><div class="button number" id="number3">3</div></div>
</div>
<div class="row">
<div class="box"><div class="button number" id="number4">4</div></div>
<div class="box"><div class="button number" id="number5">5</div></div>
<div class="box"><div class="button number" id="number6">6</div></div>
</div>
<div class="row">
<div class="box"><div class="button number" id="number7">7</div></div>
<div class="box"><div class="button number" id="number8">8</div></div>
<div class="box"><div class="button number" id="number9">9</div></div>
</div>
<div class="row">
<div class="box box-2"><div class="button number" id="number0">0</div></div>
<div class="box"><div class="button" id="colon">:</div></div>
</div>
</div>

flexbox doesn't respond well to margins, in my opinion.
The better approach / workaround I prefer is to make sure all flex children have 0 margins, set the flex container to justify-content: space-between;, and then give the children a total width less than 100%. The remainder will be your margin.
In other words, if you want two elements per row, set each to 49% wide and you'll have 2% space between them. Three elements, each at 32% wide and you'll have 2% between them. In the calculator example, the 0 cell should be 66% wide and the rest 32%.
Edit: Note that because reasons (namely that content-box is terrible), if any of your children have borders you'll need to use box-sizing: border-box for my suggestion to work properly.
https://jsfiddle.net/r3L1mtbe/2/

It is important to use the shorthand to accommodate cross browser support:
.row {
display: flex;
flex-direction: row; /* <-- this is the default so unnecessary to state */
flex-grow: 1;
justify-content: space-between;
}
.button {
display: flex;
/* flex-grow: 1; replace with shorthand */
flex:1 0 100%; /* probably making the "width: 100%;" unnecessary */
justify-content: center;
}
.button#number0 {
/* flex-grow: 2; replace with shorthand */
flex:2 0 100%;
}
.button#colon {
/* flex-grow: 1; replace with shorthand */
flex:1 0 100%;
}

Related

Flexbox - image columns with fixed margins: how to get even image widths

I have a design with images in columns with a fixed margin (or gap) between them.
Right now the columns have margins, and because the total margin is different for each column (since there is no left margin on the first column and no right margin on the last), the width of each column image becomes different, causing the height to be different on the middle images.
I tried to divide the margin so that each column uses the same total amount of margin (which seems instinctively over complicated) . I can get that to work, but it doesn't work for 3 columns. You can't make three columns use the same amount of margin I think.
I know there is some "gap" property in css grid, but how do I solve it in flexbox?
See my example code here: example
<template>
<div id="app">
<div class="row">
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
<div class="col-3">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "App",
components: {},
};
</script>
<style lang="scss">
#app {
margin: 0 auto;
width: 800px;
border: 3px solid orange;
}
.row {
display: flex;
box-sizing: border-box;
display: flex;
flex-grow: 0;
flex-shrink: 1;
flex-direction: row;
flex-wrap: wrap;
img {
width: 100%;
object-fit: cover;
}
}
.col-3 {
flex: 0 1 25%;
max-width: 25%;
/* width:25%; */
> div {
/* margin-right:25px; */
}
&:first-child > div {
margin-right: 12.5px;
}
&:nth-child(2) > div {
margin-right: 12.5px;
margin-left: 12.5px;
}
&:nth-child(3) > div {
margin-right: 12.5px;
margin-left: 12.5px;
}
&:last-child > div {
margin-left: 12.5px;
}
}
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Update: I realise now that you're looking for a solution using flex rather than css grid. The other answer provides some options there. If you do want to use grid though this approach is handy as your widths will be automatically calculated with whatever gap you choose.
Use display:grid, and set your container to have four columns with one fractional unit for each column, and a column-gap of the gap you want.
The gap below the image is caused because by default images are inline elements, so they sit with the baseline of text. If you set your images to display:block the gap will disappear.
.row {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 12px;
column-gap: 12px;
border:3px solid yellow;
}
.col-3 {
width: 100%;
}
.col-3 img {
display: block;
width: 100%;
object-fit: cover;
}
<div class="row">
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
<div class="col-3">
<img src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80" />
</div>
</div>
I have written the code using flexbox and made a little change to your HTML code
<template>
<div id="app">
<div class="row">
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
<div>
<img
src="https://images.unsplash.com/photo-1633357337538-83612701c7a9?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1470&q=80"
/>
</div>
</div>
</div>
</template>
<style lang="scss">
body {
margin: 0;
.row {
display: flex;
width: 100%;
justify-content: space-between;
div {
padding: 0 10px;
&:nth-child(1) {
padding-left: 0;
}
&:nth-last-child(1) {
padding-right: 0;
}
img {
width: 100%;
}
}
}
}
</style>
Flexbox does have a gap property that will space your items evenly and won't create empty spaces on the right or left. But, based on the code sample you shared, I think your issue is a combination of a couple of things:
Your middle two containers are smaller than your outer two containers because of their extra margin
Your images are set to take the full width of their containers
Because the middle two containers are smaller—and the images are preserving their aspect ratio—you get images that are both narrower and shorter.
If you strip everything down to just using gap, I think you'll be a lot closer to what you're trying to accomplish:
.flexbox {
background-color: #ace;
border: 3px solid orange;
box-sizing: border-box;
display: flex;
flex-flow: row nowrap;
gap: 8px;
width: 800px;
}
.container {
flex: 0 1 25%;
margin: 0;
max-width: 25%;
}
img.full-width {
width: 100%
}
<div class="flexbox">
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
<div class="container">
<img class="full-width" src="https://picsum.photos/300/200" />
</div>
</div>

flex-shrink does not update item width

Update: I think there is no easy solution to this if I have to use flexbox. I'll just use "flex-shrink: 0" and use media queries to adjust the design. Anyway, thanks for your help.
I have got a horizontal list (display: flex) with multiple elements. When I reduce the window size the list elements start to shrink. Unfortunately, the width of the elements nearly stays the same. The li-elements take up too much space. I want the li-element's size to fit the content. If I add "flex-shrink: 0" to the li-element the width is right, but I need this word-wrap.
How to fix this?
With flex-shrink: 0
With flex-shrink: 1
#categories ul {
border-top: 1px solid lightgrey;
display: flex;
margin: 0 0 -1px 0;
padding: 7px 0 0 0;
}
#categories li {
padding: 0 0 7px 0;
margin-right: 15px;
list-style-type: none;
}
<nav id="categories">
<ul>
<li><a href="#">
Thanks & regards,
Mark
Remember that the flex-shrink property sets the flex-shrink of a flex container item. So the items will shrink to fit the container if they are larger than the container's size. In your case, I think that setting flex-shrink to 2 would make the word-wrap as you requested. Check this code snippet. I hope it helps!
function myFunction(val) {
document.getElementById("example-element").style.flexShrink = val;
}
* {
font-family: arial;
}
.buttons-section {
display: flex;
justify-content: space-evenly;
width: 70%;
}
.example-container {
background-color: #eee;
border: .2em solid;
padding: .75em;
border-radius: 10px;
width: 70%;
margin-left: 20px;
max-height: 300px;
display: flex;
}
.example-container>div {
margin: 10px;
}
.first-solution>div {
background-color: rgba(0, 255, 0, 0.2);
border: 3px solid green;
flex-grow: 1;
flex-shrink: 1;
flex-basis: 300px;
}
p {
margin-left: 20px;
}
small {}
.second-solution {
flex: 1 1 0;
justify-content: space-evenly;
}
.second-solution>div {
background-color: rgba(255, 0, 0, 0.2);
border: 3px solid red;
text-align: center;
}
.third-solution {
flex-wrap: wrap;
}
.third-solution>div {
background-color: rgba(0, 0, 255, 0.2);
border: 3px solid blue;
flex-basis: 100px;
text-align: center;
}
<section class="buttons-section">
<div>
<pre><code>flex-shrink: 0;</code></pre>
<button type="button" onclick="myFunction(0)" aria-hidden="true">Apply
</button>
</div>
<div>
<pre><code>flex-shrink: 1;</code></pre>
<button type="button" onclick="myFunction(1)" aria-hidden="true">Apply
</button>
</div>
<div>
<pre><code>flex-shrink: 2;</code></pre>
<button type="button" onclick="myFunction(2)" aria-hidden="true">Apply
</button>
</div>
</section>
<br>
<div>
<section>
<div class="example-container first-solution">
<div id="example-element">I shrink</div>
<div id="example-element">Item Two</div>
<div id="example-element">Item Three</div>
</div>
</section>
</div>
<p>Added more examples after the first comment. Feel free to resize the screen to see the behaviour😉</p>
<p>All elements same size, container with flex: 1 1 0;<br>
<small>Added <b>justify-content: space-evenly;</b> which helps to distribute the extra free space </small>
</p>
<div>
<section>
<div class="example-container second-solution">
<div>Item one</div>
<div>Item Two</div>
<div>Item Three</div>
</div>
</section>
</div>
<p>Suggestion: using flex-wrap: wrap; on the container so the items will always fit the container</p>
<div>
<section>
<div class="example-container third-solution">
<div>Item one</div>
<div>Item Two</div>
<div>Item Three</div>
</div>
</section>
</div>
Can you paste your code here so we can see more info about your problem. Also did you try with flex-grow property?

Flex box - arrange items in column with multiple elements on each row if space allows

I want to arrange some items of variable width in a column, where the column has a fixed width. But I want elements to stack up next to each other if the total width of a group of elements doesn't exceed the column width. In the snippet, I want to add CSS to the first container so that that the 2nd and 3rd divs will be next to each other, as they are in the second container. I don't know the element widths in advance so I can't just do this manually by putting divs around the elements that should be adjacent. I want to also accomplish with just flexbox, not CSS grids if that's also a solution.
.container {
width: 500px;
border: 1px solid red;
}
.flexcol {
display: flex;
flex-direction: column;
align-items: center;
}
.inlineblock > div {
display: inline-block;
}
.item {
border: 1px solid black;
font-size: 24pt;
text-align: center;
}
.item1, .item4 {
width: 400px;
}
.item2, .item3 {
width: 200px;
}
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
<div class="item item4">
Item 4
</div>
</div>
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="inlineblock">
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
</div>
<div class="item item4">
Item 4
</div>
</div>
Fiddle here of same code: https://jsfiddle.net/6ycer7b0/1/
You can do this with a row direction and enable the wrap to simulate a column behavior as the elements will stack above each other:
.container {
width: 500px;
border: 1px solid red;
}
.flexcol {
display: flex;
flex-direction: row;
flex-wrap:wrap;
justify-content: center;
}
.item {
border: 1px solid black;
font-size: 24pt;
text-align: center;
}
.item1, .item4 {
width: 400px;
}
.item2, .item3 {
width: 200px;
}
<div class="container flexcol">
<div class="item item1">
Item 1
</div>
<div class="item item2">
Item 2
</div>
<div class="item item3">
Item 3
</div>
<div class="item item4">
Item 4
</div>
</div>

Centering form with flex rows

I have a form where every single row has display: flex. Each row has a label box with a fixed width and a field box which fit to his content. Now it looks like this:
What I want to achive is to shrink every row to the minimum content width and centering the form horizontally. Something like this:
I tried using inline-flex for rows but seems like parent becomes smaller than the total width of children.
Is there a way to achive it keeping flexbox for each row and without using transform: translate to align form horizontally?
Here is the code of the first image:
.form-container {
background-color: black;
padding: 5px;
color: white;
}
.form {
background-color: yellow;
padding: 5px;
}
.form-row {
display: flex;
background-color: silver;
padding: 5px 0;
margin: 5px 0;
}
.form-row-label {
flex: 0 0 200px;
background-color: cornflowerblue;
}
.form-row-field {
flex: 0 1 0;
background-color: orangered;
white-space: nowrap;
}
<div class="form-container">
<div class="form">
<div class="form-row">
<div class="form-row-label">
label 1
</div>
<div class="form-row-field">
field 1 xxxxxxxxxxxxxxxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 2
</div>
<div class="form-row-field">
field 2 xxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 3
</div>
<div class="form-row-field">
field 3 xxxxxxxxxx
</div>
</div>
</div>
</div>
And here is the jsfiddle.
Thanks
Here is an idea:
.form-container {
background-color: black;
padding: 5px;
color: white;
text-align:center; /* Center the inline-block*/
}
.form {
background-color: yellow;
padding: 5px;
display:inline-block; /* Use this to fit content */
}
.form-row {
display: flex;
background-color: silver;
padding: 5px 0;
margin: 5px 0;
}
.form-row-label {
width: 200px;
flex-shrink:0; /* Avoid the content to shrink*/
background-color: cornflowerblue;
text-align:left;
}
.form-row-field {
flex-shrink:0;
background-color: orangered;
}
<div class="form-container">
<div class="form">
<div class="form-row">
<div class="form-row-label">
label 1
</div>
<div class="form-row-field">
field 1 xxxxxxxxxxxxxxxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 2
</div>
<div class="form-row-field">
field 2 xxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 333
</div>
<div class="form-row-field">
field 3 xxxxxxxxxx
</div>
</div>
</div>
</div>
Here is an idea using display:inline-flex and flex-direction:column in the .form class with text-align: center in the parent class. Also you will need to use width: 200px in label instead of flex-basis:200px
.form-container {
background-color: black;
padding: 5px;
color: white;
text-align: center;
}
.form {
background-color: yellow;
padding: 5px;
display: inline-flex;
flex-direction: column;
justify-content: center;
}
.form-row {
display: flex;
background-color: silver;
padding: 5px 0;
margin: 5px 0;
}
.form-row-label {
width: 200px;
background-color: cornflowerblue;
}
.form-row-field {
flex: 0 1 0;
background-color: orangered;
white-space: nowrap;
}
<div class="form-container">
<div class="form">
<div class="form-row">
<div class="form-row-label">
label 1
</div>
<div class="form-row-field">
field 1 xxxxxxxxxxxxxxxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 2
</div>
<div class="form-row-field">
field 2 xxxx
</div>
</div>
<div class="form-row">
<div class="form-row-label">
label 3
</div>
<div class="form-row-field">
field 3 xxxxxxxxxx
</div>
</div>
</div>
</div>
If you want to center the yellow box within the black box, you should only add below code to your code:
.form {
background-color: yellow;
padding: 5px;
display: flex;
width: 650px;
max-width: 650px;
flex-direction: column;
margin: 0 auto;
}
.form-row-label {
flex: 0 0 60%;
background-color: cornflowerblue;
}
I think it will help you a little bit.

Flexbox 2x2 grid - input inside overflows the flex container

I want to have a flexbox 2x2 grid that contain a random number of elements (odd or even). I've achieved to create this flexbox without any problems.
When I added my content: input fields with width 100% I noticed that those are bigger then their flex container which seems odd. In the example this is easy to spot on the last red box.
How can I make the input fields be 100% of the flex container?
.flex-grid {
display: flex;
flex-wrap: wrap;
height: 500px;
}
.grid-item {
flex: 1 1 50%;
background: #F90;
border-top: solid 1px #000;
}
.grid-item:nth-child(odd) {
background: #F00;
flex: 0 0 50%;
}
input {
width: 100%;
}
<div class="flex-grid">
<div class="grid-item">
<input type="text" value="email">
</div>
<div class="grid-item">
<input type="text" value="email">
</div>
<div class="grid-item">
<input type="text" value="email">
</div>
</div>
Fiddle: https://jsfiddle.net/x3pyenkL/
From Documentation:
The box-sizing property is used to alter the default CSS box model used to calculate width and height of the elements.
Set box-sizing: border-box for <input> elements.
input {
box-sizing: border-box;
width: 100%;
}
Or better to add for all elements of your page for consistency.
* {
box-sizing: border-box;
}
.flex-grid {
display: flex;
flex-wrap: wrap;
height: 500px;
}
.grid-item {
flex: 1 1 50%;
background: #F90;
border-top: solid 1px #000;
}
.grid-item:nth-child(odd) {
background: #F00;
flex: 0 0 50%;
}
input {
box-sizing: border-box;
width: 100%;
}
<div class="flex-grid">
<div class="grid-item">
<input type="text" value="email">
</div>
<div class="grid-item">
<input type="text" value="email">
</div>
<div class="grid-item">
<input type="text" value="email">
</div>
</div>

Resources