CSS flex-wrap: Put gap between items unless wrapped? - css

I come often across with this scenario, where i have i.e. 2 flex-children, with property flex-direction row. so they are first shown side by side with a gap between them (margin-right).
And by resize, as soon as there is not enough space left for both, flex-wrap moves the second child under the first one, so i don't need the margin-right from 1. item anymore.
Can i dynamically set the margins depends on the "wrap" status?
Fiddle: https://jsfiddle.net/ejmhxztd/
Fiddle Note: You should resize the window (reduce width) and see the wrapped case. If you continue reducing the width, you will see that the text of the first child will also split into 2 lines, since the margin-right is there and takes space.
.parent {
display:flex;
flex-direction:row;
flex-wrap:wrap
}
.child1 {
margin-right:300px
}
<div class="parent">
<span class="child1">Child1 Text</span>
<span class="child2">Child2 Text</span>
</div>

I know this is an old issue, but you can use "column-gap" and "row-gap" to define gaps only in some direction.
For example:
.class{
display: flex;
align-items: center;
column-gap: 10px;
flex-wrap: wrap-reverse;
flex-direction: row;
}
In this case, the gap will only affect in columns.

You can try to use CSS-grid for this:
.parent {
display:grid;
grid-column-gap:300px;
grid-template-columns:repeat(auto-fit,minmax(100px,max-content));
}
.parent > * {
border:1px solid;
}
<div class="parent">
<span class="child1">Child1 Text</span>
<span class="child2">Child2 Text</span>
</div>

Related

Flexboxgrid and gap overflow issue

Im using flexboxgrid library to create easy responsive layout, I have a parent div styled like so
display: flex;
flex-wrap: wrap;
gap: 2rem;
children have flexboxgrid styling
col-xs-12 col-md-6 col-lg-4
and it works otherwise well, except when I put that 'gap: 2rem' on parent, then div's start overflowing and push last item to another row.
To illustrate problem:
How can I fix it ?
EDIT: Link to CodePen, with gap there is 2 rows, without gap 1 row.
How to keep gap, stay on 1 row ?
https://codepen.io/ShinigamiZ/pen/YzezgwE
If you want to spread them out over the whole width, don't set a flex-basis for the elements. Rather set flex-grow: 1. This means, that the elements will grow to be as big as possible.
If you want to wrap them to a new line, you need to alter your calculation for flex-basis to also incorporate the gap.
.parent {
display: flex;
flex-wrap: wrap;
gap: 2rem;
background-color: yellow;
}
.sib {
background-color: gray;
flex-grow: 1;
}
<div class='parent'>
<div class='sib'>
123
</div>
<div class='sib'>
123
</div>
<div class='sib'>
123
</div>
</div>
Column classes (col-xs-12 col-md-6 col-lg-4) means it's percentage width and that's where the issue is because the gap is not included in percentage calculation.
My take on this is to allow the columns shrink and grow but only by the amount of gap you define. With your example that would be something like:
.col-md-6 {
flex: 1 0 calc(50% - 2rem);
max-width: 50%;
}
The only issue is a slight inconsistency with columns width if there's a empty space left or if it's the only column and not fullwidth as the elements have their own original percentage width and not one reduced by gap.
https://codepen.io/Erehr/pen/jOxYadW

Using flex-wrap:wrap but having trouble using pseudo selectors to adjust position of last item

