I am trying to create a responsive layout using flexbox where one pair of items (two and four) need to be stacked, next to section three when on a large screen, but then on narrow screens, the order needs to change so that they are stacked, one, two, three, four.
I've got a solution, but it isn't DRY, as you can see in the snippet I have duplicated section four.
Is it possible to achieve this using flexbox without the duplication?
.container {
display: flex;
flex-wrap: wrap;
width: 100%;
border: 1px solid red;
}
.section {
border: 1px solid green;
flex-grow: 1;
}
.one, .two, .four {
width: 100%;
}
.sub-container {
display: inline-flex;
flex-direction: column;
width: 50%;
}
.mobile-only {
display: none;
}
#media (max-width: 600px) {
.container {
flex-direction: column;
}
.sub-container {
width: 100%;
}
.section {
width: 100%;
}
.desktop-only {
display: none;
}
.mobile-only {
display: block;
}
}
<div class="container">
<div class="section one">One</div>
<div class="sub-container">
<div class="section two">Two</div>
<div class="section four desktop-only">Four</div>
</div>
<div class="section three">Three</div>
<div class="section four mobile-only">Four</div>
</div>
Flexbox isn't designed for building 2-dimensional grids (where columns and rows can intersect). So for your layout to work using flex properties, you're going to have to hack your way there.
Here's a more complete explanation of the problem:
Is it possible for flex items to align tightly to the items above them?
However, your layout is simple and easy with CSS Grid.
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-auto-rows: 50px;
grid-gap: 5px;
grid-template-areas: " one one "
" two three "
" four three ";
}
.one { grid-area: one; }
.two { grid-area: two; }
.three { grid-area: three; }
.four { grid-area: four; }
#media ( max-width: 600px ) {
.container {
grid-template-columns: 1fr;
grid-template-areas: " one "
" two "
" three"
" four ";
}
}
/* non-essential; just for demo */
.section {
border: 1px solid gray;
background-color: lightgreen;
display: flex;
align-items: center;
justify-content: center;
}
<div class="container">
<div class="section one">One</div>
<div class="section two">Two</div>
<div class="section three">Three</div>
<div class="section four">Four</div>
</div>
https://jsfiddle.net/9fyprm3u/
The Grid specification provides multiple methods for designing grids. In this case, I've use the grid-template-areas property which allows you to lay out grid items using ASCII art.
browser support
Related
This question already has an answer here:
Put one div next to two vertical divs
(1 answer)
Closed 1 year ago.
I am looking for the simplest method, to position three images in the following way using CSS and HTML
Image number 2 and 3 should be of 50% width and height of image number 1.
Thank you in advance
You can use a grid to do so
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
width: 100%;
grid-gap: 4px;
}
img {
max-width: 100%;
}
img:first-child {
max-width: unset;
height: 100%;
width: 100%;
object-fit: cover;
grid-row: span 2;
}
<div class="grid">
<img src="https://images.unsplash.com/photo-1618326985678-88285545a9aa?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYyMDc5OTgyNA&ixlib=rb-1.2.1&q=85" alt="">
<img src="https://images.unsplash.com/photo-1618326985678-88285545a9aa?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYyMDc5OTgyNA&ixlib=rb-1.2.1&q=85" alt="">
<img src="https://images.unsplash.com/photo-1618326985678-88285545a9aa?crop=entropy&cs=srgb&fm=jpg&ixid=MnwxNDU4OXwwfDF8cmFuZG9tfHx8fHx8fHx8MTYyMDc5OTgyNA&ixlib=rb-1.2.1&q=85" alt="">
</div>
.container{
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
height: 100px;
width: 100px;
}
#img1{
grid-row: 1/3;
grid-column: 1/2;
border: red solid 1px;
}
#img2{
border: blue solid 1px;
}
#img3{
border: green solid 1px;
}
<div class="container">
<div id="img1"></div>
<div id="img2"></div>
<div id="img3"></div>
</div>
I would honestly go with Charles Lavalard's grid solution above, but wanted to show a flex solution that takes in that the images may not fit the designated space.
html, body {
margin: 0px;
}
.container {
display: flex; /* display side by side */
align-items: stretch; /* stretches the .right div */
}
.container img {
object-fit: cover; /* clips image if it's too big */
}
.left, .right {
flex: 1 1 auto; /* fills out the width */
flex-basis: 50%;
}
.right {
display: flex;
flex-direction: column;
}
.small {
flex: 1 1 auto; /* fills out the height */
}
<div class="container">
<img class="left large" src="https://picsum.photos/150/300">
<div class="right">
<img class="small" src="https://picsum.photos/300/100">
<img class="small" src="https://picsum.photos/300/100">
</div>
</div>
There are numerous ways to do this, but i think flex box is the right approach if you want to further align the text inside one of the divs.
A simple method will be to use 3 child divs and wrap it around 2 parent divs and finally 1 grandparent div. Use flex box with direction row on the grandparent div content. Then specify on col2 to be flex-direction column.
.content {
display: flex;
flex-direction: row;
}
.col2 {
display: flex;
flex-direction: column;
}
.first {
background-color: red;
height: 100vh;
width: 50vw;
}
.second {
background-color: green;
height: 50vh;
width: 50vw;
}
.third {
background-color: yellow;
height: 50vh;
width: 50vw;
}
<div class="content">
<div class="col1">
<div class="first">lst</div>
</div>
<div class="col2">
<div class="second">2nd</div>
<div class="third">third</div>
</div>
</div>
img {
height:300px;
width:150px;
border:1px solid white
}
.rows {
display:flex;
flex-direction:column;
}
.container {
display:flex;
flex-direction:row;
}
.rows img{
height:150px;
width: 75px;}
<div class="container">
<img src="https://cdn5.vectorstock.com/i/1000x1000/40/49/cityscape-vertical-3-vector-19094049.jpg">
<div class="rows">
<img src="https://cdn5.vectorstock.com/i/1000x1000/40/49/cityscape-vertical-3-vector-19094049.jpg">
<img src="https://cdn5.vectorstock.com/i/1000x1000/40/49/cityscape-vertical-3-vector-19094049.jpg">
</div>
</div>
I am using CSS media queries to create a responsive layout.
In my current HTML layout I use flexbox to align rows of divs:
<div id="page">
<div id="div-1">DIV 1</div>
<div id="div-2">DIV 2</div>
<div id="div-3">DIV 3</div>
<div id="div-4">DIV 4</div>
</div>
and CSS:
#page {
display: flex;
flex-direction: column;
align-items: center;
}
[id^="div-"] {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 50vw;
}
#div-1 {
height: 50px;
}
#div-2 {
height: 70px;
}
#div-3 {
height: 150px;
}
#div-4 {
height: 100px;
}
Here is Jsfiddle for you to tinker with.
It is what I am after for a smaller viewports, but would like to switch things around on the next media query break point to have 2 middle divs to shift to a separate column on the right like this:
How do I achieve that? It is pretty obvious for me how to shift last few div rows to another column, but not sure how to tackle the middle rows...
Is there a way to do it by using flexbox or grid?
Another possible solution is to use the order-property in combination with Flexbox (drawback: you'll need a tiny bit of extra html and set the height of the #page container; advantages: flexible div heights and gap sizes):
#page {
display: flex;
flex-direction: column;
align-items: center;
}
[id^="div-"] {
border: 1px solid #ccc;
margin-bottom: 10px;
width: 50vw;
}
#div-1 {
height: 50px;
background: lightgreen;
}
#div-2 {
height: 70px;
background: lightyellow;
}
#div-3 {
height: 150px;
background: lightcoral;
}
#div-4 {
height: 100px;
background: lightblue;
}
#media (max-width: 1000px) {
#page {
flex-wrap: wrap;
height: 300px;
}
#small-screen-spacer {
order: 3;
align-self: stretch;
flex: 1 0 100%;
margin-right: 10px;
}
#div-1 {
order: 1;
}
#div-2 {
order: 4;
}
#div-3 {
order: 5;
}
#div-4 {
order: 2;
}
}
<div id="page">
<div id="div-1">DIV 1</div>
<div id="div-2">DIV 2</div>
<div id="div-3">DIV 3</div>
<div id="div-4">DIV 4</div>
<div id="small-screen-spacer"> </div>
</div>
The #small-screen-spacer will fill the entire available vertical space, so that all elements that come after the spacer (defined by the order property) are moved to the second column. Additionally you can set the desired gap between the two columns by setting margin-right on the spacer to your desired value.
Fiddle
I have the following code:
.wrapper {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.a {
background-color: red;
width: 65%;
}
.b {
background-color: green;
width: 35%;
}
.c {
background-color: blue;
width: 65%;
height: 100px;
}
.d {
background-color: orange;
width: 35%;
}
.e {
background-color: teal;
width: 65%;
}
.f {
background-color: purple;
width: 35%;
}
<div class="wrapper container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
<div class="d">D</div>
<div class="e">E</div>
<div class="f">F</div>
</div>
I am trying to make the F be right under D, like this:
The main reason I am doing this instead of 2 separate columns is because I want to be able to arrange the columns in mobile later by using order. Is this possible in Flexbox, or is there another way?
In order for a flex column to wrap, you need to define a fixed height on the container. Otherwise, without a breakpoint, the column has no reason to wrap. It will simply expand the container as a single column.
This problem is explained in more detail here:
Is it possible for flex items to align tightly to the items above them?
You're probably better off with a grid solution.
.wrapper {
display: grid;
grid-template-columns: 65% 35%;
grid-template-areas: " a b "
" c d "
" c f "
" c . "
" c . "
" e . " ;}
.a { grid-area: a; background-color: red; }
.b { grid-area: b; background-color: green; }
.c { grid-area: c; background-color: blue; height: 100px; }
.d { grid-area: d; background-color: orange; }
.e { grid-area: e; background-color: teal; }
.f { grid-area: f; background-color: purple; }
<div class="wrapper">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
<div class="d">D</div>
<div class="e">E</div>
<div class="f">F</div>
</div>
Browser support is now pretty strong for CSS Grid.
Also, the order property works in both Grid and Flex layouts. But you may not need order in Grid to re-arrange your layout for mobile screens. You can simply use grid properties.
Inspired from Easy-Masonry-Layout-With-Flexbox
Seems to do the job for me
<div id="masonry">
<img src="irina.jpg" alt>
<img src="daniella.jpg" alt>
<img src="karina.jpg" alt>
…
</div>
div#masonry {
display: -ms-flexbox;
-ms-flex-direction: column;
-ms-flex-wrap: wrap;
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 100vw;
font-size: 0;
}
div#masonry img {
width: 33.3%;
transition: .8s opacity;
}
This question already has answers here:
Centering in CSS Grid
(9 answers)
Closed 3 years ago.
Using CSS-Grid, I try to put my items in the center of a grid cell, without shrinking them completely to only the content. Is this possible?
I made a simple example on stackblitz. You can see that the items there don't fill the entire grid-cell with the background color. What is the proper way to get that working? I can remove the justify-items/align-items classes, but then the content isn't centered anymore.
https://stackblitz.com/edit/angular-8bggtq?file=app/app.component.html
Cells filled, but content not in center:
Cells not filled, but content is centered:
.wrapper {
display: grid;
height: 100vh;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
justify-items: center;
align-items: center;
}
.item {
//justify-self: stretch;
//align-self: stretch;
}
.one {
background: red;
}
.two {
background: pink;
}
.three {
background: violet;
}
.four {
background: yellow;
}
.five {
background: brown;
}
.six {
background: green;
}
html,
body {
margin: 0;
}
<div class="wrapper">
<div class="item one">1</div>
<div class="item two">2</div>
<div class="item three">3</div>
<div class="item four">4</div>
<div class="item five">5</div>
<div class="item six">6</div>
</div>
The HTML structure of a grid container has three levels:
the container
the items (the children of the container)
the content (the grandchildren of the container and children of the items)
The problem you're having is that you're taking a two-level approach instead of the correct three-level approach. When you set align-items and justify-items on the container, they apply to the grid items, not to the content.
That's exactly what you are seeing: The grid items are being vertically and horizontally centered.
If you want to center the grid item children (the content), you need to specify that on the items. You can repeat on the items what you did on the container:
.item {
display: grid;
justify-items: center;
align-items: center;
}
Or, if you don't need grid layout in the items, here's another simple method:
.item {
display: flex;
justify-content: center;
align-items: center;
}
The above concepts apply to flex containers, as well.
For a more complete explanation and other centering methods see this post: Centering in CSS Grid
.wrapper {
display: grid;
height: 100vh;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
}
.item {
display: flex;
align-items: center;
justify-content: center;
}
.one { background: red; }
.two { background: pink; }
.three { background: violet; }
.four { background: yellow; }
.five { background: brown; }
.six { background: green; }
body { margin: 0; }
<div class="wrapper">
<div class="item one">1</div>
<div class="item two">2</div>
<div class="item three">3</div>
<div class="item four">4</div>
<div class="item five">5</div>
<div class="item six">6</div>
</div>
I would say the only way to do that just with CSS-grid is to insert a additional element- / grid-level.
However, I would also say that here - as #Zuber has already showed - the combination between grid and flexbox is the best way to achieve what you want.
Grid is designed to be used with flexbox, not instead of it
Ollie Williams: Things I’ve Learned About CSS Grid Layout
Pure Grid-example:
* { margin: 0; padding: 0; box-sizing: border-box; }
body { margin: 20px; }
.wrapper {
height: 100vh;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-gap: 20px;
}
.wrapper__item {
display: grid;
align-items: center;
justify-items: center;
background: gray;
color: white;
font-size: 2em;
}
<div class="wrapper">
<div class="wrapper__item"><span>1</span></div>
<div class="wrapper__item"><span>2</span></div>
<div class="wrapper__item"><span>3</span></div>
<div class="wrapper__item"><span>4</span></div>
<div class="wrapper__item"><span>5</span></div>
<div class="wrapper__item"><span>6</span></div>
</div>
Grid- & Flexbox-example:
* { margin: 0; padding: 0; box-sizing: border-box; }
body { margin: 20px; }
.wrapper {
height: 100vh;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
grid-gap: 20px;
}
.wrapper__item {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: gray;
color: white;
font-size: 2em;
}
<div class="wrapper">
<div class="wrapper__item">1</div>
<div class="wrapper__item">2</div>
<div class="wrapper__item">3</div>
<div class="wrapper__item">4</div>
<div class="wrapper__item">5</div>
<div class="wrapper__item">6</div>
</div>
you need to add some css in the class "item"
.item {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
Try with justify-self: center or text align:center if it is only text.
I'm trying to make a horizontal card using flexbox without having nested divs.
For example, what I have right now is this:
.card {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.card-img {
width: 37%;
}
.card-content {
width: 63%;
}
<div class='card'>
<div class='card-img'>
<img src='example' />
</div>
<div class='card-content'>
<div class='card-number'>
</div>
<div class='card-type'>
</div>
</div>
</div>
This layout works for now, but when the viewport resizes, I want the horizontal card to change into a vertical one (which it already does). However, I want to re-order the card-img, card-number, and card-type using flex order.
How could I get this same horizontal layout while using this type of div layout?
Edit:
Sorry for the confusing wording, what I was aiming to do was to create a horizontal layout that works like the image except with 3 separate divs, not 2 divs with 1 being a nested div. Sorry!
.card {
display: flex;
}
.card-img {
flex: 0 0 37%;
border: 1px dashed red;
}
.card-number {
flex: 0 0 30%;
border: 1px dashed red;
}
.card-type {
flex: 1 0 30%;
border: 1px dashed red;
}
img {
width: 100%;
}
.card > div {
display: flex;
align-items: center;
justify-content: center;
}
#media (max-width: 500px) {
.card { flex-direction: column; }
.card-img { order: 3; }
.card-number { order: 2; }
.card-type { order: 1; }
}
<div class='card'>
<div class='card-img'>
<img src="http://i.imgur.com/60PVLis.png">
</div>
<div class='card-number'>555</div>
<div class='card-type'>Orange</div>
</div>
https://jsfiddle.net/7y8sarwn/