What's the best way to achieve this layout with flexbox? - css

I have 4 columns and three rows. The last tile is two rows high.
I'm working on a code that looks like this:
.gridContainer {
display: flex;
flex-wrap: wrap;
}
.gridItems {
width: 290px;
height: 350px;
margin: 0 5px 10px;
border-radius: 4px;
}
.gridItemsTall {
width: 290px;
height: 710px;
margin: 0 5px 10px;
border-radius: 4px;
}

A good way to achieve this with flexbox it's by using a combinaison of flex-direction: column, flex-wrap: wrap and max-height.
He is an update of your CSS :
.gridContainer {
display: flex;
flex-direction: column;
flex-wrap: wrap;
align-content: flex-start;
max-height: 1080px; // box height *3 + margin-bottom*3 -> 350*3 + 10*3
}
.gridItems {
width: 290px;
height: 350px;
margin: 0 5px 10px;
border-radius: 4px;
background-color: #ccc;
}
.gridItemsTall {
height: 710px;
}
And on the following link, you can find a live example : https://jsfiddle.net/julienvanderkluft/dkdfy7gb/
Note that the height of .gridContainer can be manipulate by JavaScript for more flexibility.

You can use CSS Grid layout instead of flexbox in this case. You just need to set grid-row: span 2 on element you want to take two rows.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 50px;
grid-gap: 10px;
width: 200px;
}
.box {
background: #aaa;
}
.large {
grid-row: span 2;
}
<div class="grid">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box large"></div>
<div class="box"></div>
<div class="box"></div>
</div>

Related

How to remove the empty space between elements?

