creating equal width flex items - html

I'm using flex for layout purposes, but the browser does not spread the width equally between items.
.parent {
display: flex;
width: 200px;
}
.btn1 {
flex: 1 1 auto;
}
<div class="parent">
<div class="btn1">
<button align="left" style="width:100%">Ok</button>
</div>
<button align="left" class="btn1">Cancel</button>
<div>
Now, I want the buttons to split the container length 50% / 50%.
But that's not what's happening. I tried using flex: 1 1 auto and flex: 1 1 0 but with no success.
I know that I can use the OK button directly and it will solve my problem, but in my particular scenario it's important to wrap it with a div.
Now, as I understand it, flex should be able to spread the width equally and that's my goal here.
One more thing though, I noticed that the button content seems to have an effect on the width and I want to ignore this effect somehow.
Thanks!
JSFiddle example:
https://jsfiddle.net/edismutko/cvytLkyp/3/

flex-basis: auto vs flex-basis: 0
You're sizing your flex items with flex: 1 1 auto.
However, if you want to distribute space evenly among items, you need to use flex: 1 1 0.
The difference is the flex-basis component.
With flex-basis: 0, every item is considered to have a zero width and flex-grow distributes container space equally among them. This results in all items having the same length.
With flex-basis: auto, the size of the item is factored into the flex-grow calculation and container space is distributed proportionally among items.
So when you want equal length items use flex: 1 1 0, which is the same as flex: 1.
Here's a more detailed explanation: Make flex-grow expand items based on their original size
Default rules on button elements
Browsers apply styles to elements by default. For instance, Chrome adds padding and border widths to button elements.
Reset those defaults.
Now you have two equal width flex items. (Additional styling is up to you.)
.parent {
display: flex;
width: 200px;
}
.btn1 {
flex: 1;
}
button {
padding: 1px 0;
border-left-width: 0;
border-right-width: 0;
}
<div class="parent">
<div class="btn1">
<button align="left" style="width:100%">Ok</button>
</div>
<button align="left" class="btn1">Cancel</button>
<div>
box-sizing: border-box
Something else to consider is including the padding and border lengths in the width / flex-basis calculation. Why are borders causing div to overflow container?

Related

Merging width and flex properties for utility classes

On my site, I have a lot of utility classes that sets the width of elements. For almost all of the cases, the width property is perfect. The only issue is with flex box. For flex items, the width property does not set the width, and instead the flex property needs to be used. I tried combining the two into a single class like so:
.width-100 {
width: 100px !important;
flex: 0 0 100px !important;
}
Surprisingly enough, this worked on the few places I tried it. When an element is not using flexbox, its width gets set properly. When an element is using flexbox, the flex property makes sure it works as well. However, I am wondering if this is a good idea or not. Could there be possible bugs that I am simply not foreseeing right now?
You may fall into the case where you have min-width constraint and setting the width will make the flex item behave differently:
Here is a basic example:
.container {
display:flex;
margin:5px;
}
.container > img {
flex: 0 0 100px;
}
.container > span {
flex-grow:1;
background:red;
}
<div class="container">
<img src="https://i.picsum.photos/id/100/200/100.jpg">
<span></span>
</div>
<div class="container">
<img src="https://i.picsum.photos/id/100/200/100.jpg" style="width:100px">
<span></span>
</div>
In the first case the min-width constraint of the image will force it to have a width equal to 200px whereas in the second case you will have 100px. So it's wrong to assume that width isn't working in flexbox and you need flex-basis.Your code will indeed force the width to always be 100px but it's not the same as flex:0 0 100px when dealing with flex items:
By the way, your code can be simplified to only
.width-100 {
width: 100px !important;
flex-shrink: 0 !important;
}
This should produce the same output because flex-grow is by default 0 and flex-basis will consider the width you set.

FlexBox - specify a certain child div to control the width? [duplicate]

