CSS: Refactor grid to a flex layout with different childs - html

I'm currently trying to build a flex layout which should have 3 possible childs:
If the wrapper has just one button, it should have the full width of the wrapper.
If the wrapper has two childs, the last one should have a max width of 75px and the first one must fill the available space.
If the wrapper has at all three childs, the first row should be like at the example before but the last child should be in the next row with 100% width.
I've build this grid layout but I need change it to flex because of the compatibility of some browsers. How can I do this? I've tried a lot but the result is always bad.
.wrapper {
width: 60%;
display: grid;
display: -ms-grid;
grid-template-columns: 1fr 75px;
text-align: center;
position: relative;
margin-bottom: 20px;
}
.wrapper div+div {
margin-left: 6px;
}
.wrapper div {
background-color: yellow;
padding: .6em 1em;
text-align: center;
}
.wrapper div.show.single-button {
grid-column: 1/span 2;
}
.wrapper div.cancel {
grid-column: 1/span 2;
-ms-grid-row: 2;
-ms-grid-column-span: 2;
margin-top: 6px;
margin-left: 0;
}
<div class="wrapper">
<div class="show single-button">Show</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
<div class="cancel">Cancel</div>
</div>

Judicious use of flex:1 and flex-wrap seems to work.
* {
box-sizing: border-box;
}
.wrapper {
width: 60%;
margin: auto;
display: flex;
flex-wrap: wrap;
margin-bottom: 20px;
justify-content: space-between;
border: 1px solid green;
}
.wrapper div {
background-color: lightblue;
padding: .6em 1em;
text-align: center;
margin: 6px;
}
.wrapper div.show {
flex: 1;
}
.wrapper div.invoice {
flex: 1;
max-width: 75px;
}
.wrapper div.cancel {
flex: 1 1 100%;
}
<div class="wrapper">
<div class="show single-button">Show</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
</div>
<div class="wrapper">
<div class="show">Show</div>
<div class="invoice">Invoice</div>
<div class="cancel">Cancel</div>
</div>