I have made a calculator using one parent div and plenty of child divs, the children are in html. After that I styled it using flex-box and it is almost done; nevertheless there is an empty space between first div (.result) and the rest of the divs (.btn).
I want to remove that empty space which is shown in picture below:
I tried to use flex-box method to arrange these div elements like blocks.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin: 0 auto 0 auto;
background-color: gray;
text-align: center;
margin-bottom: 5vh;
}
.kalkulator {
display: flex;
flex-wrap: wrap;
height: 50vh;
width: 30vw;
margin: 0 auto 0 auto;
}
.kalkulator .result {
flex-basis: 100%;
height: 10vh;
background-color: #333;
}
.kalkulator div {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 33%;
background-color: #666;
border: 1px solid black;
}
.btn:nth-last-child(2),
.btn:nth-last-child(1) {
flex-basis: 50%;
}
<h1>kalkulator</h1>
<div class="kalkulator">
<div class="result">wynik</div>
<div class="btn">1</div>
<div class="btn">2</div>
<div class="btn">3</div>
<div class="btn">4</div>
<div class="btn">5</div>
<div class="btn">6</div>
<div class="btn">7</div>
<div class="btn">8</div>
<div class="btn">9</div>
<div class="btn">+</div>
<div class="btn">0</div>
<div class="btn">-</div>
<div class="btn">/</div>
<div class="btn">.</div>
<div class="btn">*</div>
<div class="btn">C</div>
<div class="btn">=</div>
</div>
Apply align-content: flex-start to the flex container.
The default setting is align-content: stretch, which will spread the items across the container. With flex-start, they'll be packed together at the top.
Your code:
.kalkulator {
display: flex;
flex-wrap: wrap;
height: 50vh;
width: 30vw;
margin: 0 auto 0 auto;
border: 2px dashed red;
background-color: yellow;
}
.kalkulator .result {
flex-basis: 100%;
height: 10vh;
background-color: #333;
}
.kalkulator div {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 33%;
background-color: #666;
border: 1px solid black;
}
.btn:nth-last-child(2),
.btn:nth-last-child(1) {
flex-basis: 50%;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin: 0 auto 0 auto;
background-color: gray;
text-align: center;
margin-bottom: 5vh;
}
<h1>kalkulator</h1>
<div class="kalkulator">
<div class="result">wynik</div>
<div class="btn">1</div>
<div class="btn">2</div>
<div class="btn">3</div>
<div class="btn">4</div>
<div class="btn">5</div>
<div class="btn">6</div>
<div class="btn">7</div>
<div class="btn">8</div>
<div class="btn">9</div>
<div class="btn">+</div>
<div class="btn">0</div>
<div class="btn">-</div>
<div class="btn">/</div>
<div class="btn">.</div>
<div class="btn">*</div>
<div class="btn">C</div>
<div class="btn">=</div>
</div>
Modified code:
.kalkulator {
display: flex;
flex-wrap: wrap;
height: 50vh;
width: 30vw;
margin: 0 auto 0 auto;
border: 2px dashed red;
background-color: yellow;
align-content: flex-start;
}
.kalkulator .result {
flex-basis: 100%;
height: 10vh;
background-color: #333;
}
.kalkulator div {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 33%;
background-color: #666;
border: 1px solid black;
}
.btn:nth-last-child(2),
.btn:nth-last-child(1) {
flex-basis: 50%;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin: 0 auto 0 auto;
background-color: gray;
text-align: center;
margin-bottom: 5vh;
}
<h1>kalkulator</h1>
<div class="kalkulator">
<div class="result">wynik</div>
<div class="btn">1</div>
<div class="btn">2</div>
<div class="btn">3</div>
<div class="btn">4</div>
<div class="btn">5</div>
<div class="btn">6</div>
<div class="btn">7</div>
<div class="btn">8</div>
<div class="btn">9</div>
<div class="btn">+</div>
<div class="btn">0</div>
<div class="btn">-</div>
<div class="btn">/</div>
<div class="btn">.</div>
<div class="btn">*</div>
<div class="btn">C</div>
<div class="btn">=</div>
</div>
It seems too much* for one flexbox. Create the layout with two nested ones: one for the vertical partition of the calculator to the result and the buttons, and another one for the fluid positioning of the buttons within the buttons div:
<div class="kalkulator">
<div class="result">wynik</div>
<div class="buttons">[...]</div>
</div>
.kalkulator {
display: flex;
flex-direction: column;
}
.result {
flex-basis: 10vh;
}
.buttons {
flex-grow: 1; /* Fill the rest */
display: flex;
flex-wrap: wrap;
}
.btn {
flex-grow: 1;
flex-basis: 33%;
justify-content: center;
align-items: center;
display: flex;
}
For placing the buttons under the result box you need a flexbox with a vertical main axis (flex-direction: column). For placing the buttons in a row, a flexbox with a horizontal main axis (the default flex-direction: row).
See the following code, here I removed height: 50vh; property from .kalkulator class:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin: 0 auto 0 auto;
background-color: gray;
text-align: center;
margin-bottom: 5vh;
}
.kalkulator {
display: flex;
flex-wrap: wrap;
/* height: 50vh; */
width: 30vw;
margin: 0 auto 0 auto;
}
.kalkulator .result {
flex-basis: 100%;
height: 10vh;
background-color: #333;
}
.kalkulator div {
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
flex-basis: 33%;
background-color: #666;
border: 1px solid black;
}
.btn:nth-last-child(2),
.btn:nth-last-child(1) {
flex-basis: 50%;
}
<h1>kalkulator</h1>
<div class="kalkulator">
<div class="result">wynik</div>
<div class="btn">1</div>
<div class="btn">2</div>
<div class="btn">3</div>
<div class="btn">4</div>
<div class="btn">5</div>
<div class="btn">6</div>
<div class="btn">7</div>
<div class="btn">8</div>
<div class="btn">9</div>
<div class="btn">+</div>
<div class="btn">0</div>
<div class="btn">-</div>
<div class="btn">/</div>
<div class="btn">.</div>
<div class="btn">*</div>
<div class="btn">C</div>
<div class="btn">=</div>
</div>
Viewport Height (vh): This unit is based on the height of the viewport. A value of 1vh is equal to 1% of the viewport height.
Height of container is 50vh and height of result is 10vh. This leaves 40vh for button panel, or 6.67vh per row. If you want to preserve container height, you can just add height: 6.67vh; to .kalkulator div. This will set button height to fixed value, so there won't be any empty space.

