Expandable box keeps growing - css

Browser: Chrome
I was trying to setup a row of boxes that have an item inside of them that can expand. I'd like the items on the same row to be the same height. When the expandable items are collapsed I expect the containers to shrink accordingly. Somehow I created something where when the box expands it keeps increasing its size.
This example was extracted from an application. So the multiple layer's of divs probably wont make much sense to you. I just extracted the minimal pieces needed to reproduce the issue.
const expandButtons = document.querySelectorAll('.expand');
for (let i = 0; i < expandButtons.length; i++) {
const button = expandButtons[i];
button.addEventListener("click", () => toggleExpand(button));
}
function toggleExpand(el) {
const content = el.parentElement.querySelector('.content');
content.classList.toggle('is-expanded');
}
.row {
display: flex;
}
.col {
display: flex;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid black;
}
.wrapper {
height: 100%;
}
.card-block {
height: 100%;
display: flex;
flex-direction: column;
}
.content {
max-height: 0;
overflow: auto;
transition: max-height 500ms;
}
.content.is-expanded {
max-height: 6em;
}
.spacer {
display: flex;
flex-grow: 1;
}
<div class="row">
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<p>ASDF</p>
<div class="content">
<p>First</p>
<p>Second</p>
<p>Third</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<div class="content">
<p>First</p>
<p>Second</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
</div>
Question
Expanding the second box increases the height and it doesn't retract back when collapsed. Expanding/Collapsing the first fixes it. Why does this happen? (I'm interested in an explanation rather than a solution)
Edit
I added the part of the example that justifies the height: 100% to avoid confusion. It is so that I can keep the buttons on the bottom. This involves adding display: flex; flex-direction: column; to .card-block and adding the .spacer class and corresponding element. These do not contribute to the problem but are the reason for the height: 100%.
BTW, and easy way to fix it is to remove flex-direction: column from .card. Again, I'm interested in the "why".

Note, the odd behavior appears to be a bug in Chrome, as this does not happen in the other browsers.
No matter why this happens, when using height: 100% on .wrapper you need to ask yourself, 100% of what?
An element having height: 100% picks up those 100% from its parent, but for that to work, also the parent need a height, which your card doesn't have, hence the 100% will resolve to auto.
And since wrapper will be resolved auto, so will also the .card-block, as its parent (the wrapper) doesn't have a defined height either. (It appears to somewhat behave in Chrome, thought this won't work cross browsers.)
The solution is to use Flexbox all the way.
Stack snippet
const expandButtons = document.querySelectorAll('.expand');
for (let i = 0; i < expandButtons.length; i++) {
const button = expandButtons[i];
button.addEventListener("click", () => toggleExpand(button));
}
function toggleExpand(el) {
const content = el.parentElement.querySelector('.content');
content.classList.toggle('is-expanded');
}
.row {
display: flex;
}
.col {
display: flex;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid black;
}
.wrapper {
/*height: 100%; removed */
flex: 1; /* added, fill parent height */
display: flex; /* added */
/*align-items: stretch this is the default and make its item,
the "card-block", fill its parent height */
}
.card-block {
/*height: 100%; removed */
display: flex;
flex-direction: column;
}
.content {
max-height: 0;
overflow: auto;
transition: max-height 500ms;
}
.content.is-expanded {
max-height: 6em;
}
.spacer {
display: flex;
flex-grow: 1;
}
<div class="row">
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<p>ASDF</p>
<div class="content">
<p>First</p>
<p>Second</p>
<p>Third</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<div class="content">
<p>First</p>
<p>Second</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
</div>