Flexbox offers everything you need for this. No need to hack around by mixing flexbox with box-model or width properties:
.wrapper {
height: 100px;
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.wrapper > div {
flex: 1;
border: 1px dotted #999;
background-color: aliceblue;
}
.wrapper > div:nth-child(2) {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 75px;
-webkit-flex-basis: 75px;
}
.wrapper > div:nth-child(3) {
flex-shrink: 0;
flex-grow: 0;
flex-basis: 100%;
-webkit-flex-basis: 100%;
}
<div class="wrapper">
<div>1</div>
</div>
<div class="wrapper">
<div>1</div>
<div>2</div>
</div>
<div class="wrapper">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

You can use flex-basis to tell the initial width of the elements and flex-grow to tell if they should take more space, and flex-wrap so it does not force them to fit in one line, something like:
.wrapper {
display: flex;
flex-wrap: wrap; // make it multiline
margin-bottom: 1rem;
}
.wrapper div {
background: yellow;
margin: .2rem;
padding: .2rem;
text-align: center;
}
.invoice {
flex-basis: 75px; // make it have 75px
flex-grow: 0; // can't grow
flex-shrink: 0; // can't shrink
}
.show {
flex-grow: 1; // no basis width, just grow and take the space left from .invoice
}
.cancel {
flex-basis: 100%; // takes the whole width so it must go on a single row
}
https://codepen.io/anon/pen/KJLqVj

Related

wrap and stretch inputs on form resize

I have two inputs on form in one row. First one should be stretching on form resize, second is of fixed width. But when form is narrowed to a particular breakpont, second input should wrap to second line and stretch as well as first one.
Is it possible to achieve using CSS?
Tried using grid, but it won't wrap at all.
When using flexbox the result is better, but still have to set flex-grow for second input and it's width is not fixed, while inputs are in one row
.box {
background: grey;
padding: 20px 20px 20px 20px;
}
.wrapper {
display: grid;
grid-column-gap: 6px;
grid-row-gap: 6px;
grid-template-columns: minmax(320px, 1fr) 200px;
}
.flexWrapper {
display: flex;
flex-wrap: wrap;
}
.flex1 {
flex-basis: 320px;
flex-grow: 10;
flex-shrink: 0;
}
.flex2 {
flex-basis: 200px;
flex-shrink: 0;
flex-grow: 1;
}
<div class="wrapper">
<div class="box">One</div>
<div class="box">Two</div>
</div>
<div class="flexWrapper">
<div class="flex1 box">One</div>
<div class="flex2 box">Two</div>
</div>
https://codepen.io/C4off/pen/WNKjJaK
The easiest way to do this is to use a media query. At 600px I've reset the wrapper to be display: block with the children at 100% width which forces them to stack on top of each other. I've set the width of flex2 to 200px to fix it at that.
.box {
background: grey;
padding: 20px 20px 20px 20px;
}
.flexWrapper {
display: flex;
padding-top: 12px;
column-gap: 6px;
row-gap:6px;
}
.flex1 {
flex: 1;
}
.flex2 {
width: 200px;
}
#media only screen and (max-width: 600px) {
.flexWrapper {
display: block;
}
.flex1, .flex2 {
width: 100%;
}
}
<div class="container">
<div class="flexWrapper">
<div class="flex1 box">One</div>
<div class="flex2 box">Two</div>
</div>
</div>
Using flexbox only and the min-width proprty. Note .flex2 will overflow at container widths less than 200px
.container {
width: 60%;
outline: 1px solid red;
}
.box {
background: grey;
padding: 20px 20px 20px 20px;
}
.flex-wrapper {
display: flex;
padding-top: 12px;
column-gap: 6px;
row-gap:6px;
flex-wrap: wrap;
}
.flex1 {
flex: 2 0;
}
.flex2 {
min-width: 200px;
flex-grow: 1;
}
<div class="container">
<div class="flex-wrapper">
<div class="flex1 box">One</div>
<div class="flex2 box">Two</div>
</div>
</div>
The final way this can be done is using container queries which are quite well supported now. The max size is applied to the container and not the screen as the example below
* {
box-sizing:border-box;
}
.container {
container-type: inline-size;
container-name: my-container;
width: 60%;
}
.box {
background: grey;
padding: 20px 20px 20px 20px;
}
.flex-wrapper {
display: flex;
padding-top: 12px;
column-gap: 6px;
row-gap:6px;
}
.flex1 {
flex: 1;
}
.flex2 {
width: 200px;
}
#container my-container (max-width: 600px) {
.container {outline: 1px solid red;}
.flex-wrapper {
display: block;
}
.flex1, .flex2 {
width: 100%;
}
}
<div class="container">
<div class="flex-wrapper">
<div class="flex1 box">One</div>
<div class="flex2 box">Two</div>
</div>
</div>

Shrink grid container to number of rows

I have a flexbox with a grid and a div in it, and I'd like to collapse the grid container's height to the height of the rows, so that the buttons below it are just below the grid items. The number of rows is also dynamic, because I'm using grid-template-columns: repeat(auto-fit, minmax(250px, 1fr). I can set a max-height of the grid items, like in this image, but that only makes the items smaller and doesn't make the grid container any shorter.
I've tried changing the flexbox they're in so the flex-direction is row, and set flex-wrap to wrap, but that causes other problems and overlapping text when the window size changes. Setting the height or max-height of the grid container to fit-content seems to do nothing as well.
Here is what I have:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Boardgame Database</title>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
aside {
background-color: red;
flex: 1;
min-width: 250px;
}
.grid-container {
flex: 4;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.grid-item {
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
font-size: 24px;
text-align: center;
overflow: hidden;
min-height: 100px;
}
#main-container {
display: flex;
flex-direction: row;
min-height: 100vh;
}
#section-container {
display: flex;
flex-direction: column;
width: 100%;
}
#page-buttons {
height: 50px;
text-align: center;
font-size: 20px;
}
</style>
</head>
<body>
<div id="main-container">
<aside class="sidebar">
</aside>
<div id="section-container">
<section class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
</section>
<div id="page-buttons">
first
prev
page
next
last
</div>
</div>
</div>
</body>
</html>
The style
.grid-container {
flex: 4;
}
is equivalent to flex-grow: 4;
so it makes the container grow. Just remove it and it will keep its dimension
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
aside {
background-color: red;
flex: 1;
min-width: 250px;
}
.grid-container {
/* flex: 4; */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.grid-item {
border: 1px solid rgba(0, 0, 0, 0.8);
padding: 20px;
font-size: 24px;
text-align: center;
overflow: hidden;
min-height: 100px;
}
#main-container {
display: flex;
flex-direction: row;
min-height: 100vh;
}
#section-container {
display: flex;
flex-direction: column;
width: 100%;
}
#page-buttons {
height: 50px;
text-align: center;
font-size: 20px;
}
<body>
<div id="main-container">
<aside class="sidebar">
</aside>
<div id="section-container">
<section class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
</section>
<div id="page-buttons">
first
prev
page
next
last
</div>
</div>
</div>
</body>

