Images in flex items causing equal width to break - html

The red borders are around flex items. They have
flex-grow: 1
These items are contained in a flex container:
width: 100%
display: flex
flex-direction: row
align-items: flex-end
As you can see the vertical alignment is working fine.
When I put the images in, the width of the images pushes the flex items to be bigger or smaller than they should be. The width of the images overrides the width of the flex items. What I want to say is something like:
Firstly make the flex items all the same size and then size the contained images (whatever their natural size) to be the size of their container (the flex item). I've tried width: 100% and width: auto on the images. Didn't help.
This image shows the equally spaced flex items (red borders). I want the images to fit into these boxes without causing the width of the box to change. This is the behaviour I get if I replace the flex items with table cells.
This Fiddle shows the 3 equal boxes: https://jsfiddle.net/justinwyllie/zdrd89gu/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">B</div>
<div class="flex-boxes">C</div>
</div>
This one shows an image in the middle flex item. It has completely messed up the equal boxes. The question is what do I need to do to make the cat fit into the middle box? (Fit width-wise that is; i don't mind about height).
https://jsfiddle.net/justinwyllie/zdrd89gu/1/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
.cat {
width: auto;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>

The flex-grow property tells flex items how much available space in the container to consume.
In your second image, the empty items are all equal width because flex-grow: 1 tells them to distribute available space equally among themselves.
Any content you put in these items will impact flex-grow since, as mentioned, flex-grow is based on the free space in the container – and content consumes space.
If you want the items to maintain a fixed width, use flex-basis.
So instead of:
flex-grow: 1;
Try:
flex: 0 0 20%; /* don't grow, don't shrink, fixed width at 20% */
The 20% is just an example, if you wanted five items per row.
Here's your revised code:
jsFiddle
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
/* flex-grow: 1; <-- REMOVE */
flex: 0 0 33.33%; /* NEW */
border: 2px solid red;
/* box-sizing: border-box; you may need to add this property if
you enable wrapping */
}
.cat {
width: auto;
/* vertical-align: bottom; add this property if you want to remove
the small gap of whitespace underneath images
https://stackoverflow.com/a/31445364/3597276 */
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>
Learn more here:
flex-grow not sizing flex items as expected

Related

Flex shrink in nested containers

I am trying to mimic a nav bar with flex box where i have 2 sets of items - nav-left aligned to the left and nav-right aligned to the right. Each item has 50px width.
nav-right should take the width of the children. nav-left should take up the rest of the space.
https://jsbin.com/xanaxaruxo/edit?html,css,output
.nav-group {
display: flex;
flex-direction: row;
border: 2px solid;
padding: 2px;
}
.nav-group-left {
border-color: turquoise;
flex: 1 1 auto;
}
.nav-group-right {
border-color: lime;
flex: 0 1 auto;
}
My expectation is this -
The nav-left container should take the available space but when i shrink, this space should shrink first but when total is more than the container- all the elements should shrink together.
I have put flex: 1 1 auto - but the elements are overflowing the container;
(2nd bar)
But it works when the flex containers are not nested.
https://jsbin.com/bapaqecabe/edit?html,css,output
My question is why does not shrink work with nested flex containers.
It doesn't work when the items are nested because they have a defined width. Their parents have flex basis of auto and shrink set to true, they do what they're supposed to, but their children will expand them regardless.
If you require your navigation items to have a static width, one solution I see being used a lot is to have them show as a list on smaller devices.
If you're fine with them shrinking a bit, you could make them flexible, set their basis to 50px, and modify both containers to grow, but the left one more than the right one, say 3 to 1.
.nav-group-left {
border-color: turquoise;
flex: 3 1 auto;
}
.nav-group-right {
border-color: lime;
flex: 1 1 auto;
}
.item {
border: 3px solid;
display: flex;
flex-basis: 50px;
}
Example
An explicit width will override flex-grow and flex-shrink. For flex-grow and flex-shrink to work their direct parent must be set to display: flex;. Their widths will be rendered relative to the width of their content.
<div style="display: flex;">
<div style="flex-grow: 1;"><p>Div should use up remaining space.</p></div>
<div style="flex-shrink: 1;"><p>Div should shrink to minimal space.</div>
</div>
Use flex-grow when you have an element (typically display: inline;) to use up the rest of the space (e.g. an input that you use for meta descriptions).
Use flex-shrink for elements (typically display: block;) that you do not want using up more than it's equal share of space.

How do I align inline element to the side of a centered div with 50vw so that it resizes properly?

I have a div that is set to be 50% of the page's width at all times, and is centered.
I would like to add an element to it's side so that the element touches the div's left edge (on different screen sizes). The element has a fixed max-width.
I've tried:
#element {
position: absolute;
align: left;
padding-left: 3vw;
}
It works on most common screen sizes, but as I go very small or very big the element either overlaps with the div or there is a gap. What I would like ideally would be for the element to stick to the side of the div.
Absolute positioning is very problematic for this kind of layout. I recommend a flexbox layout.
In order to achieve this layout we will need to add a couple divs. One div to act as the 3rd column, I gave it the class ghost because it will contain nothing although the demo here uses a red border so you can see it.
The other div I added wraps the special element you want to stick to the middle div. This is so the element wrapper (.element-wrap) is a flex child, not the element itself.
Next, give the container of these 3 divs display: flex, turning it into a flexbox container:
section {
display: flex;
align-items: center; /* vertical alignment */
justify-content: center; /* horizontal alignment */
}
The align-items property allows you to vertically align the items inside, while justify-content allows you to align them horizontally.
The center div, which I gave a blue background, will of course be in the middle of the layout and maintains its width with width: 50vw;. The 2 divs on the side have a class applied .flex-child which get this CSS:
.flex-child {
flex-grow: 1;
flex-basis: calc((100% - 50vw) / 2);
}
flex-grow: 1 allows those side divs to expand with the size of the screen. The flex-basis rule determines the starting size, where I am using a calc() function to dynamically calculate the width.
Now, the special element with the yellow background needs to be positioned next to the blue background, which is achieved here by turning its wrapper div into a flexbox too and aligning the content to the right:
.element-wrap {
display: flex;
justify-content: flex-end;
}
* { box-sizing: border-box; }
section {
display: flex;
align-items: center;
justify-content: center;
/* just for demo */
background: #eee;
}
.flex-child {
flex-grow: 1;
flex-basis: calc((100% - 50vw) / 2);
/* just for demo */
border: 1px solid red;
}
.half-screen {
width: 50vw;
min-height: 300px;
background: dodgerblue;
}
.element-wrap {
display: flex;
justify-content: flex-end;
}
.element {
/* just for demo */
background: yellow;
}
<section>
<div class="flex-child element-wrap">
<div class="element">
ELEMENT that we are<br>
getting to stick to the side<br>
of the 50vw div.
</div>
</div>
<div class="half-screen">
50vw DIV
</div>
<div class="flex-child ghost"><!-- nothing here --></div>
</section>

alignment breaks when placed inside other container

I am exploring Flexbox and am trying to align some items. The following code works and shows what I want to achieve (successful codepen here):
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
}
.labelinput > *:first-child {
flex-basis: 10em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is an input box" />
</div>
The above code produces the nicely align output shown below:
My reading of the above code is that it works because the first child in every div has a determined size (10em) and is totally inflexible (flex-grow and flex-shrink set to 0) whereas the second child has no determined size and will grow and shrink as appropriately.
What breaks though is when I try to embed the two top-level div elements (of class labelinput) into yet another container (failing codepen here):
#container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 5px;
border: 1px solid grey;
}
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
}
.labelinput > *:first-child {
flex-basis: 7em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div id='container'>
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is the input box" />
</div>
</div>
The above produces the following unsatisfactory output:
I can't explain why this fails as I am just inserting the content of the successful case into a container that simply performs the default vertical stacking (flex-direction: column;).
By experimentation I have discovered that removing the align-items property from the outer level container (#container) fixes the problem but I can't explain that either.
I understand that the outermost container (#container) is asked to layout two other containers (of class labelinput) so whatever property I set for align-items in the outermost container should apply to the inner containers as a whole, not change the layout of their internal items.
Moreover I can't explain why the layout is changed based on the element type when there's nothing in my CSS that differentiates between items of element div versus items of element input.
I can't explain why this fails as I am just inserting the content of the successful case into a container that simply performs the default vertical stacking (flex-direction: column).
The difference is that this new primary container has align-items: flex-start.
By experimentation I have discovered that removing the align-items property from the outer level container (#container) fixes the problem but I can't explain that either.
When you nest the .labelinput flex containers in the larger container (#container), then the .labelinput elements become flex items, in addition to flex containers.
Since the #container flex container is set to flex-direction: column, the main axis is vertical and the cross axis is horizontal1.
The align-items property works only along the cross axis. It's default setting is align-items: stretch2, which causes flex items to expand the full width of the container.
But when you override the default with align-items: flex-start, like in your code, you pack the two labelinput items to the start of the container, as illustrated in your problem image:
Because stretch is the default value for align-items, when you omit this property altogether, you get the behavior you want:
I understand that the outermost container (#container) is asked to layout two other containers (of class labelinput) so whatever property I set for align-items in the outermost container should apply to the inner containers as a whole, not change the layout of their internal items.
The outermost container is not changing the layout of the inner container's children. At least not directly.
The align-items: flex-start rule on the outermost container is applying directly to the inner containers. The internal items of the inner containers are just responding to the sizing adjustment of their parent.
Here's an illustration of align-items: flex-start impacting .labelinput (red borders added).
#container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 5px;
border: 1px solid grey;
}
.labelinput {
display: flex;
flex-flow: row;
margin: 1px;
border: 2px dashed red;
}
.labelinput > *:first-child {
flex-basis: 7em;
flex-grow: 0;
flex-shrink: 0;
}
.labelinput > *:nth-child(2) {
flex-grow: 1;
flex-shrink: 1;
border: 3px solid purple;
}
<div id='container'>
<div class='labelinput'>
<div>1st input</div>
<div>this is just a div</div>
</div>
<div class='labelinput'>
<div>2nd:</div>
<input type="text" name="foo" value="this is the input box" />
</div>
</div>
Moreover I can't explain why the layout is changed based on the element type when there's nothing in my CSS that differentiates between items of element div versus items of element input.
There may be no difference between div and input in your code, but there are intrinsic differences.
Unlike a div, an input element has a minimum width set by the browser (maybe to always allow for character entry).
You may be able to reduce the input width by applying min-width: 0 or overflow: hidden3.
Footnotes
1. Learn more about flex layout's main axis and cross axis here: In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
2. Learn more about the align-items property in the spec: 8.3. Cross-axis Alignment: the align-items and align-self properties
3. Why doesn't flex item shrink past content size?

How to swap the position of divs?

I want to be able to swap the position of two divs depending on the screen size.
I have this JSfiddle.
HTML
<div class="divMain">
<div class="div2">
</div>
<div class="div3">
</div>
</div>
CSS
.divMain{
height: 500px;
width: 500px;
border: 2px solid black;
}
.div2{
display: inline-block;
height: 200px;
width: 200px;
border: 2px solid red;
}
.div3{
display: inline-block;
height: 200px;
width: 200px;
border: 2px solid blue;
}
#media only screen and (max-width: 500px) {
.div2{
background-color: red;
}
.div3{
background-color: blue;
}
}
}
When the screen size is wider than 500px wide, i want div2 to be on the left and div3 to be on the right. Once the screen size reduces to 500px or smaller, i'd like div2 to be on the right and div3 to be on the left.
How would i go about this?
Help is much appreciated.
Thanks
You can add a float left on your div 3
#media only screen and (max-width: 500px) {
.div2{
background-color: red;
}
.div3{
background-color: blue;
float:left;
}
}
This pulls div 3 to the left of div 2
fiddle: https://jsfiddle.net/x0x2tu38/3/
It may be something of a hack, but a technique I find myself reaching for when I want an element to be be laid out in a different part of my HTML document depending on screen size – as opposed to just styled differently – is to just duplicate the element and then hide or show the two versions of the element using #media queries.
Using your example:
HTML
<div class="divMain">
<div class="div2 visible-small"></div>
<div class="div3"></div>
<div class="div2 visible-large"></div>
</div>
CSS
.divMain{
height: 500px;
width: 500px;
border: 2px solid black;
}
.div2{
display: inline-block;
height: 200px;
width: 200px;
border: 2px solid red;
}
.div3{
display: inline-block;
height: 200px;
width: 200px;
border: 2px solid blue;
}
#media only screen and (max-width: 500px) {
.visible-large {
display: none;
}
.div2{
background-color: red;
}
.div3{
background-color: blue;
}
}
#media only screen and (min-width: 501px) {
.visible-small {
display: none;
}
}
I can't speak to whether or not this has performance implications, but it certainly gets the job done. I prefer it mostly because it covers all cases – not just when the elements in question are right after one another in the HTML document – but also because I find the behavior of the css float property to be beyond the grasp of my simple mind.
I should also say that, if you're using Bootstrap, then helper classes that do this are built in! And it looks like Foundation has classes that do something similar.
Try to play with flexes:
display
This defines a flex container; inline or block depending on the given value. It enables a flex context for all its direct children:
.container {
display: flex; /* or inline-flex */
}
flex-direction
This establishes the main-axis, thus defining the direction flex items are placed in the flex container. Flexbox is (aside from optional wrapping) a single-direction layout concept. Think of flex items as primarily laying out either in horizontal rows or vertical columns:
.container {
flex-direction: row | row-reverse | column | column-reverse;
}
row (default): left to right in ltr; right to left in rtl
row-reverse: right to left in ltr; left to right in rtl
column: same as row but top to bottom
column-reverse: same as row-reverse but bottom to top
flex-wrap
By default, flex items will all try to fit onto one line. You can change that and allow the items to wrap as needed with this property. Direction also plays a role here, determining the direction new lines are stacked in.
.container{
flex-wrap: nowrap | wrap | wrap-reverse;
}
nowrap (default): single-line / left to right in ltr; right to left in rtl
wrap: multi-line / left to right in ltr; right to left in rtl
wrap-reverse: multi-line / right to left in ltr; left to right in rtl
justify-content
This defines the alignment along the main axis. It helps distribute extra free space left over when either all the flex items on a line are inflexible, or are flexible but have reached their maximum size. It also exerts some control over the alignment of items when they overflow the line.
.container {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
flex-start (default): items are packed toward the start line
flex-end: items are packed toward to end line
center: items are centered along the line
space-between: items are evenly distributed in the line; first item is on the start line, last item on the end line
space-around: items are evenly distributed in the line with equal space around them. Note that visually the spaces aren't equal, since all the items have equal space on both sides. The first item will have one unit of space against the container edge, but two units of space between the next item because that next item has it's own spacing that applies.
align-items
This defines the default behaviour for how flex items are laid out along the cross axis on the current line. Think of it as the justify-content version for the cross-axis (perpendicular to the main-axis).
.container {
align-items: flex-start | flex-end | center | baseline | stretch;
}
flex-start: cross-start margin edge of the items is placed on the cross-start line
flex-end: cross-end margin edge of the items is placed on the cross-end line
center: items are centered in the cross-axis
baseline: items are aligned such as their baselines align
stretch (default): stretch to fill the container (still respect min-width/max-width)
align-content
This aligns a flex container's lines within when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis.
.container {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
flex-start: lines packed to the start of the container
flex-end: lines packed to the end of the container
center: lines packed to the center of the container
space-between: lines evenly distributed; the first line is at the start of the container while the last one is at the end
space-around: lines evenly distributed with equal space around each line
stretch (default): lines stretch to take up the remaining space
Following above, try to use something like that in your code:
<div class="container">
<button>div1</button>
<button>div1</button>
</div
#media only screen and (max-width: 500px) {
.container {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: space-around;
align-items: center
}
}
I also prepared snippet for you to let you play with it.
.container {
display: flex;
flex-wrap: wrap;
flex-direction: row;
justify-content: space-around;
align-items: center
}
<div class="container">
<button>div1</button>
<button>div1</button>
</div>
More info on flexes (including examples) you can find here

Equally spaced images in a flexbox

I am using a flexbox container that is using a row layout that wraps. Each of the items is exactly the same size (150x267 pixels). I have added a margin of 10px to each of the items to provide some space.
Flexbox does a great job of laying out the items but I am trying to get a layout so that whatever the viewport width, the spacing between each of the items is the same. Sometimes this occurs but at a width of about 1400px, there is noticeably a much larger gap between the images than the 10px margin on each end of the row.
My CSS is
div#imgcontainer {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
font-size: 0;
}
div#imgcontainer img {
margin: 10px;
height: 150px;
}
You can see an example of the layout at 3-dot-timb-photos.appspot.com