You should use height: auto, rather than height: 100%;
const expandButtons = document.querySelectorAll('.expand');
expandButtons.forEach(x => {
x.addEventListener("click", () => toggleExpand(x));
});
function toggleExpand(el) {
const content = el.parentElement.querySelector('.content');
content.classList.toggle('is-expanded');
}
.row {
display: flex;
}
.col {
display: flex;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid black;
}
.wrapper {
height: auto;
}
.card-block {
height: auto;
}
.content {
max-height: 0;
overflow: auto;
transition: max-height 500ms;
}
.content.is-expanded {
max-height: 6em;
}
<div class="row">
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<p>ASDF</p>
<div class="content">
<p>First</p>
<p>Second</p>
<p>Third</p>
</div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<div class="content">
<p>First</p>
<p>Second</p>
</div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
</div>

As far as I understood you mean this. I just add align-items:baseline; to .card class. Also I remove margin-top from .card-block > p class.
const expandButtons = document.querySelectorAll('.expand');
for (let i = 0; i < expandButtons.length; i++) {
const button = expandButtons[i];
button.addEventListener("click", () => toggleExpand(button));
}
function toggleExpand(el) {
const content = el.parentElement.querySelector('.content');
content.classList.toggle('is-expanded');
}
.row {
display: flex;
}
.col {
display: flex;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid black;
align-items: baseline;
}
.wrapper {
height: 100%;
}
.card-block {
height: 100%;
}
.card-block > p {
margin-top: 0;
}
.content {
max-height: 0;
overflow: auto;
transition: max-height 500ms;
}
.content.is-expanded {
max-height: 6em;
}
<div class="row">
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<p>ASDF</p>
<div class="content">
<p>First</p>
<p>Second</p>
<p>Third</p>
</div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<div class="content">
<p>First</p>
<p>Second</p>
</div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
</div>

A possible solution is to use
.wrapper {
flex: 1;
display: flex;
}
Rather than .wrapper {height: 100%}.
const expandButtons = document.querySelectorAll('.expand');
for (let i = 0; i < expandButtons.length; i++) {
const button = expandButtons[i];
button.addEventListener("click", () => toggleExpand(button));
}
function toggleExpand(el) {
const content = el.parentElement.querySelector('.content');
content.classList.toggle('is-expanded');
}
.row {
display: flex;
}
.col {
display: flex;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid black;
}
.wrapper {
flex: 1;
display: flex;
}
.card-block {
height: 100%;
display: flex;
flex-direction: column;
}
.content {
max-height: 0;
overflow: auto;
transition: max-height 500ms;
}
.content.is-expanded {
max-height: 6em;
}
.spacer {
display: flex;
flex-grow: 1;
}
<div class="row">
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<p>ASDF</p>
<div class="content">
<p>First</p>
<p>Second</p>
<p>Third</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="wrapper">
<div class="card-block">
<div class="content">
<p>First</p>
<p>Second</p>
</div>
<div class="spacer"></div>
<button type="button" class="expand">Expand</button>
</div>
</div>
</div>
</div>
</div>
According to my understanding, height: 100% confuses the element, as its parent's height is the expanded one (after the transition), so it want to fill it. Using flex: 1; and display: flex, the element will fill the remaining height, and will be a flexbox too, meaning its child won't change the .card height too.

Related

How to have make a div take a minumum of 50% space but if no sibling is present take 100%