Flexbox layout multiple items wrapping around a big one

I want to have a layout like the following image using flexbox:
I have tried the following but no luck so far:
.flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 200px;
}
.flex-item:not(:first-child) {
width: calc(100% - 50px);
background-color: gray;
border: 1px solid blue;
}
.flex-item:first-child {
width: 50px;
height: 100%;
border: 1px solid blue;
background-color: red;
}
<div class="flex-container">
<div class="flex-item">item 1(long)</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>
I need the DOM structure to be like this, otherwise, I already know how to achieve this with nested elements.
It's possible using your DOM structure but I doubt it will be particularly useful, and as others have said, you're better off using Grid or even just inline elements. The trouble is you are trying to mix two layout contexts and flexbox isn't really built for that.
Give the container a explicit height and the wider elements a flex:1 property, which will make them grow to fill the remaining space. I used nth-child as it is more readable to me and your original code didn't actually behave as you intended when put into codepen.
.flex-container {
border: 1px solid green;
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 200px;
height: 200px;
}
.flex-item {
height: 100%;
width: 50px;
background-color: gray;
border: 1px solid blue;
}
.flex-item:nth-child(n+2) {
width: calc(100% - 50px);
flex: 1;
border: 1px solid blue;
background-color: red;
}
<div class="flex-container">
<div class="flex-item">item 1(long)</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>
CSS Grid Layout would be more appropriate.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: grid;
width: 90%;
height: 50%;
margin: 2rem auto;
text-align: center;
grid-template-columns: 25% 75%;
grid-template-rows: 25% 25% 50%;
}
.flex-item1 {
grid-column: 1 / 2;
grid-row: 1 / 4;
background: pink;
}
.flex-item2 {
grid-column: 2 / 3;
grid-row: 1 / 2;
background: lightcoral;
}
.flex-item3 {
grid-column: 2 / 3;
grid-row: 2 / 3;
background: lemonchiffon;
}
.flex-item4 {
grid-column: 2 / 3;
grid-row: 3 / 4;
background: lightblue;
}
<div class="flex-container">
<div class="flex-item1">item 1</div>
<div class="flex-item2">item 2</div>
<div class="flex-item3">item 3</div>
<div class="flex-item4">item 4</div>
</div>
Although you can do it with Flexbox.
It makes more sense to me to make a small change to the DOM structure.
Your items 2, 3, 4 would be wrapped in an other Flex container.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
display: flex;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: flex;
flex-wrap: wrap;
width: 90%;
height: 50%;
margin: 1rem;
text-align: center;
}
.flex-itemA {
width: 25%;
background: pink;
}
/* Flex-itemB and Flex container itself for the next 3 elements */
.flex-itemB {
width: 75%;
display: flex;
flex-wrap: wrap;
flex-direction: column;
background: lightblue;
}
.flex-itemB1,
.flex-itemB2 {
height: 25%;
}
.flex-itemB1 {
background: lightcoral;
}
.flex-itemB2 {
background: lemonchiffon;
}
<div class="flex-container">
<div class="flex-itemA">item A</div>
<div class="flex-itemB">
<div class="flex-itemB1">item B1</div>
<div class="flex-itemB2">item B2</div>
<div class="flex-itemB3">item B3</div>
</div>
</div>
EDIT based on #lawrence-witt answer
Or if you really want to keep the DOM structure that way and use Flexbox and not Grid. Then you could do it like #lawrence-witt suggested.
I kept the :nth-child selectors although it would be easy to add a class for each element and avoid increasing the specificity.
body {
background: white;
color: #323232;
font-weight: 300;
height: 100vh;
margin: 0;
font-family: Helvetica neue, roboto;
}
.flex-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 90%;
height: 50%;
margin: 2rem 5%;
text-align: center;
}
.flex-item {
height: 100%;
width: 25%;
background: pink;
}
.flex-item:nth-child(n + 2) {
width: 75%;
/* This will make the height = 25% since the last element will have flex-grow: 1 */
flex: 0.5;
}
.flex-item:nth-child(2) {
background: lightcoral;
}
.flex-item:nth-child(3) {
background: lemonchiffon;
}
.flex-item:nth-child(4) {
flex: 1;
background: lightblue;
}
<div class="flex-container">
<div class="flex-item">item 1</div>
<div class="flex-item">item 2</div>
<div class="flex-item">item 3</div>
<div class="flex-item">item 4</div>
</div>

