A two-column card-based layout for desktop and mobile using flexbox - html

I want to make a two column html structure like the following picture:
The numbers inside each box means the order of the div's in the html markup.
The challenge is that I want to have a different sort order on a mobile device. For example, on a mobile device, number 4 should be below number 1 without changing the html. I think using flexbox should be the best way to achieve this. But I couldn't make it work.
Using floats isn't the way, because at the first side when there is some space, the next div will automatic float to that side.
I also cannot make extra wrapper's for each column, because using order will not work then.
Does somebody has an idea?
* {
box-sizing: border-box;
}
.row {
display: inline-block;
width: 100%;
}
.item {
border: 1px solid #000;
width: 50%;
float: left;
}
<div class="row">
<div style="height:200px;" class="item">1</div>
<div style="height:150px;" class="item">2</div>
<div style="height:100px;" class="item">3</div>
<div style="height:50px; float:right;" class="item">4</div>
<div style="height:75px; float:right;" class="item">5</div>
<div style="height:150px; float:right;" class="item">6</div>
</div>

The code below features:
A column-direction flex container
Flex items wrap at viewport height
On smaller screens (based on height or width), the fourth item moves to the second position in the visual order, and the items align in a single full-width column.
* {
box-sizing: border-box;
}
.row {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 100vh;
max-width: 100vw;
}
.item {
width: 45%;
margin: 5px;
border: 1px solid #000;
}
#media ( max-width: 500px ), ( max-height: 500px ) {
.row { flex-direction: row; }
.item { flex-basis: 100%; }
.item:nth-child(1) { order: -2; }
.item:nth-child(4) { order: -1; }
}
<div class="row">
<div style="height:200px;" class="item">1</div>
<div style="height:150px;" class="item">2</div>
<div style="height:100px;" class="item">3</div>
<div style="height:50px;" class="item">4</div>
<div style="height:75px;" class="item">5</div>
<div style="height:150px;" class="item">6</div>
</div>
jsFiddle

Related

How can I distance these 2 divs in a flex column? [duplicate]