Simply put I have:
#Container {
display:'flex';
flex-wrap:'wrap';
}
.item {
flex-basis: '50%'
}
Scenario one:
<div id=Container>
<div class="item"></div> 33 %
<div class="item"></div> 33 %
<div class="item"></div> 33%
</div>
Scenario 2
<div id=Container>
<div class="item"></div> 50 %
<div class="item"></div> 50 %
</div>
scenario 3:
<div id=Container>
<div class="item"></div> 100 %
</div>
What I want in general tems is this to be fluid, the more items I put in the less space each item will have but if there is only 1 then I want it to take full space.
First you have to add display: flex; to #Container
#Container{
display: flex;
}
If you want to equally distribute the space between children then you can use flex property as
.item{
flex: 1;
}
Above CSS is minimum required styles, rest is for demo
#Container {
display: flex;
margin-top: 1rem;
}
.item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
padding: 1rem;
}
.item:nth-child(1) {
background-color: red;
}
.item:nth-child(2) {
background-color: blueviolet;
}
.item:nth-child(3) {
background-color: aquamarine;
}
<div id="Container">
<div class="item">33 %</div>
<div class="item">33 %</div>
<div class="item">33 %</div>
</div>
<div id=Container>
<div class="item"> 50 % </div>
<div class="item"> 50 % </div>
</div>
<div id=Container>
<div class="item">100 %</div>
</div>
I think that this example could give you an idea of how to achieve what you want:
https://codepen.io/Eylen/pen/vYJBpMQ
.Container {
display: flex;
flex-wrap: wrap;
margin-bottom: 8px;
}
.item {
flex-grow: 1;
margin: 0 12px;
background: #f1f1f1;
}
Your main issue in the code that you gave, is that you're missing the flex item behaviour. I have just set that the item can grow to fill the space with the flex-grow:1.
You can make sure a flex child covers up the space if it can, you can provide flex-grow: 1
#Container {
display:flex;
}
.item {
width: 100%;
flex-grow: 1;
border: 1px solid;
text-align: center;
}
<h1> Scenario 1 </h1>
<div id=Container>
<div class="item">33 %</div>
<div class="item">33 %</div>
<div class="item">33%</div>
</div>
<h1> Scenario 2 </h1>
<div id=Container>
<div class="item">50 %</div>
<div class="item">50 %</div>
</div>
<h1> Scenario 3 </h1>
<div id=Container>
<div class="item">100 %</div>
</div>
Added a demo below.
$( document ).ready(function() {
$( "#add" ).click(function() {
$('#container').append('<div class="item"></div>');
});
$( "#remove" ).click(function() {
$('#container').children().last().remove();
});
});
#container {
width:100%;
height:500px;
background-color:#ebebeb;
display:flex;
flex-direction: column;
}
.item {
width:100%;
display: flex;
flex: 1;
border-bottom:1px solid #007cbe;
}
.item1 {
background:#007cbe;
}
.item2 {
background: #d60000;
}
.item3 {
background: #938412
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
</div>
<button id="add"> Add </div>
<button id="remove"> Remove </div>
Apply to the below CSS to fulfill your requirement.
#Container {
display: flex;
}
.item {
width: 100%;
min-height: 100px;
border: 1px solid;
}

Grid layout with auto columns resize with a full-width flex child