This question already has answers here:
One flex/grid item sets the size limit for siblings
(6 answers)
Closed 5 years ago.
With flexbox, the childs by default resize according to the widest element.
Is there some way to define that a particular child will control the width, even if it's smaller? With selectors maybe?
Codepen: https://codepen.io/dsomekh/pen/rwEYYE
Code:
.center {
display: flex;
align-items: center;
justify-content: center;
}
.first {
border: 1px solid red;
margin-bottom: 0.5vw;
}
.second {
border: 1px solid red;
}
.wrapper {
font-family: Calibri;
display: flex;
flex-direction: column;
}
<html>
<div class="center">
<div class="wrapper">
<div class="first">This DIV is bigger. However, can it shrink according to it's brother?</div>
<div class="second">This div is smaller.Can it control the width?</div>
</div>
</div>
</html>
Here's the important CSS rule to know:
flex: {number} {number} {number};
The third number is the default size of a flex-item (width, if the flex item is in a row). By default it is auto meaning a flex-item's default size is dictated by it's content.
The first and second numbers are proportionally how much it can grow or shrink by, respectively, compared to other flex items if there is room along the main axis (again, width if this flex item is in a row).
So, you cannot set the default size of a flex-item to be relative to a sibling's intrinsic size - i.e. that which is dictated by it's content - but you can set the default size of a flex-item (and it's sibling items) to all be the same and let them grow or shrink.
I find myself often doing the following:
flex: 1 0 0
on flex items which cause siblings to all be the same size.
All flex-items start out with a default size of 0 and they all grow equally - as given by the first number being the same for all flex items (here it's a one, but it could be any positive number as long as it's the same for every sibling) - as they need to.
Best flexbox learning around is here: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
EDIT
If you knew, in advance, which item was going to be intrinsically bigger, you could probably do it by setting that item to flex: 0 0 auto and letting all other flex-item's grow from flex: 1 0 0, but I have a feeling you don't know in advance which one is bigger.
.wrapper { display: flex; }
.wrapper>div { border: 1px solid #000; }
.first { flex: 1 0 0; }
.second { flex: 0 1 auto; }
<div class="center">
<div class="wrapper">
<div class="first">This DIV is bigger. However, can it shrink according to it's brother?</div>
<div class="second">This div is smaller.Can it control the width?</div>
</div>
</div>

Flexbox layout losing proportions when reduced in size

I'm reproducing a golden spiral using CSS flexbox and percentage size for cells.
The round shape is drawn using a regular border and border-radius combination.
Everything is proportional until I resize the window to smaller widths.
I tried removing borders completely and turns out the layout is still losing proportions at some point.
Big container:
Small container:
Demo:
https://jsfiddle.net/s09rkwub/1/
html
<div class="wrapper">
<div class="rows fib">
<div class="cols fill">
<div class="rows fill">
<div class="fr tl">3</div>
<div class="fill cols">
<div class="fc bl">4</div>
<div class="fill rows">
<div class="fill cols">
<div class="fill tl fr">7</div>
<div class="fc tr">6</div>
</div>
<div class="fr br">5</div>
</div>
</div>
</div>
<div class="fc tr">2</div>
</div>
<div class="fr br">1</div>
</div>
</div>
css
.rows {
flex-direction: column;
}
.cols {
flex-direction: row;
}
.rows,
.cols {
display: flex;
.fill {
flex: 1;
}
> * {
outline: solid 1px rgba(127,127,127, 0.3);
}
}
...
Update:
Working demo with applied solution.
Update 2
Thanks to Your support guys. I could finish my fib spiral codepen.
Solution
Add this to your code:
* {
flex-shrink: 0;
min-width: 0;
min-height: 0;
}
revised fiddle
Explanation
Two concepts to consider:
An initial setting of a flex container is flex-shrink: 1.
This means that, by default, flex items are allowed to shrink below any defined width, height or flex-basis.
To prevent this behavior use flex-shrink: 0.
More details here: What are the differences between flex-basis and width?
An initial setting of a flex item is min-width: auto.
This means that, by default, a flex item cannot be smaller than the size of its content (regardless of flex-shrink or flex-basis).
To allow flex items to shrink past their content use min-width: 0 (row direction), min-height: 0 (column direction), or overflow: hidden (other values also work, except visible).
More details here: Why doesn't flex item shrink past content size?
With the adjustment below, your flex items can shrink past the text you have in your divs, allowing the scaling to continue without any obstacles:
* {
flex-shrink: 0;
min-width: 0;
min-height: 0;
}
Taking <div class="fr br">5</div> as an example, it's min-width is 26px. Thus, scaling the spiral down to a level, where the mentioned div's height becomes less than 26px, it's getting distorted.
If you change the border width to 1px instead of 5px, you'll see, that suddenly, it's able to scale down to 18px perfectly fine and breaks in smaller sizes.
That's why you probably tried to reduce the border. However, the reason for the distortion is much simpler: You have content inside the divs and they do require some space (even if the font color is transparent). If you remove all the numbers from the div elements, you get the desired result. See the demo at https://jsfiddle.net/s09rkwub/2/

How to get flexbox to include padding in calculations?

Below are two rows.
First row is two items at flex 1 and one at flex 2.
Second Row is two items at flex 1.
According to the spec 1A + 1B = 2A
But when padding is included in the calculation the sum is incorrect as you can see in the example below.
QUESTION
How to get flex box to include padding into its calculation so the boxes in the example line up correctly?
.Row{
display:flex;
}
.Item{
display:flex;
flex:1;
flex-direction:column;
padding:0 10px 10px 0;
}
.Item > div{
background:#7ae;
}
.Flx2{
flex:2;
}
<div class="Row">
<div class="Item">
<div>1A</div>
</div>
<div class="Item">
<div>1B</div>
</div>
<div class="Item Flx2">
<div>1C</div>
</div>
</div>
<div class="Row">
<div class="Item">
<div>2A</div>
</div>
<div class="Item">
<div>2B</div>
</div>
</div>
The solution:
Set margin on the child element instead of padding on your flex item.
.Row{
display:flex;
}
.Item{
display:flex;
flex:1;
flex-direction:column;
}
.Item > div{
background:#7ae;
margin:0 10px 10px 0;
}
.Flx2{
flex:2;
}
<div class="Row">
<div class="Item">
<div>1A</div>
</div>
<div class="Item">
<div>1B</div>
</div>
<div class="Item Flx2">
<div>1C</div>
</div>
</div>
<div class="Row">
<div class="Item">
<div>2A</div>
</div>
<div class="Item">
<div>2B</div>
</div>
</div>
The problem:
The calculation is done without padding. So; adding padding to the flex element is not giving you your expected width by the
spec.
The specific article
For example, the available space to a flex item in a floated auto-sized flex container is:
the width of the flex container’s containing block minus the flex container’s margin, border, and padding in the horizontal dimension
infinite in the vertical dimension
Why is the padding not calculated? That's what the spec wants.
Determine the available main and cross space for the flex items. For each dimension, if that dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint; otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value. This might result in an infinite value.
If you subtract the padding and margin from the element's size, you get:
1A + 1B = 2A
However, after you did that, the padding was added to the element. The more elements, the more padding. That's not being calculated in the width, causing your statement to be false.
How to get flexbox to include padding in calculations?
In your code, padding is included in the calculations.
According to the spec 1A + 1B = 2A
I don't believe this is correct. Maybe provide a link reference for an explanation.
The flex-grow property
When you apply flex: 1 to an element, you are using the flex shorthand property to say this:
flex-grow: 1
flex-shrink: 1
flex-basis: 0
flex-grow tells a flex item to consume the free space in the container.
Here is your code:
.Item {
display: flex;
flex: 1;
flex-direction: column;
padding: 0 10px 10px 0;
}
In the first row, padding-right: 10px is applied to three flex items.
In the second row, padding-right: 10px is applied to two flex items.
Hence, in the first row there is 10px less free space to distribute. This breaks the grid's alignment.
For distributing space (e.g., you want an element to take the remaining height or width of a container), use flex-grow.
For precise sizing of a flex item use flex-basis, width or height.
Here's some more info:
flex-grow not sizing flex items as expected
How exactly does flex-grow work with flexbox?
Not sure I follow the accepted answer. The nested flex container doesn't seem to be relevant to the problem. If you run the example without display: flex; on .Item, the problem persists.
The problem here seems to be that flex-grow only calculates the available space that it can take after factoring in the cumulative horizontal padding.
Let's assume the top level flex container is 300px wide.
1st row's available space: 300px - 30px padding = 270px
The flex items in this row have flex-grows of 1, 1, and 2, for a total of 4 units. 270 / 4 = 67.5px. The content boxes of 1A and 1B are thus 67.5px each, the content box of 1C is 135px.
2nd row's available space: 300px - 20px padding = 280px
We have flex-grows of 1 for both 2A and 2B in this row. 280 / 2 = 140px.
So 1A and 1B would have a content box of 67.5px + 10px horizontal padding, making their total width 77.5px each.
2A would have a content box of 140px + 10px horizontal padding, making its total width 150px.
77.5px + 77.5px ≠ 150px. In other words, 1A + 1B ≠ 2A, and that's why they aren't lining up.
The accepted answer does solve the problem, but CSS grid has become well supported since that answer was submitted, and is the more idiomatic approach nowadays for this problem.
.Row{
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.Item:nth-child(n + 3) {
grid-column: span 2;
}
.Item > div{
background:#7ae;
}
<div class="Row">
<div class="Item">
<div>1A</div>
</div>
<div class="Item">
<div>1B</div>
</div>
<div class="Item">
<div>1C</div>
</div>
<div class="Item">
<div>2A</div>
</div>
<div class="Item">
<div>2B</div>
</div>
</div>
By default, when using flexbox, the padding property is not included in the calculation of the element's width or height. To include the padding in the calculation, you can set the box-sizing property to border-box.
.Row{
display:flex;
}
.Item{
display:flex;
flex:1;
flex-direction:column;
padding:0 10px 10px 0;
box-sizing: border-box; // Add this line
}
.Item > div{
background:#7ae;
}
.Flx2{
flex:2;
}
You can use floated pseudo block elements instead of padding, like this:
(In this case 30px right padding)
.Item:after {
content: '';
display: block;
float: right;
width: 30px;
height: 100%;
}

Understanding flex-grow

I have 3 divs and if I give flex: 0.5 to first two divs, the last div should move to the next line if I have given flex-wrap: wrap. Please correct if I am wrong.
Following is my html / css:
.parent {
display: flex;
height: 100px;
background-color: red;
flex-wrap: wrap;
}
.child-1 {
background-color: green;
flex: 0.5;
}
.child-2 {
background-color: yellow;
flex: 0.5;
}
.child-3 {
background-color: pink;
}
<div class="parent">
<div class="child-1">LOREN IPSUM LOREN IPSUM</div>
<div class="child-2">LOREN IPSUMLOREN IPSUMLOREN IPSUM</div>
<div class="child-3">LOREN IPSUMLOREN IPSUM</div>
</div>
Please check JSFiddle for this.
Thanks in advance.
The flex-grow property is designed to distribute free space in the container among flex items.
It is not intended for directly or precisely sizing flex items.
From the spec:
flex-grow ... determines how much the flex item will grow relative
to the rest of the flex items in the flex container when positive free
space is distributed.
Hence, flex-grow will not force items to wrap. Here's your example with flex-grow: 1000: demo
To define a length of a flex item use width, height or flex-basis.
Explaining flex-grow: 0.5
When you apply flex:0.5, you're using the flex shorthand property to say this:
flex-grow: 0.5
flex-shrink: 1
flex-basis: 0
The flex-grow component represents a proportion. In this case, it's telling flex items to consume half of the available space in the container relative to the flex-grow factor of its siblings.
So, for instance, if the container were width: 600px and the total width of the three divs was 450px, this would leave 150px in free space (demo).
If each item had flex-grow: 1, then each item would consume 50px of the extra space, and each item would compute to width: 200px (demo).
But in your code, two items have flex-grow: 0.5, and the last item has flex-grow: 0, so here's how it breaks down:
div#1 will get 75px (half of the available space)
div#2 will get 75px (half of the available space)
div#3 will get 0 (because its flex-grow is 0)
These are the computed values now:
div#1 = width: 225px
div#2 = width: 225px
div#3 = width: 150px
demo
.parent {
display: flex;
flex-wrap: wrap;
height: 100px;
background-color: red;
width: 600px;
}
.parent > div {
flex-basis: 150px;
}
.child-1 {
flex: 0.5;
background-color: green;
}
.child-2 {
flex: 0.5;
background-color: yellow;
}
.child-3 {
background-color: pink;
}
<div class="parent">
<div class="child-1">LOREN IPSUM LOREN IPSUM</div>
<div class="child-2">LOREN IPSUMLOREN IPSUMLOREN IPSUM</div>
<div class="child-3"> LOREN IPSUMLOREN IPSUM</div>
</div>
NOTE: The truth of the matter is, because divs #1 and #2 have the same flex-grow factor, and div #3 has a factor of 0, the value is irrelevant. You could use any number to replace 0.5. As long as it's the same in both divs, the items will compute to 225px, because the proportions will be the same.
More information:
flex-grow not sizing flex items as expected
How to get flexbox to include padding in calculations?
What are the differences between flex-basis and width?
Flex = 0.5 means, you have flex-grow = 0.5, flex-shrink = 1 and flex-basis = 0
you are allocating the available space to flex items.
Let's just say, you have a container (Assuming div element) of width: 100vw with two flexible items(div elements) of each 40vw in it. So the total width consumed by your two div elements will be equals to 80vw and the left over available space would be 20vw. Now, You have set the flex = 0.5 for each of the two div elements. so, Each div element will grow up by 20vw/2 = 10vw in order to fully occupy the free space of the container.
Consider one more scenario with 3 div elements, div1 of width = 30vw, div2 of width = 10vw and div3 of width = 25vw.
Total width consumed by 3 div elements = 30 + 10 + 25 = 65vw.
Total available space = 100 - 65 = 35vw.
Now, Each div element is set to flex = 0.3, so each will grow up by 35/3 =~ 11.66vw