Three divs in row, last one in another column horizontally centered - Flexbox or Grid

I have a grid container and I want to align three divs like this, also doing them responsive (all stacked). I don't have the heights of the divs.
It would be two columns, in one two rows (two divs one below another), in another column a div centered vertically having in mind the height of the two first divs.
I can use grid or flexbox.
Thanks
Using Flexbox:
.wrapper {
width: 400px;
border: 2px solid black;
height: 300px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: center;
align-items: center;
}
.child {
height: 100px;
width: 150px;
border: 1px solid black;
margin-top: 20px;
margin-bottom: 20px;
}
<div class="wrapper">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
and also grid :
Example from2 columns and the third element spanning through 2 rows and margin itself in the middle.
section {
display: grid;
grid-template-columns: repeat(2, minmax(270px, 1fr));/* or any value you need */
grid-gap: 2em;/* or any value you need */
padding: 2em;/* or any value you need */
counter-reset: divs; /*demo*/
width:max-content;/* or any value you need */
margin:auto;/* or any value you need */
}
div {
border: solid red;
min-height: 30vh;/* or any value you need */
width: 270px;/* or any value you need */
display: flex; /* demo*/
}
div {
margin-left: auto;
}
div:nth-child(3) {
grid-column: 2;
grid-row: 1 / span 2;
margin: auto 0;
}
/*demo*/
div:before {
counter-increment: divs;
content: counter(divs);
margin: auto;
font-size: 3em;
}
<section>
<div></div>
<div></div>
<div></div>
</section>
To play with the grid system, you can use : https://css-tricks.com/snippets/css/complete-guide-grid/ / http://gridbyexample.com/ and https://codepen.io/pen/ for the playground.
Here's a jsfiddle: https://jsfiddle.net/5csL2dqy/
.container {
display: flex;
flex-direction: row;
width: 100%;
}
.right {
display: inherit;
align-items: center;
}
.a, .b, .c {
box-sizing: border-box;
width: 150px;
margin: 20px;
border: 1px solid black;
}
<div class="container">
<div class="left">
<div class="a">
<p>
First div
</p>
</div>
<div class="b">
<p>
second div
</p>
</div>
</div>
<div class="right">
<div class="c">
<p>
Third div
</p>
</div>
</div>
</div>
You use the following inline-flex styles
#media only screen and (min-width: 600px) {
.container {
display: inline-flex;
}
.container div {
border: 1px solid red;
padding: 1em;
margin: 1em;
}
.container>div+div {
margin: auto;
}
}
#media only screen and (max-width: 600px) {
.container div:not(first-child) {
border: 1px solid red;
}
}
<div class="container">
<div>
<div>
1
</div>
<div>
2
</div>
</div>
<div>
3
</div>
</div>
This is one of only two answers with equal width/height gaps. G-Cyr's is the other:
.grid{
display: grid;
grid-template-columns: repeat(9,1fr);
grid-template-rows: repeat(7, 1fr);
height: 90vh;
width: 120vh;
}
.grid > div{
border: solid 3px orangered;
font: 26px sans-serif;
display: flex;
align-items:center;
justify-content:center;
}
.grid > div:nth-child(1){
grid-row: 1/span 3;
grid-column: 1/span 4;
}
.grid > div:nth-child(2){
grid-row: 3/span 3;
grid-column: 6/span 4;
}
.grid > div:nth-child(3){
grid-row: 5/span 3;
grid-column: 1/span 4;
}
<div class="grid">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