I have created a table with expandable rows using CSS Grid Layout and everything works fine until I add an element with a flex layout in the expandable section.
The expandable section use a grid-column: 1/-1; and the content should not affect the rest of the grid or that is what i thought.
I have created a simplified version to show the problem:
function toggle() {
const elem = document.querySelector('.all-columns');
if (elem.style.display === "none") {
elem.style.display = "flex";
} else {
elem.style.display = "none";
}
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto;
width: 400px;
}
.row {
display: contents;
}
.all-columns {
grid-column: 1/-1;
display: flex;
flex-wrap: wrap;
background: red;
border: 1px solid black;
}
.column {
border: 1px solid black;
}
.flex-child {
width: 180px;
text-align: center;
background: blue;
border: 1px solid black;
}
<div class="grid-container">
<div class="row">
<div class="column">Row1 Column1</div>
<div class="column">Row1 Column2 with large name</div>
<div class="column">Row1 Column3</div>
</div>
<div class="row">
<div class="all-columns">
<div class="flex-child">1 flex</div>
<div class="flex-child">2 flex</div>
<div class="flex-child">3 flex</div>
<div class="flex-child">4 flex</div>
<div class="flex-child">5 flex</div>
</div>
</div>
<div class="row">
<div class="column">Row3 Column1</div>
<div class="column">Row4 Column2</div>
<div class="column">Row1 Column3</div>
</div>
</div>
<br>
<button onclick="toggle()">Toggle expandable section</button>
As you can see, the size of the middle column changes when the expandable section is shown/hide.
The content of the expandable section is external so I cannot modify it at all.
I have tested it in Firefox and Chrome with the same result.
I'd appreciate if someone has an explanation for this behaviour.
Thanks.
add width: 0;min-width: 100%; to all-columns so it won't contribute to defining the width and won't affect the other elements:
function toggle() {
const elem = document.querySelector('.all-columns');
if (elem.style.display === "none") {
elem.style.display = "flex";
} else {
elem.style.display = "none";
}
}
.grid-container {
display: grid;
grid-template-columns: auto auto auto;
width: 400px;
}
.row {
display: contents;
}
.all-columns {
grid-column: 1/-1;
width: 0;
min-width: 100%;
display: flex;
flex-wrap: wrap;
background: red;
border: 1px solid black;
}
.column {
border: 1px solid black;
}
.flex-child {
width: 180px;
text-align: center;
background: blue;
border: 1px solid black;
}
<div class="grid-container">
<div class="row">
<div class="column">Row1 Column1</div>
<div class="column">Row1 Column2 with large name</div>
<div class="column">Row1 Column3</div>
</div>
<div class="row">
<div class="all-columns">
<div class="flex-child">1 flex</div>
<div class="flex-child">2 flex</div>
<div class="flex-child">3 flex</div>
<div class="flex-child">4 flex</div>
<div class="flex-child">5 flex</div>
</div>
</div>
<div class="row">
<div class="column">Row3 Column1</div>
<div class="column">Row4 Column2</div>
<div class="column">Row1 Column3</div>
</div>
</div>
<br>
<button onclick="toggle()">Toggle expandable section</button>

Nested Flexbox - outer container doesn't flex

Update : I have edited the snippet to show better what I'm trying to achieve...
I have a number of tables of data, each of variable length, on a kiosk display. I want to fill the viewport as columns then overflow to pages below ie paging down would give me next screen of data. I thought Nested Flexbox would allow me to do this but the outer Container doesn't do what I hoped and data just flows to right - see below. Am I just inept or should I be doing it another way? Thx!
.container1 {
background: lightgrey;
display: flex;
width:300px;
flex-direction: column;
flex-wrap:wrap;
}
.container2 {
background: orangered;
display: flex;
flex-direction: column;
height:200px;
width: 300px;
flex-wrap: wrap;
}
.container2 > div{
font-size: 40px;
width: 100px;
}
.green {
background: yellowgreen;
}
.blue {
background: steelblue;
}
My effort doesn't work ...
<div class="container1">
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">5a<br>5b</div>
<div class="blue">6a<br></div>
<div class="green">7a<br>7b</div>
<div class="blue">8a<br>8b<br>8c</div>
<div class="green">9a<br>9b<br>9c</div>
<div class="blue">10a<br></div>
<div class="green">11a<br>11b</div>
<div class="blue">12a<br>12b<br>12c</div>
</div>
</div>
I want output like this but ...
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
</div>
<div class="container2">
<div class="green">5a<br>5b</div>
<div class="blue">6a<br></div>
<div class="green">7a<br>7b</div>
<div class="blue">8a<br>8b<br>8c</div>
</div>
<div class="container2">
<div class="green">9a<br>9b<br>9c</div>
<div class="blue">10a<br>10a<br></div>
<div class="green">11a<br>11b</div>
<div class="blue">12a<br>12b<br>12c</div>
</div>
You don't want to use flex-direction: column on the inner container2. You still want that to be row.
Setting flex-direction: column only establishes that the direction of children should flow from top to bottom (or reverse with column-reverse).
Setting flex-wrap: wrap on a parent with flex-direction: column wraps the elements on the cross axis (row in this case).
You don't even need the outer parent container, since there was only one flex child container2.
.container2 {
background: orangered;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 300px;
height: 200px;
overflow: scroll;
}
.container2 > div {
font-size: 40px;
flex: 0 0 33.33333%;
min-height: 200px;
}
.green {
background: yellowgreen;
}
.blue {
background: steelblue;
}
<div class="container2">
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
<div class="green">1a<br>1b<br>1c</div>
<div class="blue">2a<br></div>
<div class="green">3a<br>3b</div>
<div class="blue">4a<br>4b<br>4c</div>
</div>