We have an odd number of items inside of a flex: flex-wrap container and at a certain resolution when they wrap the last of the items is over to the left but I want it to (continue to) be to the right.
I googled and found a resource discussing a similar issue at: https://haizdesign.com/css/flexbox-align-last-item-grid-left/
The ::after pseudo-element they applied to achieve this is:
.speakers::after {
content: '';
flex: auto;
}
So I tried to apply this knowledge, but instead use the ::before pseudo-element to try to move my last item over to the right, but I could not get it to work. Below is some HTML and CSS code followed by a link to the CodePen:
#container {
display: flex;
flex-wrap: wrap;
}
.el-width {
min-width: 40%;
}
.last-el::before {
content: '';
flex: auto;
}
<div id="container">
<div class="el-width">Foo</div>
<div class="el-width">Bar</div>
<!-- uncomment to move Baz under Bar
<div class="el-width last-el"> </div>
-->
<div class="el-width last-el">Baz</div>
</div>
https://codepen.io/dexygen/pen/ExaNZYv
As you can see in the HTML if you interpose an actual (empty) div, Baz gets moved under Bar. I've also been able to introduce an actual element in my application and it does likewise. However I'd like to know how or if it can be achieved using ::before
This would be a lot easier with CSS Grid. I leave this here as an alternative answer, in case it helps others.
#container {
display: grid;
grid-template-columns: 40% 40%;
}
.el-width {
border: 1px solid;
}
.last-el {
grid-column-start: 2;
}
<div id="container">
<div class="el-width">Foo</div>
<div class="el-width">Bar</div>
<!-- uncomment to move Baz under Bar
<div class="el-width last-el"> </div>
-->
<div class="el-width last-el">Baz</div>
</div>
Pseudo elements on a flex container are treated as flex items (source).
So the first problem is that the pseudo element in your code is applied to the flex item (.last-el). It needs to be applied to the flex container (#container).
Then, the default order matters. The ::before() pseudo is the first flex item, and an ::after() pseudo would be the last.
So, if you're going to use a pseudo element as a flex item, to bump over an inner item, you need to use the order property to re-arrange the visual order. (Incidentally, this obviates the need to choose between ::before() and ::after().)
#container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.el-width {
flex: 0 0 33.33%;
}
#container::before {
order: 1;
flex: 0 0 33.33%;
content: '';
}
.el-width:nth-child(-n+3) {
order: 0;
}
.el-width:nth-last-child(-n+2) {
order: 2;
}
<div id="container">
<div class="el-width">Item 1</div>
<div class="el-width">Item 2</div>
<div class="el-width">Item 3</div>
<div class="el-width">Item 4</div>
<div class="el-width">Item 5</div>
</div>
The pseudo element method you're describing in your question is explained here:
Properly sizing and aligning the flex item(s) on the last row
A description of the problem, along with potential solutions, can be found here:
Targeting flex items on the last or specific row
And a clean and efficient solution for handling this problem, using CSS Grid, is here:
Equal width flex items even after they wrap
Fixing this is simple and to do it, we use a pseudo element. Going back to our container, (in this case, my container has a class of .speakers)
So they applied the ::after to the container to create a last element, not they apply to the last element last-el. And they did that because they used justify-content: space-between to justify their items leads their last element to unexpected position, which seems to not be of your case. If you want to layout in 2 dimensions, CSS Grid is the best. If you want better browser compatible, then you might already have the answer yourself in the codepen you gave. But I think what you really want might just be the answer that you basically can't solve this by just adding styles to .el-width::before?

3 column grid layout spaced out over width with dynamic content

I'm trying to achieve the following layout with dynamic content:
code:
.container {
display: flex;
flex-direction:row;
justify-content:space-between;
}
<div class="container">
<span>Published</span>
<span>Song title</span>
<span>Edit song</span>
</div>
What would be the best way to go about it, taking into account that sometimes the texts in each span don't always appear, I want they layout to be fixed so that even if one of the texts doesn't appear they always remain in the same place. Thanks!
The original code above I tried with display:flex doesn't work because when the text doesn't appear the grid collapses.
add the following css
.container {
display: flex;
flex-direction:row;
justify-content:space-between;
}
https://jsfiddle.net/hxazcg71/

Flex-Basis: 0% causes incorrect overflow in IE11

