I have a series of inline-block divs that can wrap onto a second line depending on how wide the browser is at a given time. Here's a fiddle example. For example, it might look like this:
However if the viewport is of a certain width, it can end up with just one orphaned inline-block on the next line:
Is there a CSS method that is essentially some form of "orphan control" (CSS orphan doesn't seem to apply in this situation) so that if there are fewer than a certain number of elements on the second line, it'd wrap more of them down there to even things out?
.container {
width: 100%;
}
.item {
display: inline-block;
width: 50px;
background-color: blue;
}
<div class="container">
<div class="item">
Item 1
</div>
<div class="item">
Item 2
</div>
<div class="item">
Item 3
</div>
<div class="item">
Item 4
</div>
<div class="item">
Item 5
</div>
<div class="item">
Item 6
</div>
<div class="item">
Item 7
</div>
</div>
Here is an idea using CSS grid where the trick is to make sure your div has a width expressed as a multiple of a fixed amount to make sure you never have an orphan element.
.wrapper {
--w: 100px; /* width of one item */
display: grid;
grid-template-columns: repeat(auto-fit, calc(2*var(--w))); /* the item will break two by two*/
justify-content:center;
}
.container {
grid-column: 1/-1;
display:flex;
flex-wrap:wrap;
justify-content:center;
}
.item {
width: calc(var(--w) - 10px);
margin: 5px;
}
/* irrelevant styles */
.container {
counter-reset: num;
}
.item {
height: 150px;
background-color: blue;
color: #fff;
}
.item::before {
color: #fff;
content: attr(class) " " counter(num);
counter-increment: num;
}
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
If we use 3
.wrapper {
--w: 100px; /* width of one item */
display: grid;
grid-template-columns: repeat(auto-fit, calc(3*var(--w)));
justify-content:center;
}
.container {
grid-column: 1/-1;
display:flex;
flex-wrap:wrap;
justify-content:center;
}
.item {
width: calc(var(--w) - 10px);
margin: 5px;
}
/* irrelevant styles */
.container {
counter-reset: num;
}
.item {
height: 150px;
background-color: blue;
color: #fff;
}
.item::before {
color: #fff;
content: attr(class) " " counter(num);
counter-increment: num;
}
<div class="wrapper">
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
You could use flex and flex-wrap for this with break points so you can decide how many items are in the top row on each width. Where it is inevitable that there is a single item on the last row this snippet goes for minimising the number of rows.
It would have been nice to use CSS variables for all of this so the choice is easily altered, but you can't use variables in media queries so we have to put them in by hand.
.container {
width: 104px; /* item width plus 2 lots of padding */
margin: 0;
padding: 0;
justify-content: center;
display: flex;
flex-wrap: wrap;
}
#media (min-width: 312px) {
.container {
width: 312px;
}
}
#media (min-width: 416px) {
.container {
width: 416px;
}
}
#media (min-width: 728px) {
.container {
width: 728px;
}
}
.item {
r-sizing: border-box;
width: 100px;
background-color: cyan;
text-align: center;
flex: 0 0 100px; /* made 100px so easier to test on different widthe */
margin: 2px;
}
<div class="container">
<div class="item">
Item 1
</div>
<div class="item">
Item 2
</div>
<div class="item">
Item 3
</div>
<div class="item">
Item 4
</div>
<div class="item">
Item 5
</div>
<div class="item">
Item 6
</div>
<div class="item">
Item 7
</div>
</div>
Related
I'm building a horizontal list of items:
Once scrolled to the end of the list, the second last item needs to be in center, and the last item takes all of the remaining space after it.
I've had a success in making the list of items, but not sure what's the best way to implement the desired behaviour of having second last item centered and last item taking remaining space. Here is my current code:
I would appreciate any help or advice for guiding me in the right direction. Thank you!
/* layout */
.container {
display: flex;
overflow-x: auto;
gap: 21px;
}
.item {
min-width: 212px;
height: 254px;
}
/* visuals */
.container {
padding-left: 11px;
}
.item {
background: #D9D9D9;
}
.item.green {
background: #099F9F;
}
.item.red {
background: #FF6262;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item green"></div>
<div class="item red"></div>
</div>
You can define the width as calc((100% - item_wdith - 2*gap)/2)
.container {
display: flex;
overflow-x: auto;
padding-left: 10px;
gap: 20px;
}
.item {
width: 220px;
flex-shrink: 0;
height: 254px;
background: #D9D9D9;
}
.item.green {
background: #099F9F;
}
.item.red {
background: #FF6262;
width: calc((100% - 220px - 2*20px)/2);
}
/* to illustrate the center */
html {
background: linear-gradient(red 0 0) 50%/2px 100% no-repeat;
min-height: 100%;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item green"></div>
<div class="item red"></div>
</div>
I have a problem with flex-wrap property of a flexbox.
Here is my code:
.container {
display: flex;
flex-wrap: wrap;
}
.item {
width: 50%;
margin: 0 10px;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
</div>
What I want to do is to let some space margin between items but without wrapping them. I want them horizontally two by two and every item with width of 50% margin included to prevent flex-wrap.
.container {
display: flex;
flex-wrap: wrap;
}
.item {
flex: 1 0 34%; /* fg, fs, fb */
margin: 10px;
height: 50px;
background-color: gray;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
With flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis (width) to be 50%, which results in one item per row due to the margins.
Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap.
In this case, with flex-basis: 34%, there's plenty of space for the margins, but not enough space for a third item on each line.
You can use calc for this, so for example you can set flex-basis: calc(50% - margin x 2).
.container {
display: flex;
flex-wrap: wrap;
}
.item {
flex-basis: calc(50% - 20px);
margin: 10px;
background: lightblue;
height: 50px;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
You may use calc to exclude margin from the width:
.container {
display: flex;
flex-wrap: wrap;
}
.item {
width: calc(50% - 20px);
margin: 5px 10px;
height:20px;
background:red;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
I need to arrange following elements as This sample Image with CSS Flexbox.
.wrapper {
width: 60%;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.element {
width: 200px;
height: 200px;
background: green;
margin-bottom: 20px;
}
<div class="wrapper">
<div class="element large"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element"></div>
<div class="element large"></div>
</div>
I have tried few styles but couldn't get successful. Is there any way to fix this with css flex?
In case you change your mind, the Grid solution:
.grid {
width: 60%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(4, 100px); /* "width" */
grid-template-rows: repeat(4, 100px); /* "height" */
grid-gap: 10px; /* gap between items */
justify-content: center; /* horizontally centered */
}
.item {
background: green;
}
.larger {
grid-column: 1 / span 2; /* starts at the 1st column & spans two */
grid-row: 1 / span 2; /* starts at the 1st row & spans two */
/* can also omit the 1's since it's the first child */
}
.larger2 {
grid-column: 3 / span 2;
grid-row: 3 / span 2;
}
<div class="grid">
<div class="item larger"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item larger2"></div>
</div>
This question already has answers here:
How to specify line breaks in a multi-line flexbox layout?
(9 answers)
Closed 5 years ago.
There is a flexbox grid.
.flex {
display: flex;
flex-wrap: wrap;
border: 2px solid red;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
border: 2px solid blue;
}
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item new-string"></div>
</div>
How to transfer .new-string to a new line, along with the elements that follow it?
If you look at this great answer you'll notice that the only cross-browser way (without 2 line break limit) is inserting 100%-width empty blocks ("line-breaks"). So for similar markup this will look like
.flex {
display: flex;
flex-wrap: wrap;
border: 2px solid red;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
border: 2px solid blue;
}
.line-break {
width: 100%;
}
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="line-break"></div>
<div class="item"></div>
<div class="item"></div>
<div class="line-break"></div>
<div class="item"></div>
<div class="line-break"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
If you want to preserve your markup style, you'll have to insert this line-break blocks via JavaScript:
var items = document.querySelectorAll(".flex > .item.new-string");
for (var i = 0; i < items.length; i++) {
var lineBreak = document.createElement('div');
lineBreak.className = "line-break";
items[i].parentNode.insertBefore(lineBreak, items[i]);
}
.flex {
display: flex;
flex-wrap: wrap;
border: 2px solid red;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
border: 2px solid blue;
}
.line-break {
width: 100%;
}
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item new-string"></div>
<div class="item"></div>
<div class="item new-string"></div>
<div class="item new-string"></div>
<div class="item"></div>
<div class="item"></div>
</div>
This seems to be possible to do with Grid-layout.
First to position items inline you can use
grid-template-columns: repeat(auto-fill, 50px);
so that each item takes 50px and it will position items in one line until no more items can fit in one line. And then you can use grid-column-start: 1; on specific item so that it goes to new line.
* {
box-sizing: border-box;
}
.flex {
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(auto-fill, 50px);
border: 2px solid red;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
border: 2px solid blue;
}
.new-string {
grid-column-start: 1;
background: red;
}
<div class="flex"><div class="item"></div><div class="item"></div><div class="item"></div><div class="item new-string"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item new-string"></div><div class="item"></div><div class="item"></div><div class="item new-string"></div><div class="item"></div></div>
As an alternative you could simply do this
HTML
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item line-break"></div>
<div class="item new-string"></div>
</div>
CSS
.line-break {
width: 100%;
}
The 100% width flex item will give you the line break.
Easiest way to get a new line in the flex grid, sure you need an extra div but I don't find it that bad of a way to do it
All flex items are set by default to order: 0. This means they will be laid out in the order they appear in the source code.
If you give the last item order: 1, this forces it to be last when additional items are added.
The ::before and ::after pseudo elements on a flex container create new flex items.
So if we add one pseudo element with a large enough width, it will force your last item (set by order) to the next row.
.flex {
display: flex;
flex-wrap: wrap;
border: 2px solid red;
}
.item {
width: 50px;
height: 50px;
margin: 5px;
border: 2px solid blue;
}
.new-string {
order: 1;
}
.flex::after {
content: "";
flex: 0 0 100%;
height: 0;
}
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item new-string"></div>
</div>
I have the following requirement.
The green colored parent width will be varying depending on device width. I need all the boxes to be in the center of the parent.
I have tried the following things already, but it didnt help me.
Trial 1
Parent {text-align:center} box {display:inline-block}.
This resulted in following output
Trial 2
Parent {text-align:center} box{float:left}.
This resulted in following output
Trial 3
Parent {display:flex} box -> justify-around & justify-between also didn't work.
.parent {
text-align: center;
}
.item {
display: inline-block;
width: 100px;
height: 100px;
background: red;
}
<div class="parent">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Any help on this will be appreciated.
Without Javascript this very hard using floats &/or inline-block.
Flexbox offers some hope but even then some creativity is required.
Essentially, provided the maximum number of elements "per row" is known you can create a required number of invisible elements which can be ustilised in conjunction with justify-content:center to acheieve the last line appearance you require by essentially pushing the last line content back over to the left.
Codepen Demo
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.parent {
text-align: center;
border: 1px solid grey;
width: 80%;
max-width: 800px;
margin: 1em auto;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.item {
width: 100px;
height: 100px;
margin: 10px;
background: red;
}
.balancer {
height: 0;
width: 100px;
margin: 0 10px;
visibility: hidden;
}
<div class="parent">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="balancer"></div>
<div class="balancer"></div>
<div class="balancer"></div>
<div class="balancer"></div>
</div>
Got it working by using jQuery and adding a #wrapper.
All you've got to do is calculate how many items will fit on one row. Then you set the wrapper to the exact width that is needed to fit these items.
I hoped it could be done in pure CSS, but as far as I know there is no Math.floor() equivalent for CSS.
Example:
function fitItemsOnRow() {
var windowWidth = $(window).width();
var itemWidth = $(".item").outerWidth(true);
var itemAmount = Math.floor((windowWidth / itemWidth));
if(itemAmount > $(".item").length) {
/* Set the maximum amount of items */
itemAmount = $(".item").length;
}
var rowWidth = itemWidth * itemAmount;
$("#wrapper").width(rowWidth);
}
$(window).resize(function() {
/* Responsive */
fitItemsOnRow();
});
$(document).ready(function() {
fitItemsOnRow();
});
body {
margin: 0px;
}
#parent {
background: #75DB3C;
min-width: 100vw;
min-height: 100vh;
text-align: center;
}
#wrapper {
display: inline-block;
text-align: left;
font-size: 0px; /* Removes default margin */
}
.item {
display: inline-block;
margin: 12px;
width: 100px;
height: 100px;
background: #0B56A9;
}
<div id="parent">
<div id="wrapper">
<!-- A wrapper is necessary to center the items -->
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
You can do this with css selector "nth-of-type(n)"
<div class="parent">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
and css here
.parent
{
display: block;
width: 980px;
padding: 10px 50px;
background: green;
box-sizing: border-box;
}
.parent::after
{
content: '';
display: block;
clear: both;
}
.item
{
float: left;
width: 24%;
margin-right: 1.25%;
margin-bottom: 1.25%;
/*
note
you may need min height , height or overflow:hidden
*/
}
.item:nth-of-type(4n)
{
float: right;
margin-right: 0;
}