To set the minimal distance between flexbox items I'm using margin: 0 5px on .item and margin: 0 -5px on container. For me it seems like a hack, but I can't find any better way to do this.
#box {
display: flex;
width: 100px;
margin: 0 -5px;
}
.item {
background: gray;
width: 50px;
height: 50px;
margin: 0 5px;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
CSS gap property:
There is a new gap CSS property for multi-column, flexbox, and grid layouts that works in newer browsers now! (See Can I use link 1; link 2). It is shorthand for row-gap and column-gap.
#box {
display: flex;
gap: 10px;
}
CSS row-gap property:
The row-gap CSS property for both flexbox and grid layouts allows you to create a gap between rows.
#box {
display: flex;
row-gap: 10px;
}
CSS column-gap property:
The column-gap CSS property for multi-column, flexbox and grid layouts allows you to create a gap between columns.
#box {
display: flex;
column-gap: 10px;
}
Example:
#box {
display: flex;
flex-wrap: wrap;
width: 200px;
background-color: red;
gap: 10px;
}
.item {
background: gray;
width: 50px;
height: 50px;
border: 1px black solid;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
Flexbox doesn't have collapsing margins.
Flexbox doesn't have anything akin to border-spacing for tables (edit: CSS property gap fulfills this role in newer browsers, Can I use)
Therefore achieving what you are asking for is a bit more difficult.
In my experience, the "cleanest" way that doesn't use :first-child/:last-child and works without any modification on flex-wrap:wrap is to set padding:5px on the container and margin:5px on the children. That will produce a 10px gap between each child and between each child and their parent.
Demo
.upper {
margin: 30px;
display: flex;
flex-direction: row;
width: 300px;
height: 80px;
border: 1px red solid;
padding: 5px; /* this */
}
.upper > div {
flex: 1 1 auto;
border: 1px red solid;
text-align: center;
margin: 5px; /* and that, will result in a 10px gap */
}
.upper.mc /* multicol test */ {
flex-direction: column;
flex-wrap: wrap;
width: 200px;
height: 200px;
}
<div class="upper">
<div>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa<br/>aaa</div>
<div>aaa<br/>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa</div>
</div>
<div class="upper mc">
<div>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa<br/>aaa</div>
<div>aaa<br/>aaa<br/>aaa</div>
<div>aaa</div>
<div>aaa</div>
</div>
This is not a hack.
The same technique is also used by bootstrap and its grid, though, instead of margin, bootstrap uses padding for its cols.
.row {
margin:0 -15px;
}
.col-xx-xx {
padding:0 15px;
}
Flexbox and css calc with multiple rows support
Hello, below is my working solution for all browsers supporting flexbox. No negative margins.
Fiddle Demo
.flexbox {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.flexbox > div {
/*
1/3 - 3 columns per row
10px - spacing between columns
*/
box-sizing: border-box;
margin: 10px 10px 0 0;
outline: 1px dotted red;
width: calc(1/3*100% - (1 - 1/3)*10px);
}
/*
align last row columns to the left
3n - 3 columns per row
*/
.flexbox > div:nth-child(3n) {
margin-right: 0;
}
.flexbox::after {
content: '';
flex: auto;
}
/*
remove top margin from first row
-n+3 - 3 columns per row
*/
.flexbox > div:nth-child(-n+3) {
margin-top: 0;
}
<div class="flexbox">
<div>col</div>
<div>col</div>
<div>col</div>
<div>col</div>
<div>col</div>
</div>
Take a note this code can be shorter using SASS
Update 2020.II.11
Aligned columns on the last row to the left
Update 2020.II.14
Removed margin-bottom in the last row
You can use & > * + * as a selector to emulate a flex-gap (for a single line):
#box { display: flex; width: 230px; outline: 1px solid blue; }
.item { background: gray; width: 50px; height: 100px; }
/* ----- Flexbox gap: ----- */
#box > * + * {
margin-left: 10px;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
If you need to support flex wrapping, you can use a wrapper element:
.flex { display: flex; flex-wrap: wrap; }
.box { background: gray; height: 100px; min-width: 100px; flex: auto; }
.flex-wrapper {outline: 1px solid red; }
/* ----- Flex gap 10px: ----- */
.flex > * {
margin: 5px;
}
.flex {
margin: -5px;
}
.flex-wrapper {
width: 400px; /* optional */
overflow: hidden; /* optional */
}
<div class='flex-wrapper'>
<div class='flex'>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
</div>
</div>
You can use transparent borders.
I have contemplated this issue while trying to build a flex grid model which can fallback to a tables + table-cell model for older browsers. And Borders for column gutters seemed to me the best appropriate choice. i.e. Table-cells don't have margins.
e.g.
.column{
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 10px solid transparent;
}
Also note that you need min-width: 50px; for flexbox. The flex model will not handle fixed sizes unless you do flex: none; on the particular child element you want as fixed and therefore excluded from being "flexi". http://jsfiddle.net/GLpUp/4/
But all columns together with flex:none; is no longer a flex model.
Here is something closer to a flex model: http://jsfiddle.net/GLpUp/5/
So you can actually use margins normally if you don't need the table-cell fallback for older browsers. http://jsfiddle.net/GLpUp/3/
Setting background-clip: padding-box; will be necessary when using a background, as otherwise the background will flow into the transparent border area.
This solution will work for all cases even if there are multiple rows or any number of elements. But the count of the section should be same you want 4 in first row and 3 is second row it won't work that way the space for the 4th content will be blank the container won't fill.
We are using display: grid; and its properties.
#box {
display: grid;
width: 100px;
grid-gap: 5px;
/* Space between items */
grid-template-columns: repeat(4,1fr);
/* Decide the number of columns(4) and size(1fr | 1 Fraction | you can use pixels and other values also) */
}
.item {
background: gray;
width: 100%;
/* width is not necessary only added this to understand that width works as 100% to the grid template allocated space **DEFAULT WIDTH WILL BE 100%** */
height: 50px;
}
<div id='box'>
<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>
The Downside of this method is in Mobile Opera Mini will not be supported and in PC this works only after IE10.
Note for complete browser compatability including IE11 please use Autoprefixer
OLD ANSWER
Don't think of it as an old solution, it's still one of the best if you only want single row of elements and it will work with all the browsers.
This method is used by CSS sibling combination, so you can manipulate it many other ways also, but if your combination is wrong it may cause issues also.
.item+.item{
margin-left: 5px;
}
The below code will do the trick. In this method, there is no need to give margin: 0 -5px; to the #box wrapper.
A working sample for you:
#box {
display: flex;
width: 100px;
}
.item {
background: gray;
width: 22px;
height: 50px;
}
.item+.item{
margin-left: 5px;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
Let's say if you want to set 10px space between the items, you can just set .item {margin-right:10px;} for all, and reset it on the last one .item:last-child {margin-right:0;}
You can also use general sibling ~ or next + sibling selector to set left margin on the items excluding the first one .item ~ .item {margin-left:10px;} or use .item:not(:last-child) {margin-right: 10px;}
Flexbox is so clever that it automatically recalculates and equally distributes the grid.
body {
margin: 0;
}
.container {
display: flex;
}
.item {
flex: 1;
background: gray;
height: 50px;
}
.item:not(:last-child) {
margin-right: 10px;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
If you want to allow flex wrap, see the following example.
body {
margin: 0;
}
.container {
display: flex;
flex-wrap: wrap;
margin-left: -10px;
}
.item {
flex: 0 0 calc(50% - 10px);
background: gray;
height: 50px;
margin: 0 0 10px 10px;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Update: gap for flexbox is now supported in all modern browsers (Edge/Chrome/Opera/Samsung Internet/Safari/Firefox)
Eventually they will add the gap property to flexbox. Until then you could use CSS grid instead which already has the gap property, and just have a single row. Nicer than dealing with margins.
I have found a solution that is based on the general sibling selector, ~, and allows infinite nesting.
See this code pen for a working example
Basically, inside of column containers, every child that is preceded by another child gets a top margin. Likewise, inside every row container, every child that is preceded by another gets a left margin.
.box {
display: flex;
flex-grow: 1;
flex-shrink: 1;
}
.box.columns {
flex-direction: row;
}
.box.columns>.box~.box {
margin-left: 5px;
}
.box.rows {
flex-direction: column;
}
.box.rows>.box~.box {
margin-top: 5px;
}
<div class="box columns">
<div class="box" style="background-color: red;"></div>
<div class="box rows">
<div class="box rows">
<div class="box" style="background-color: blue;"></div>
<div class="box" style="background-color: orange;"></div>
<div class="box columns">
<div class="box" style="background-color: yellow;"></div>
<div class="box" style="background-color: pink;"></div>
</div>
</div>
<div class="box" style="background-color: green;"></div>
</div>
</div>
According to #ChromeDevSummit there's an implementation of the gap property for Flexbox in Firefox and Chromium-based browsers.
Here's a Live Demo
Moving on from sawa's answer, here's a slightly improved version that allows you to set a fixed spacing between the items without the surrounding margin.
http://jsfiddle.net/chris00/s52wmgtq/49/
Also included is the Safari "-webkit-flex" version.
.outer1 {
background-color: orange;
padding: 10px;
}
.outer0 {
background-color: green;
overflow: hidden;
}
.container
{
display: flex;
display: -webkit-flex;
flex-wrap: wrap;
-webkit-flex-wrap: wrap;
background-color: rgba(0, 0, 255, 0.5);
margin-left: -10px;
margin-top: -10px;
}
.item
{
flex-grow: 1;
-webkit-flex-grow: 1;
background-color: rgba(255, 0, 0, 0.5);
width: 100px;
padding: 10px;
margin-left: 10px;
margin-top: 10px;
text-align: center;
color: white;
}
<div class="outer1">
<div class="outer0">
<div class="container">
<div class="item">text</div>
<div class="item">text</div>
<div class="item">text</div>
<div class="item">text</div>
<div class="item">text</div>
<div class="item">text</div>
</div>
</div>
</div>
I have used this for wrapped and fixed width columns. The key here is calc()
SCSS sample
$gap: 10px;
dl {
display: flex;
flex-wrap: wrap;
padding: $gap/2;
dt, dd {
margin: $gap/2;}
dt { // full width, acts as header
flex: 0 0 calc(100% - #{$gap});}
dd { // default grid: four columns
flex: 0 0 calc(25% - #{$gap});}
.half { // hall width columns
flex: 0 0 calc(50% - #{$gap});}
}
Full Codepen sample
A flex container with -x (negative) margin and flex items with x (positive) margin or padding both lead to the desired visual result: Flex items have a fixed gap of 2x only between each other.
It appears to be simply a matter of preference, whether to use margin or padding on the flex items.
In this example, the flex items are scaled dynamically in order to preserve the fixed gap:
.flex-container {
margin: 0 -5px;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.flex-item {
margin: 0 5px; // Alternatively: padding: 0 5px;
flex: 1 0 auto;
}
Using Flexbox in my solution I've used the justify-content property for the parent element (container) and I've specified the margins inside the flex-basis property of the items.
Check the code snippet below:
.container {
display: flex;
flex-flow: row wrap;
justify-content: space-around;
margin-bottom: 10px;
}
.item {
height: 50px;
display: flex;
justify-content: center;
align-items: center;
background-color: #999;
}
.item-1-4 {
flex-basis: calc(25% - 10px);
}
.item-1-3 {
flex-basis: calc(33.33333% - 10px);
}
.item-1-2 {
flex-basis: calc(50% - 10px);
}
<div class="container">
<div class="item item-1-4">1</div>
<div class="item item-1-4">2</div>
<div class="item item-1-4">3</div>
<div class="item item-1-4">4</div>
</div>
<div class="container">
<div class="item item-1-3">1</div>
<div class="item item-1-3">2</div>
<div class="item item-1-3">3</div>
</div>
<div class="container">
<div class="item item-1-2">1</div>
<div class="item item-1-2">2</div>
</div>
With flexbox, creating gutters is a pain, especially when wrapping is involved.
You need to use negative margins (as shown in the question):
#box {
display: flex;
width: 100px;
margin: 0 -5px;
}
... or alter the HTML (as shown in another answer):
<div class='flex-wrapper'>
<div class='flex'>
<div class='box'></div>
<div class='box'></div>
...
</div>
</div>
... or something else.
In any case, you need an ugly hack to make it work because flexbox doesn't provide a "flex-gap" feature
(at least for now).
The issue of gutters, however, is simple and easy with CSS Grid Layout.
The Grid spec provides properties that create space between grid items, while ignoring the space between items and the container. These properties are:
grid-column-gap
grid-row-gap
grid-gap (the shorthand for both properties above)
Recently, the spec has been updated to conform with the CSS Box Alignment Module, which provides a set of alignment properties for use across all box models. So the properties are now:
column-gap
row-gap
gap (shorthand)
However, not all Grid-supporting browsers support the newer properties, so I'll use the original versions in the demo below.
Also, if spacing is needed between items and the container, padding on the container works just fine (see the third example in the demo below).
From the spec:
10.1. Gutters: the row-gap, column-gap, and gap
properties
The row-gap and column-gap properties (and their gap shorthand),
when specified on a grid container, define the gutters between grid
rows and grid columns. Their syntax is defined in CSS Box Alignment 3
ยง8 Gaps Between Boxes.
The effect of these properties is as though the affected grid lines
acquired thickness: the grid track between two grid lines is the space
between the gutters that represent them.
.box {
display: inline-grid;
grid-auto-rows: 50px;
grid-template-columns: repeat(4, 50px);
border: 1px solid black;
}
.one {
grid-column-gap: 5px;
}
.two {
grid-column-gap: 10px;
grid-row-gap: 10px;
}
.three {
grid-gap: 10px;
padding: 10px;
}
.item {
background: lightgray;
}
<div class='box one'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
<hr>
<div class='box two'>
<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>
<hr>
<div class='box three'>
<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>
More information:
Browser Support for CSS Grid
Make it easier to define margins that only apply between flex-items (discussion)
Spacing between flexbox items
Why not do it like this:
.item + .item {
margin-left: 5px;
}
This uses the adjacent sibling selector, to give all .item elements, except the first one a margin-left. Thanks to flexbox, this even results in equally wide elements. This could also be done with vertically positioned elements and margin-top, of course.
Here's my solution, that doesn't require setting any classes on the child elements:
.flex-inline-row {
display: inline-flex;
flex-direction: row;
}
.flex-inline-row.flex-spacing-4px > :not(:last-child) {
margin-right: 4px;
}
Usage:
<div class="flex-inline-row flex-spacing-4px">
<span>Testing</span>
<span>123</span>
</div>
The same technique can be used for normal flex rows and columns in addition to the inline example given above, and extended with classes for spacing other than 4px.
I often use the + operator for such cases
#box {
display: flex;
width: 100px;
}
.item {
background: gray;
width: 50px;
height: 50px;
}
.item + .item {
margin-left: 5px;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
You could use the new property gap. I copy paste the explanation I found in this article, as well as more information
CSS grid layout has had gap (previously grid-gap) for some time. By specifying the internal spacing of a containing element rather than the spacing around child elements, gap solves many common layout issues. For example, with gap, you don't have to worry about margins on child elements causing unwanted whitespace around the edges of a containing element:
Unfortunately right now, only FireFox supports gap in flex layouts.
#use postcss-preset-env {
stage: 0;
browsers: last 2 versions
}
section {
width: 30vw;
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(12ch, 1fr));
&[flex] {
display: flex;
flex-wrap: wrap;
}
margin-bottom: 3rem;
}
.tag {
color: white;
background: hsl(265 100% 47%);
padding: .5rem 1rem;
border-radius: 1rem;
}
button {
display: inline-flex;
place-items: center;
gap: .5rem;
background: hsl(265 100% 47%);
border: 1px solid hsl(265 100% 67%);
color: white;
padding: 1rem 2rem;
border-radius: 1rem;
font-size: 1.25rem;
}
body {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
<section>
<h1>Grid</h1>
<div class="tag">Awesome</div>
<div class="tag">Coo</div>
<div class="tag">Rad</div>
<div class="tag">Math</div>
</section>
<br>
<section flex>
<h1>Flex</h1>
<div class="tag">Awesome</div>
<div class="tag">Coo</div>
<div class="tag">Rad</div>
<div class="tag">Math</div>
</section>
I find the easiest way of doing this is with percentages and just allowing the margin to tally up your width
This means you end up with something like this if you where using your example
#box {
display: flex;
}
.item {
flex: 1 1 23%;
margin: 0 1%;
}
Does mean your values are based on the width though which might not be good for everybody.
Here's a grid of card UI elements with spacing completed using flexible box:
I was frustrated with manually spacing the cards by manipulating padding and margins with iffy results. So here's the combinations of CSS attributes I've found very effective:
.card-container {
width: 100%;
height: 900px;
overflow-y: scroll;
max-width: inherit;
background-color: #ffffff;
/*Here's the relevant flexbox stuff*/
display: flex;
flex-direction: row;
justify-content: center;
align-items: flex-start;
flex-wrap: wrap;
}
/*Supplementary styles for .card element*/
.card {
width: 120px;
height: 120px;
background-color: #ffeb3b;
border-radius: 3px;
margin: 20px 10px 20px 10px;
}
<section class="card-container">
<div class="card">
</div>
<div class="card">
</div>
<div class="card">
</div>
<div class="card">
</div>
</section>
Hope this helps folks, present and future.
Columnify - A solo class for N columns
Flexbox and SCSS
.columnify {
display: flex;
> * {
flex: 1;
&:not(:first-child) {
margin-left: 2rem;
}
}
}
Flexbox and CSS
.columnify {
display: flex;
}
.columnify > * {
flex: 1;
}
.columnify > *:not(:first-child) {
margin-left: 2rem;
}
<div class="columnify">
<div style="display: inline-block; height: 20px; background-color: blue;"></div>
<div style="display: inline-block; height: 20px; background-color: blue"></div>
<div style="display: inline-block; height: 20px; background-color: blue"></div>
</div>
Play with it on JSFiddle.
#box {
display: flex;
width: 100px;
}
.item {
background: gray;
width: 50px;
height: 50px;
}
/* u mean utility */
.u-gap-10 > *:not(:last-child) {
margin-right: 10px;
}
<div id='box' class="u-gap-10">
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
Just use .item + .item in selector to match from second .item
#box {
display: inline-flex;
margin: 0 -5px;
}
.item {
background: gray;
width: 10px;
height: 50px;
}
#box .item + .item {
margin-left: 10px;
}
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
</div>
I found a hack because i really need this my self.
/* grid */
.container {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.container::after, /* this makes sure odd element goes left and not space between */
.item {
content:"";
width: calc(33.3333% - 20px);
margin-bottom: 40px;
}
/* extra styling - not important */
.item {
height: 100px;
background: #787878;
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
Here's a post grid with nice flex grow categories also.
I think you'd like it.
See Codepen
Assuming:
You want 4 column grid layout with wrapping
The number of items is not necessarily a multiple of 4
Set a left margin on every item except 1st, 5th, 9th item and so on; and set fixed width on each item. If the left margin is 10px then each row will have 30px margin between 4 items, the percentage width of item can be calculated as follows:
100% / 4 - horizontal-border - horizontal-padding - left-margin * (4 - 1) / 4
This is a decent workaround for issues involving last row of flexbox.
.flex {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 1em 0;
background-color: peachpuff;
}
.item {
margin-left: 10px;
border: 1px solid;
padding: 10px;
width: calc(100% / 4 - 2px - 20px - 10px * (4 - 1) / 4);
background-color: papayawhip;
}
.item:nth-child(4n + 1) {
margin-left: 0;
}
.item:nth-child(n + 5) {
margin-top: 10px;
}
<div class="flex">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
</div>
<div class="flex">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
<div class="flex">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
There is indeed a nice, tidy, CSS-only way to do this (that one may consider "better").
Of all the answers posted here, I only found one that uses calc() successfully (by Dariusz Sikorski). But when posed with: "but it fails if there are only 2 items in the last row" there was no solution expanded.
This solution addresses the OP's question with an alternative to negative margins and addresses the problem posed to Dariusz.
notes:
This example only demonstrates a 3-column layout
It uses calc() to let the browser do math the way it wants --
100%/3 (although 33.3333% should work just as well), and
(1em/3)*2 (although .66em should also work well).
It uses ::after to pad the last row if there are fewer elements than columns
.flex-container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.flex-container:after {
content: "";
}
.flex-container > div,
.flex-container:after {
box-sizing: border-box;
width: calc((100%/3) - ((1em/3)*2));
}
.flex-container > :nth-child(n + 4) {
margin-top: 1em;
}
/* the following is just to visualize the items */
.flex-container > div,
.flex-container:after {
font-size: 2em;
}
.flex-container {
margin-bottom:4em;
}
.flex-container > div {
text-align: center;
background-color: #aaa;
padding: 1em;
}
.flex-container:after {
border: 1px dashed red;
}
<h2>Example 1 (2 elements)</h2>
<div class="flex-container">
<div>1</div>
<div>2</div>
</div>
<h2>Example 2 (3 elements)</h2>
<div class="flex-container">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
Also at https://codepen.io/anon/pen/rqWagE
You could use the following equation
.container {
max-width: 960px;
margin: 0 auto;
padding: 4rem 0;
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
.flex:after {
content: "";
max-width: calc(100% * var(--col) / 12 - var(--gap));
width: 100%;
}
#media (max-width: 960px) {
.flex:after {
max-width: calc(100% * var(--colTablet) / 12 - var(--gap));
}
}
#media (max-width: 680px) {
.flex:after {
max-width: calc(100% * var(--colMobile) / 12 - var(--gap));
}
}
.flex .item {
max-width: calc(100% * var(--col) / 12 - var(--gap));
width: 100%;
}
#media (max-width: 960px) {
.flex .item {
max-width: calc(100% * var(--colTablet) / 12 - var(--gap));
margin-bottom: 1rem;
}
.flex .item:last-child {
margin-bottom: unset;
}
}
#media (max-width: 680px) {
.flex .item {
max-width: calc(100% * var(--colMobile) / 12);
}
}
.flex .item .card {
background: #eee;
text-align: center;
padding: 2rem;
}
<div class="flex container" style="--col: 3; --colTablet: 6; --colMobile: 12; --gap: 2%">
<div class="item" style="--col: 3; --colTablet: 6; --colMobile: 12; --gap: 2%">
<div class="card">
<h2>Hello world</h2>
</div>
</div>
<div class="item" style="--col: 3; --colTablet: 6; --colMobile: 12; --gap: 2%">
<div class="card">
<h2>Hello world</h2>
</div>
</div>
<div class="item" style="--col: 3; --colTablet: 6; --colMobile: 12; --gap: 2%">
<div class="card">
<h2>Hello world</h2>
</div>
</div>
</div>
I came across the same issue earlier, then stumbled upon the answer for this. Hope it will help others for future reference.
long answer short, add a border to your child flex-items.
then you can specify margins between flex-items to whatever you like.
In the snippet, i use black for illustration purposes, you can use 'transparent' if you like.
#box {
display: flex;
width: 100px;
/* margin: 0 -5px; *remove this*/
}
.item {
background: gray;
width: 50px;
height: 50px;
/* margin: 0 5px; *remove this*/
border: 1px solid black; /* add this */
}
.item.special{ margin: 0 10px; }
<div id='box'>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item'></div>
<div class='item special'></div>
</div>

Why is that that no matter how many items are in this overflowX'd div, it will only horizontally scroll to the last item but ignore the right padding?

Here is a codepen.
Here is code:
HTML
<div class="body">
<div class="page">
<div class="regular-flow">
</div>
<div class="regular-flow">
</div>
<div class="regular-flow">
</div>
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
<div class="item">11</div>
<div class="item">12</div>
<div class="item">13</div>
<div class="item">14</div>
<div class="item">15</div>
<div class="item">16</div>
</div>
<div class="regular-flow">
</div>
<div class="regular-flow">
</div>
</div>
</div>
CSS
.body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: grey;
}
.page {
height: 100vh;
width: 100%;
background-color: lightgrey;
overflow: hidden;
}
.regular-flow {
height: 20px;
background-color: limegreen;
margin: 10px 24px;
}
.container {
width: 100%;
height: 25%;
padding-left: 24px;
padding-right: 24px;
background-color: orangered;
display: flex;
gap: 16px;
overflow-x: scroll;
-ms-overflow-style: none;
scrollbar-width: none;
}
.container::-webkit-scrollbar {
display: none;
}
.item {
height: 100%;
width: 121px;
color: yellow;
background-color: blue;
flex-shrink: 0;
}
We have several "regular flow" items (green) that keep their own margin consistently down the page (grey). However, I'd like to have a horizontal scrolling div (blue and orangered) that disrespects these boundaries, allowing items to seemingly pass from "edge" to "edge" of the screen. My thought was to overflowX: 'scroll' a 100% width div and put padding on the left and right equal to the margin being set by the other items. This way, on the "edges" of the scrollable content, it appears to still respect the margin set by the other regularly flowing items. However, no matter what I try or how many items you put into this container, it will always scroll right only to the right edge of the last item. It will not show the last bit of right padding.
Things to keep in mind about the codepen
You have to have enough items for it to need to scroll horizontally, obviously.
I am hiding the scrollbar, so hover over the blue/orange container and shift+scrollwheel to move the items. I know this isn't great UX.
Most Basic Question
Why can you not scroll right far enough such that the padding-right (orangered) is visible?
Update
I was able to come up with a workaround for this issue, and that is to go into the parent component (in my codepen, this would be .container and remove the padding-right and padding-left at that level, but then add:
.container & .div:first-child {
padding-left: 24,
}
.container & .div:last-child {
padding-right: 24,
}
This seems to accomplish the same goals, but only if you do NOT have any background color to the .item. For my purposes, this works. I only added a background color here for visualizing the issue. I would still be curious if anyone can tell me what the problem above was, so I will keep this question up and not edit the codepen. Thanks!

Adjust flex items in same column

I have a horizontal slider(scrollable) on my page, which contains list of sub-cards. The below code snippet works perfectly fine.
JSFiddle-1: https://jsfiddle.net/28rnwpqk/
I now want to display different sized cards, which are dynamic. I made following change to my container css, so as to stack multiple cards in same column if space exists.
.container {
...
flex-direction: column;
flex-wrap: wrap;
...
}
JSFiddle-2: https://jsfiddle.net/k60zLg74/
This results in the parent container width being set only to width of first slider item, bcz of flex-direction: column property. As such the background color, slider scroll has stopped working. What is the correct way to achieve this without impacting width of container. My main requirement is to have flex items stacked vertically if space exists and size of cards is not know upfront.
CSS grid can do this better:
.viewport {
width: 600px;
height: 200px;
overflow-x: hidden;
}
.container {
display: inline-grid;
background-color: DodgerBlue;
grid-auto-flow:column;
grid-template-rows:1fr 1fr;
grid-auto-columns:130px;
gap: 10px;
height: 100%;
min-width:100%;
}
.item {
box-sizing: border-box;
background-color: #f1f1f1;
padding: 20px;
margin: 10px;
font-size: 30px;
grid-row:span 2;
}
.item.small {
grid-row:span 1;
}
<div class="viewport">
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item small">3</div>
<div class="item small">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
</div>
</div>
Your width: max_content; setting is restricting the width of your container. Remove that property, and your full container will have a blue background as expected:
.container {
display: flex;
background-color: DodgerBlue;
flex-direction: column;
flex-wrap: wrap;
gap: 10px;
height: 200px;
}

How to use `flex: grow` on floating content?

I have a header with 2 rows of 2 Foundation columns of content, as below:
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
HEADER
</div>
<div class="large-6 columns">
menu
</div>
</div>
<div class="row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
The .header height is dynamic and not set. I want the .image element to take up 100% of the remaining vertical space.
eg:
To that affect I have tried using flex and flex-grow, eg:
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
}
.image-container {
flex-grow: 1;
}
but had no luck, see fiddle: https://jsfiddle.net/9kkb2bxu/46/
Would anyone know how I could negate the dynamic height of the header from the 100vh of the image container?
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
border: 1px solid black;
background-color: #ccc;
}
.row {
width: 100%;
}
.header {
background-color: green;
}
.info {
background-color: yellow;
border: 1px solid #ccc;
}
.image-container {
border: 1px solid black;
display: flex;
}
.image {
background-color: red;
flex-grow: 1;
width: 100%;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet"/>
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
<h1>
HEADER
</h1>
</div>
<div class="large-6 columns">
<h1>
menu
</h1>
</div>
</div>
<div class="row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
Set the second row to take up the rest of the remaining height with flex: 1 and make sure you nest that flex with display: flex:
.row.target-row {
flex: 1;
display: flex;
}
Set the .image-container to 100% height of its column parent.
.image-container {
height: 100%;
}
By default both columns will expand. Stop the left column from expanding with:
.large-5 {
align-self: flex-start;
}
(flex-start reference: https://stackoverflow.com/a/40156422/2930477)
Complete Example
.wrapper {
display: flex;
flex-direction: column;
min-height: 100vh;
width: 100%;
border: 1px solid black;
background-color: #ccc;
}
.row {
width: 100%;
}
.header {
background-color: green;
}
.info {
background-color: yellow;
border: 1px solid #ccc;
}
.image-container {
height: 100%;
background-color: red;
}
.large-5 {
align-self: flex-start;
}
.row.target-row {
flex: 1;
display: flex;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.3.1/css/foundation.min.css" rel="stylesheet" />
<div class="wrapper">
<div class="header row">
<div class="large-6 columns">
<h1>
HEADER
</h1>
</div>
<div class="large-6 columns">
<h1>
menu
</h1>
</div>
</div>
<div class="row target-row">
<div class="large-5 none show-for-medium columns info">
Some information to the left
</div>
<div class="large-7 columns">
<div class="image-container">
<div class="image">
image to the right
</div>
</div>
</div>
</div>
</div>
flex-grow only applies to flex children.
.image-container isn't a direct child of a display: flex element, so that property has no effect.
Plus, it affects the flex axis, which is not what you want.
Instead, you need to put those two elements in their own flex row, and use align-items (on the parent) and align-self (on either child) so that the first one aligns (on the cross axis) to flex-start (stick to top) and the second one to stretch.
You'll also want that flex row (parent) to have flex-grow: 1 so that it stretches along the vertical flex axis of its parent (.wrapper) to fill the rest of the page (otherwise, the grandchild will have nothing to stretch to).
For more information, read a good flex tutorial.
div.wrapper > div:not(.header).row {
flex: 1; /* 1 */
display: flex; /* 1 */
}
div.large-7.columns {
display: flex; /* 2 */
}
div.image-container { /* 3 */
flex: 1;
}
div.large-5.show-for-medium { /* 4 */
align-self: flex-start;
}
jsFiddle
Notes:
flex container and items consume all remaining height of respective parents
give children full height (via align-items: stretch initial setting)
flex item consumes all available width
yellow box does not need to expand to full height; now set to content height

Combine divs with percentage width with margin in pixels

I have a page with multiple items, which have a 33.33% width to fill up the complete width of the page. However, I want to add a 20px margin between the items (both vertically and horizontally, but vertically is the problem here), but just adding a 20px margin on the right of each 1st and 2nd item in a row, destroys the complete page. (remove the commented-out CSS from the fiddle to see what I mean).
JSfiddle
Now, the question: How do I add a 20px margin and make sure all .item divs keep the same size? I could play with the percentage of the width and adding the removed width of an .item as a margin, but this would never be a steady 20px, since, well, it's percentages.
HTML
<div id="main">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
<div class="item">10</div>
<div class="item">11</div>
<div class="item">12</div>
</div>
CSS
#main .item {
height: 100px;
width:33.33%;
float:left;
text-align: center;
}
#main .item:nth-child(3n+1) {
background: red;
}
#main .item:nth-child(3n+3) {
background: yellow;
}
#main .item:nth-child(3n+2) {
background:lightblue;
}
/*.item:nth-child(3n+1), .item:nth-child(3n+2) {
margin-right: 20px !important;
}*/
You could use calc() to subtract the 20px from 33.33%.
In this case, you would use width: calc(33.33% - 20px) to displace the margins.
Updated Example
#main .item:nth-child(3n+1),
#main .item:nth-child(3n+2) {
width: calc(33.33% - 20px);
margin-right: 20px;
}
Unfortunately, this will result in elements of differing widths (since the 20px isn't being subtracted from all the elements). A better solution would be to subtract 13.33px from all the elements (40px/3px):
Updated Example
#main .item {
height: 100px;
width: calc(33.33% - 13.33px);
float:left;
text-align: center;
}
Change the width of each .item class and calculate it using the calc()
#main .item {
height: 100px;
width:calc(33.33% - 20px);
float:left;
text-align: center;
}
Fiddle
You can use % for wrapper and change margin to padding while using box-sizing: border-box
.wrapper {
/* enable to add padding to width */
box-sizing: border-box;
width: 33.33%;
float: left;
padding: 20px;
background-color: #EFEFEF;
}
.item {
background-color: #ddd;
padding: 5px;
}
<div class="wrapper">
<div class="item">Some thext here</div>
</div>
<div class="wrapper">
<div class="item">Some thext here</div>
</div>
<div class="wrapper">
<div class="item">Some thext here</div>
</div>