CSS Display: Flex on Desktop and Mobile

I'm newer to using FLEXBOX and have a question on a layout using Desktop and Mobile.
I'm displaying info using a label and data (not on a form).
On the desktop, I'd like it to display like this:
label-------label-------label------label
Data--------Data--------Data-------Data
On a desktop, label and data will be a % based on number of elements.
But, on a mobile, I'd like it to display like this:
label------
Data-------
Label------
Data-------
label------
data-------
Label------
Data-------
label------
data-------
On mobile, I'd like label and data to be 25% & 75% wide.
Not sure on the HTML if it should be laid out like this
<div class="container">
<div class="label">Label</div>
<div class="data">Data</a>
<div class="label">Label</div>
<div class="data">Data</a>
<div class="label">Label</div>
<div class="data">Data</a>
</div>
OR
<div class="container">
<div class="label">label</div>
<div class="label">label</div>
<div class="label">label</div>
</div>
<div class="container">
<div class="data">data</div>
<div class="data">data</div>
<div class="data">data</div>
</div>
Any guidance would be great!
Thanks!
It sounds like you're flexible on the HTML structure.
I recommend grouping data pairs with parent elements, making columns and rows.
With two nested flexboxes, you can change flex-direction as desired.
/* FOR DEMO PURPOSES */
var $body = $('body');
$('button').on('click', function() {
$body.toggleClass('small');
});
.container {
display: flex;
flex-wrap: wrap;
background-color: lightgreen;
}
.group {
display: flex;
flex-direction: column;
}
.item {
padding: .25em .5em;
}
#media (min-width: 700px) {
.container {
background-color: lightblue;
flex-direction: column;
}
.group {
flex-direction: row;
}
}
/* FOR DEMO PURPOSES */
.small .container {
background-color: lightblue;
flex-direction: column;
}
.small .group {
flex-direction: row;
}
button {
position: absolute;
top: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="group">
<div class="item label">Label 1</div>
<div class="item data">Data 1</div>
</div>
<div class="group">
<div class="item label">Label 2</div>
<div class="item data">Data 2</div>
</div>
<div class="group">
<div class="item label">Label 3</div>
<div class="item data">Data 3</div>
</div>
</div>
<button>Simulate Size Change</button>
Here's a demo with more than one row of data:
/* FOR DEMO PURPOSES */
var $body = $('body');
$('button').on('click', function() {
$body.toggleClass('small');
});
.container {
display: flex;
flex-wrap: wrap;
background-color: lightgreen;
}
.group {
display: flex;
flex-direction: column;
}
.item {
padding: .25em .5em;
}
.label {
font-weight:bold;
}
.flag {
color:red;
}
#media (min-width: 700px) {
.container {
background-color: lightblue;
flex-direction: column;
}
.group {
flex-direction: row;
}
}
/* FOR DEMO PURPOSES */
.small .container {
background-color: lightblue;
flex-direction: column;
}
.small .group {
flex-direction: row;
}
button {
position: absolute;
top: 130px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="group">
<div class="item label">Label 1</div>
<div class="item data">Data 1a</div>
<div class="item data flag">Data 1b</div>
<div class="item data">Data 1c</div>
</div>
<div class="group">
<div class="item label">Label 2</div>
<div class="item data">Data 2a</div>
</div>
<div class="group">
<div class="item label">Label 3</div>
<div class="item data">Data 3a</div>
<div class="item data">Data 3b</div>
</div>
</div>
<button>Simulate Size Change</button>
For more ideas, I highly recommend reading Accessible, Simple, Responsive Tables # CSS-Tricks and checking out the other resources listed there.
I would order them like the first option. Then, at desktop view I'd make a flex flow of column wrap, set a height for the container and set each flex-item to be 50% height.
On mobile, id switch to flex-flow: row wrap, and define the container's width, then each child at 25%/75% depends on which child.
HTML:
<div class="container">
<div class="label">Label</div>
<div class="data">Data</a>
<div class="label">Label</div>
<div class="data">Data</a>
<div class="label">Label</div>
<div class="data">Data</a>
</div>
CSS:
.container {
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
align-items: center;
height: 500px; // what ever you want here
}
.label, .data {
height: 45% // Less than 50% for margins, etc.
}
#media only screen and (max-width: 900px) { //Set desired width to call 'mobile'
.container {
flex-flow: row wrap;
width: unset;
height: 500px; //Whatever you want again
}
.data {
width: 70%;
}
.label {
width: 22%;
}
}