The problem I have involves flex-basis: 0%; and how IE11 handles it when the user reduces the window's width. Could be related to the bug with box-sizing.
I have a variable number of flex-children, and each child is of unknown width. Each child has arbitrary dynamically-generated content. However, some of this content must have the ability to wrap text.
The flex-container itself must have 100% width and must be able to wrap its children (i.e., flex-wrap: wrap).
Let's assume three flex-children are present, with the last one requiring text-wrapping:
<div class="flex-container">
<div class="flex-child">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child">
<div class="text-break">This text should break on new lines when the container shrinks down</div>
</div>
</div>
The CSS can be defined as follows:
.flex-container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.flex-child {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 0%;
white-space: nowrap;
padding: 5px;
}
.text-break {
white-space: pre-wrap;
}
When the window is wide enough, each flex-child should be side-by-side, and the text not collapsed:
When the window is shrunken horizontally, the text in the right-most box should begin to break to new lines:
And when the text can no longer break into more lines, the flex-boxes themselves should begin to break lines:
This works great in most modern browsers but not our friend, IE11. In IE11, when changing the screen size, the text wraps fine, but the flex-children do not wrap at all. Because the flex-children do not wrap, their content overflows into one another:
Changing the flex basis to flex-basis: auto; has the opposite effect: the text will not wrap but the flex-children do, but no content overflows (in this screenshot, the text in the green box should be breaking lines rather than the flex-box breaking into a new line):
Most solutions I have seen require having fixed-length flex-children which I cannot afford to have here because they are dynamically generated. I intentionally did not use the flex shortcut property because of some other non-related issues with it. This answer recommends using _:-ms-fullscreen, :root .IE11-only-class { /* IE11 specific properties */ } which might work if I could get around the text-wrapping issue, but I cannot figure that out.
I should also say that I only need this to work on the latest browsers (technically only on certain versions of Chrome and IE11, but having other browsers and versions work as well is a plus).
Here is a code pen showing the problem in full (view in IE11 and Chrome to see the difference).
Does anyone have any ideas as to how to get around this issue?
Remember, the requirements are:
unknown number of flex-children of unspecified width
certain text must wrap before the flex-boxes wrap
flex-children must wrap once the text can no longer wrap more
Chrome (I am using 59.0.3071.109) and IE11 (I am using 11.0.9600.18816CO) should behave in the same way.
CSS-only solutions are preferred
Thank you.
Update:
A coworker of mine recommended using a separate class for flex-children that do not contain wrappable text. The following HTML and CSS were what he used:
<!--HTML-->
<div class="flex-container">
<div class="flex-child child-1">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child flex-child-without-textwrap child-2">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child flex-child-with-textwrap child-3">
<div class="text-break">This text should break on new lines when the container shrinks down</div>
</div>
</div>
And
/*CSS*/
.flex-container {
display: flex;
flex-wrap: wrap;
}
.flex-child {
flex-grow: 1;
flex-shrink: 1;
padding: 5px;
white-space: nowrap;
}
.flex-child-without-textwrap {
flex-basis: auto;
}
.flex-child-with-textwrap {
min-width: 200px;
flex-basis: 0%;
}
.text-break {
white-space: pre-wrap;
}
His solution technically fixed the problem as I presented it here, but I forgot to mention another requirement:
Within a flex-child, there can be any combination of wrappable text and unwrappable text
His solution does not succeed when changing the third flex-child to:
<div class="flex-child flex-child-with-textwrap child-3">
<div class="text-break">This text should break on new lines when the container shrinks down</div>
<div>This text should not break on new lines</div>
</div>
In this case, the text in the second div in the third flex-child flows out of its container, and the text begins wrapping too early.
A codepen showing this almost-working solution can be seen here (note that min-width was removed because it caused further issues on Chrome).
Update 2:
I don't think I was clear enough earlier: any, all, or none of the flex-children may have wrappable text. It all depends on the dynamically-generated content. In the example I gave, only the third flex-child has wrappable text, but that might not always be the case.
Note, this answer were posted prior to the question's 2 updates, which made it partially invalid, though I will leave it for now, someone might need it as is.
After a lot of trial-and-error, I came up with this set up, where I aim to make IE mimic the rest of the browsers behavior as much as possible.
IE need a minimum width on the break-able's parent, so the text won't collapse into 0 width before the element wrap, and as flex-basis needs to be auto in general but 0px on the break-able's parent, the flex-grow's need to be somewhere around 5.
I added the following rules using an IE11 only selector (which I showed in this answer of mine).
_:-ms-fullscreen, :root .flex-child {
flex-basis: auto;
}
_:-ms-fullscreen, :root .child-3 {
flex: 5 0 0px;
min-width: 80px;
}
Updated codepen
Stack snippet
.wrapper {
width: 80%;
}
.flex-container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.flex-child {
flex: 1 0 0%;
white-space: nowrap;
padding: 5px;
}
_:-ms-fullscreen, :root .flex-child {
flex-basis: auto;
}
_:-ms-fullscreen, :root .child-3 {
flex: 5 0 0px;
min-width: 80px;
}
.text-break {
white-space: pre-wrap;
}
/*Add backgrounds for display*/
.child-1 {
background-color: pink;
}
.child-2 {
background-color: lightblue;
}
.child-3 {
background-color: lightgreen;
}
<div class="wrapper">
<div class="flex-container">
<div class="flex-child child-1">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child child-2">
<div>This text should not overlay other flex-children.</div>
</div>
<div class="flex-child child-3">
<div class="text-break">This text should break on new lines when the container shrinks down</div>
</div>
</div>
</div>
This is NOT a preferred solution because it uses Javascript. However, it works.
After creating all flex-children, and whenever the screen size changes, check if the width of each flex-child is less than the width of its content (for this to work, the content must be wrapped with something with display: table-cell). If so, add a minimum width to the flex-child equal to the width of its content. Once a min-width is added, the calculation need not be done again, so no longer check when screen size changes. Only do this on IE11.
I used jquery to implement this solution.
HTML:
<div class="flex-container">
<div id="1" class="flex-child child-1">
<div class="content-container">This text should not overlay other flex-children.</div>
</div>
<div id="2" class="flex-child child-2">
<div class="content-container">This text should not overlay other flex-children.</div>
</div>
<div id="3" class="flex-child child-3">
<div class="content-container">
<div class="text-break">This text should break on new lines when the container shrinks down</div>
<div>This text should not break on new lines</div>
</div>
</div>
</div>
CSS:
.flex-container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.flex-child {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 0%;
white-space: nowrap;
padding: 5px;
}
.text-break {
white-space: pre-wrap;
}
.content-container {
display: table-cell;
}
Jquery:
if (!!window.MSInputMethodContext && !!document.documentMode) {//if IE11
$(".flex-child").each(function (i, elem) {
var $flex_child = $(elem);
var child_id = $flex_child.attr("id");
//add resize event to window
$(window).on("resize." + child_id, function () {
var width_of_flex_child = $flex_child.outerWidth();
var content_width = $flex_child.children(".content-container").outerWidth();
if (width_of_flex_child < content_width) {
$flex_child.css("min-width", content_width + "px");
//remove event
$(window).off("resize." + child_id);
}
}).trigger("resize." + child_id);
});
}
The codepen to view this solution can be found here.
Unfortunately, this solution requires additional overhead when managing resources because of the window event.