How to create a web page using 100vh to be responsive

I need to create a web page that uses 100vh, or takes up 100% of the screen height and width (no scroll). I created a container(height:100vh) that holds everything in it, and within that container, I need everything in there to be responsive.
Design concept:
The outer container height is 100vh and I need the inner container to be responsive:
#root {
position: relative;
height: 100vh;
overflow: hidden;
}
#root-inner-container {
width: 100%;
}
The problem I run into is by using 100vh, the content inside the container does not stay responsive and tends to overflow.
Jsfiddle to what I have so far: https://jsfiddle.net/fm6hmgpk/
Flex Solution
body {
margin: 0;
color: white;
}
.container {
background: grey;
width: 100vw;
height: 100vh;
}
.navbar {
height: 15vh;
background: darkblue;
margin: 0 10px;
}
.bottom {
background: lightgrey;
height: 85vh;
margin: 0 10px;
display: flex;
}
.left-bottom {
background: red;
width: 70%;
display: flex;
flex-direction: column;
}
.right-bottom {
flex: 1;
background: lightgrey;
}
.content-list {
display: flex;
height: 80%;
background: maroon;
}
.text {
flex: 1;
background: green;
}
.content {
width: 80%;
background: orange;
}
.list {
flex: 1;
}
<div class="container">
<div class="navbar">
NAVBAR
</div>
<div class="bottom">
<div class="left-bottom">
<div class="content-list">
<div class="content">
CONTENT
</div>
<div class="list">
LIST
</div>
</div>
<div class="text">
TEXT
</div>
</div>
<div class="right-bottom">
IMAGE
</div>
</div>
</div>
Calculation: (For scrolling issue)
<body> default margin:8px; and your border:2px solid black;
Sums up to 10px so we need to deduct twice of 10px
Hence height: calc(100vh - 20px);
EDIT:
To make it responsive you need to get rid of fixed px value to your li
li {}
#root {
display: flex;
position: relative;
height: calc(100vh - 20px);
border: 2px solid black;
}
#root-inner-container {
flex: 1;
width: 100%;
display: flex;
}
.app-container {
flex: 1;
display: flex;
}
.div-1,
.div-2 {
flex: 1;
display: flex;
}
ul {
flex: 1;
display: flex;
flex-direction: column;
}
li {
flex: 1;
border: 1px solid red;
}
<div id="root">
<div id="root-inner-container">
<div class="app-container">
<div class="div-1">
<ul>
<li>div 1 - One</li>
<li>div 1 - Two</li>
<li>div 1 - Three</li>
<li>div 1 -Four</li>
</ul>
</div>
<div class="div-2">
<ul>
<li>div 2 - One</li>
<li>div 2 - Two</li>
<li>div 2 - Three</li>
<li>div 2 -Four</li>
</ul>
</div>
</div>
</div>
</div>
You may want to consider using grid. For browser support you can check here.
To learn about using grid, check here.
body {
margin: 0
}
#root {
display: grid;
grid-gap: 10px;
grid-template-columns: 3fr 1fr 3fr;
grid-template-rows: 1fr 3fr 1fr;
background-color: #fff;
color: #444;
width: 100vw;
height: 100vh;
}
.box {
background-color: #444;
color: #fff;
border-radius: 5px;
padding: 20px;
font-size: 150%;
}
.navbar {
grid-column: 1 / 4;
grid-row: 1;
}
.content {
grid-column: 1;
grid-row: 2 / 3;
}
.list {
grid-column: 2;
grid-row: 2;
}
.image {
grid-column: 3 / 4;
grid-row: 2 / 4;
}
.text {
grid-column: 1 / 3;
grid-row: 3 / 4;
}
<div id="root">
<div class="navbar box">Navbar</div>
<div class="content box">Content</div>
<div class="list box">List</div>
<div class="image box">Image</div>
<div class="text box">Text</div>
</div>
I think it's because your li height is fixed height so if your root height is less than the sum of those li height it will overflow. you can use vh for them too.