Can this be achieved with a flexbox or grid?

I am trying to achieve this effect using flexbox and without media query. Can i get this effect with the grid?
desktop -> tablet -> mobile:
Sure, You can achieve something like this using flexbox. I believe the easiest way to do this would be having a main container for all three blocks, and a sub-container for the first two blocks that'll move. Here's a working example:
.container, .subcontainer {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.container {
width: 100%;
}
.subcontainer {
width: 50%;
min-width: 125px;
}
#block1, #block2, #block3 {
margin-bottom: 50px;
width: 100px;
height: 100px;
}
#block1 {
background-color: peru;
margin-right: 25px;
}
#block2 {
background-color: darkorange;
margin-right: -50px;
}
#block3 {
background-color: orange;
height: unset;
min-height: 100px;
display: flex;
align-items: stretch;
}
<div class="container">
<div class="subcontainer">
<div id="block1"></div>
<div id="block2"></div>
</div>
<div id="block3"></div>
</div>
Here's an alternate solution, using flexbox, grid, and #media queries. Easier to use flexbox on the 1st and 3rd picture because they have one dimension/direction. With the 2nd picture it's easier to use grid as it's more of a 2D layout that requires you to mind the horizontal and vertical of elements to set up properly.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
display: flex;
justify-content: space-evenly;
height: 80vh;
margin: 3rem;
}
.container>div {
border: 1px solid black;
display: grid;
width: 10rem;
place-content: center;
}
#media screen and (max-width: 992px) {
.container {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 1rem;
}
.b {
grid-column: 1/2;
grid-row: 2/3;
}
.c {
grid-column: 2/3;
grid-row: span 2;
}
}
#media screen and (max-width: 600px) {
.container {
display: flex;
flex-direction: column;
}
.container>div {
flex: 1;
}
}
<div class="container">
<div class="a">A</div>
<div class="b">B</div>
<div class="c">C</div>
</div>

100% total width for flex items in flex container, with gap inbetween