Positioning of elements in a box with Flexbox

I have been trying to make a row of responsive boxes present a nicer look. After lots of effort and googling, I am here to get a word from experts. Please check the image below:
Outermost red is a bootstrap flexible row with display:flex;
Each box, the first of which is represented by green box, has flex: 1 ...;
Until this point, there is no issue and my CSS works perfect on all screen sizes showing all the boxes in same height and width. I just have two issues which I need help on.
Issue 1:
I need that lower part of box (represented by orange border) may always get positioned to the bottom of green box. This way all the buttons will appear in same line.
I tried to use a wrapper div in each box and then set position attribute for wrapper to relative and those of inner divs (yellow & orange) to absolute. Then I set the lower one to bottom: 0px;. But it does not work with flex and needs me to mention fixed height of wrapper which I cannot mention.
Issue 2:
In the box with the blue border I need the text of all lines to be justified except the last line which should be left aligned.
Issue 1
Assign display: flex to the div that is presented by the green box. After that, add align-self: flex-end; to the orange box. The orange box should now be displayed at the end of the green box.
Issue 2
Use the following fix to achieve what you want:
.blue-box {
text-align: justify;
-moz-text-align-last: right;
text-align-last: left;
}
The problem is that this wis not supported by Safari on Mac and iOS devices. You would have to add more markup to also cover Safari. An example would be to wrap each text line into a p tag if it's possible. Then you could do this:
.blue-box p {
text-align: justify;
}
.blue-box p:last-child {
text-align: left;
}
Please report back if the fixes do work for you or not.
The best way to solve this is to use flexbox.
.promo-boxes {
width: 100%;
display: flex;
justify-content: space-between;
background-color:black;
}
.promo-box {
width: 100px;
border: solid 1px #ccc;
background-color:red;
display: flex;
}
.btns-wrapper {
margin-top: auto;
align-self: flex-end;
}
<div class="promo-boxes">
<div class="promo-box">
<div>test 2</div>
<div class="btns-wrapper">
<button>
subscribe
</button>
</div>
</div>
<div class="promo-box">
<div>
test 3
</div>
<div class="btns-wrapper">
</div>
</div>
<div class="promo-box">
<div>
test 4 <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
loooong
</div>
<div class="btns-wrapper">
</div>
</div>
</div>

Resources