Maintain ratio of container and children while resize

I have modular grid of cards with and without image, based on flexbox.
Normally cards without image are 50% of the width, cards with image are 100% of the width, and those cards without image that doesn't have a pair are also 100%.
----------------
[[ txt ][ img ]] – card with an image
----------------
[ txt ][ txt ] – 2 cards without image
----------------
[ txt ] – card without image
----------------
[[ txt ][ img ]] – card with an image
----------------
Is it possible to maintain ratio of the cards and its content while resize, and also maintain modularity with css?
Here's the code
that let me maintain ratio, but when I try to apply this technique to cards children everything breakes
<div class="wrapper">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
.wrapper
display: flex
flex-wrap: wrap
justify-content: space-between
width: 620px
.card
height: 0
padding-bottom: 50%
display: flex
flex-direction: row
justify-content: space-between
flex: 1 1 300px
.has-card-image .card-text,
.has-card-image .card-image
height: 300px
width: 300px
I can see one way this could be done w/o script.
Either as in this case, using px values, or e.g. using vw, and the reason is that the card-text and card-image needs to know the wrapper's width to be able to size according to the expected output (which mean percent can't be used).
Fiddle demo
Stack snippet
.wrapper {
display: flex;
flex-wrap: wrap;
width: 600px;
}
.card {
display: flex;
flex: 1 300px;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 300px;
}
.card .card-text:only-child {
flex: 1 300px;
}
/* demo styles */
.card .card-text, .card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
If you need the margin, you'll need an extra wrapper
Fiddle demo
Stack snippet
.wrapper {
width: 620px;
overflow: hidden;
}
.inner {
display: flex;
flex-wrap: wrap;
margin-left: -20px;
}
.card {
display: flex;
flex: 1 300px;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 300px;
margin-left: 20px;
}
.card .card-text:only-child {
flex: 1 300px;
}
/* demo styles */
.card .card-text,
.card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="inner">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
</div>
Updated
Here is a version using vw and margin, and is responsive.
Note, the make up for margin set on the wrapper can also be set on each item, though I found it simpler like this.
Fiddle demo
Stack snippet
.wrapper {
width: calc(80vw + 20px); /* 20px extra for margin */
margin: 0 auto;
overflow: hidden;
}
.inner {
display: flex;
flex-wrap: wrap;
margin-left: -20px;
}
.card {
display: flex;
flex: 1 40vw;
}
.card .card-text,
.card .card-image {
height: 100px;
width: 40vw;
margin-left: 20px;
}
.card .card-text:only-child {
flex: 1 40vw;
}
/* demo styles */
.card .card-text,
.card .card-image {
border: 1px solid;
box-sizing: border-box;
}
.card .card-text::before {
content: 'text';
}
.card .card-image::before {
content: 'image';
}
<div class="wrapper">
<div class="inner">
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
</div>
<div class="card">
<div class="card-text">
</div>
<div class="card-image">
</div>
</div>
</div>
</div>

Resources