I am trying to position elements in a flexbox with flex-wrap with gap in between
Ideally the way this should be displayed is:
On the first row the blue box taking the full width no gaps anywhere
Second row Red box taking the first 33%, Green box taking the remaining 66%
There should be 12px gap between the 2 rows
There should be 12px gap between the Red and Green item without them going on the next row, so their widths should actually become 33% - 6px and 66% - 6px so that there is space left for the gap.
End result should look something like this:
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 0 12px 0 12px;
}
.item1 {
width:33%;
height: 200px;
background-color: red;
}
.item2 {
width:66%;
height: 200px;
background-color: green;
}
.item3 {
width: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
This a CSS grid use case where you don't need a lot of code and headaches:
.container {
max-width: 200px;
display: grid;
grid-template-columns: 1fr 2fr;
grid-auto-rows:200px;
gap: 12px;
}
.item1 {
background-color: red;
}
.item2 {
background-color: green;
}
.item3 {
grid-column: 1/-1;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
I have added a flex-direction: row, Removed the padding and added a calc function to the widths as well as upped the 66% to 67% to create 100% total.
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.item1 {
width: calc(33% - 6px);
height: 200px;
background-color: red;
}
.item2 {
width:calc(67% - 6px);
height: 200px;
background-color: green;
}
.item3 {
width: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
You can use flex-basis and flex-grow instead of width. See the example below...
.container {
max-width: 200px;
width: 200px;
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 0 12px 0 12px;
}
.item1 {
flex-basis: 33%;
height: 200px;
background-color: red;
}
.item2 {
flex-basis: 60%; /* Reduce this value abit and allow to auto grow */
flex-grow: 1; /* Last item will fill up the remaining space */
height: 200px;
background-color: green;
}
.item3 {
flex-basis: 100%;
height: 200px;
background-color: blue;
}
<div class="container">
<div class="item3"></div>
<div class="item1"></div>
<div class="item2"></div>
</div>
This is #Ben variant but improve calculations
.container {
--gap: 12px;
max-width: 200px;
width: 200px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--gap); // better use like veriable, you can edit it one time in all plays together
}
.item {
height: 200px; // we have pattern, we don't need repeat it
}
.item1 {
width: calc(33.33% - var(--gap) / 2); // improve calculation
background-color: red;
}
.item2 {
width: calc(66.66% - var(--gap) / 2);// improve calculation
background-color: green;
}
.item3 {
width: 100%;
background-color: blue;
}
<div class="container">
<div class="item item3"></div>
<div class="item item1"></div>
<div class="item item2"></div>
</div>

Responsive flexbox layout: reordering elements

I'm trying to achieve a responsive layout as outlined in the image, left side would be mobile, right side desktop. This would be relatively easy using flexbox if I could set a fixed height for the wrapper, but because the content is dynamic this is not possible.
Another solution would be to use position absolute on element C, but this seems very hacky, I'm hoping to find a more elegant solution.
Here is a framework for the code:
.wrapper {
display: flex;
flex-direction: column;
}
#media(min-width: 800px) {
.wrapper {
flex-direction: row;
flex-wrap: wrap;
}
}
.section {
display: flex;
justify-content: center;
align-items: center;
font-size: 36px;
}
.section-a {
background: green;
height: 200px;
}
#media(min-width: 800px) {
.section-a {
flex-basis: 33%;
}
}
.section-b {
background: yellow;
height: 400px;
}
#media(min-width: 800px) {
.section-b {
flex-basis: 66%;
}
}
.section-c {
background: blue;
height: 200px;
}
#media(min-width: 800px) {
.section-c {
flex-basis: 33%;
}
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>
Thanks for the help!
You can achieve this by using grid. I have simplified your code and removed unwanted css
.wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: 1fr 1fr;
grid-template-rows: [row1-start] auto [row2-start] auto [row2-end];
}
.section {
padding: 20px;
display: flex;
align-items: center;
}
.section-a {
height: 50px;
background-color: green;
}
.section-b {
grid-row: row1-start / row2-end;
grid-column: 2/-1;
background-color: yellow;
}
.section-c {
background-color: blue;
height: 180px;
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>
Working fiddle here
Edit: it's an attempt, as I misunderstood OP's question. My answer has a given .wrapper height.
A solution using flex with the following properties:
order
flex-basis
This code applies for your desktop layout:
.wrapper {
width: 100%;
height: 100vh;
display: flex;
flex-flow: column wrap;
}
.section {
width: 50%;
border: 1px solid black;
box-sizing: border-box;
}
.section-a {
height: 25vh; // for example
}
.section-b {
order: 3;
flex-basis: 100%;
}
.section-c {
flex-grow: 1;
}
<div class="wrapper">
<div class="section section-a">A</div>
<div class="section section-b">B</div>
<div class="section section-c">C</div>
</div>

Flex: Align all children at flex-start but force one child to end?

I have a flex container with 3 children, and I am wanting to ensure that the children all align at flex-start, however the final child should sit at the bottom of the container.
Is it not possible to combine align-content with align-self?
.container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
padding: 15px 15px 50px 15px;
height: 100%;
background: red;
width: 200px;
height: 500px;
}
.item-1,
.item-2,
.item-3 { width: 100%; }
.item-3 {
align-self: flex-end;
}
<div class="container">
<div class="item-1">One</div>
<div class="item-2">Two</div>
<div class="item-3">Three</div>
</div>
Since you want 100% width of your element, you can switch to column direction then use margin to control alignment:
.container {
display: flex;
flex-direction: column;
padding: 15px 15px 50px 15px;
height: 100%;
background: red;
width: 200px;
height: 300px;
}
.item-3 {
margin-top: auto;
}
<div class="container">
<div class="item-1">One</div>
<div class="item-2">Two</div>
<div class="item-3">Three</div>
</div>

Resources