Responsive grid layout with perfectly squared cells - html

I have a <section> element which must not be larger than 100vh and no wider than 100vw. Ideally it should follow its parent containers dimensions.
The <section> should have a grid layout using css variables; var(--cols, 56) and var(--rows, 32)
Each cell in the grid must be 1:1 ratio, meaning that the cell width must always === cell height.
The <section> element should grow if the viewport (parent element) grows, unless this makes the cells not square. If this is the case, the <section> element should be centered. So if the viewport width becomes larger and viewport height becomes smaller, the <section> is centered horizontally. But if the viewport height becomes larger and viewport width becomes smaller, the <section> element is centered vertically.
This is what I have so far:
<section #widgets>
<ng-container *ngFor="let widget of widgetConfig">
<ng-container [libTileContainer]="widget"></ng-container>
</ng-container>
</section>
section {
display: grid;
gap: 5px;
grid-template-columns: repeat(
var(--cols, 56),
calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
);
grid-template-rows: repeat(
var(--rows, 32),
calc(((100vw - (var(--cols, 56) * 5px) - 2rem) / (var(--cols, 56))))
);
max-width: 100%;
max-height: 100%;
}
This respects the cells squareness, but it bleeds out of it's parent.
The calculations will also only respect viewport width and not height.
How can I achieve what I want?

To set the height and width of grid items you might use a strategy that calculates both the height and width according to how many columns and rows are supposed to be fit inside the available space and then just use the lesser of the two on both the properties.
https://developer.mozilla.org/en-US/docs/Web/CSS/min
The min() CSS function lets you set the smallest (most negative) value
from a list of comma-separated expressions as the value of a CSS
property value.
In this demo I slightly refactored some choices:
I removed the default value for the number of columns to simplify the
code.. anyway it could defined as an other custom property
I use the function min to determine which one is lower between the
candidates height and width
I used place-content to center the items by default
I used grid-auto-rows to determine the size of rows because it's not
important here to tell also how many
I told the div to take the whole width and height of their container
const items = 20;
const cols = 5;
const rows = 4;
initSection(items, cols, rows);
function initSection(items, cols, rows){
const parent = document.querySelector('section');
for(i=0;i<items;i++){
const item = document.createElement('div');
item.innerText = i+1;
parent.append(item);
}
parent.dataset.col = cols;
parent.dataset.col = rows;
}
:root{
--cols: 56;
--rows: 32;
}
*{
box-sizing: border-box;
}
html, body{
margin: 0;
overflow: hidden; /*just to make sure the content doesn't overflow the viewport*/
}
section::before{
/*this to show that the same dynamic strategy here worked instead*/
--cols: attr(data-cols);
--rows: attr(data-rows);
position: absolute;
content: var(--cols) 'x' var(--rows);
border: dashed 4px gray;
padding: 0 .2em;
font-size: 1.5rem;
top: .2em;
left: .4em;
}
section {
/*!this didn't work :(*/
/*
--cols: attr(data-cols number, 56);
--row: attr(data-rows number, 32);
*/
--cols: 5;
--rows: 4;
--gap: 5px;
--col-size: calc(((100vw - (var(--cols) * var(--gap))) / (var(--cols))));
--row-size: calc(((100vh - (var(--rows) * var(--gap))) / (var(--rows))));
--size: min(var(--col-size), var(--row-size));
display: grid;
place-content: center;
gap: var(--gap);
grid-template-columns: repeat( var(--cols), var(--size));
/*grid-template-rows: repeat( var(--rows), var(--size));*/
grid-auto-rows: var(--size);
width: 100vw;
height: 100vh;
}
section{
border: solid red;
}
section div {
border: solid;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 2rem;
}
<section data-cols="5" data-rows="4">
<!--
<div>1</div>
...
-->
</section>

Related

Grid setup in CSS?

I am new to CSS and HTML and have a setup of divs in CSS, something like this:
#topBar {
margin-top: 0;
height: 100px;
width: 100%
}
#sideBar {
width: 50px;
margin-left: 0;
margin-top: 100px;
height: 100%;
}
#main {
margin-left: 50px;
margin-top: 100px;
margin-bottom: 50px;
}
#footer {
margin-bottom: 0;
width: 100%;
}
<div id="container">
<div id="topbar" />
<div id="sidebar" />
<div id="main" />
<div id="footer" />
</div>
But that does not look anything like how I want it. It leaves space for every div, even though their space is restricted to x width and x height.
How could I set up divs to look as desired? Ie have a footer, main, sidebar, and topbar in CSS?
CSS actually has built in grid "builder" that you can use. I was doing something similar not long ago and ended up doing it like this:
#container {
display: grid; //uses grid
height: 100vh; // vh and vw is percentages of the screens width and height, nice for scaling for different devices
width: 100vw;
grid-template-columns: 1fr 9fr; // sets how big columns are, this sets the column quantity to two, and the first one gets 1 fraction of the are, and column two gets 9 fractions. 1fr is for sidebar
grid-template-rows: 1.5fr 15fr 3fr; // Same as with column, but this includes footer, so 1.5 fraction goes to top bar, 15 fractions to sidebar and main area, and 3 fractions to footer
grid-template-areas:
"header header" // sets area to use, the same area given space in above lines. They can be directly referenced in other parts of the css documents.
"navbar main"
"footer footer";
}
#topbar {
grid-area: header; // Referencing previous made areas
display: flex; // I used this to make the top bar, as this would align the items in the bar horizontally with same amount of space between
justify-content: space-between;
align-items: center; //used this to center items vertically
}
#sidebar {
grid-area: sidebar;
text-align: center; // used this to align the text center horizontally
}
#main {
grid-area: main;
}
#footer {
grid-area: footer;
}
You should use the semantic tags such as the header, nav, aside, footer and main.
Then apply the grid directly to the body element instead of wrapping them in an extra container:
body {
margin: 0; /* removes default margin */
display: grid; /* uses grid */
min-height: 100vh; /* will expend the grid to the entire viewport */
grid-template-columns: 1fr 2fr; /* sets the column width and amount */
grid-template-rows: min-content auto min-content; /* sets the row height to push the footer at the bottom and let the main fill the rest */
gap: 5px; /* placing the items apart */
}
header,
footer {
grid-column: 1 / -1; /* letting those element span the entire row */
}
/* for styling purpose only */
header,
aside,
main,
footer {
border: 2px dashed red;
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
}
<header>Topbar</header>
<aside>Sidebar</aside>
<main>Main</main>
<footer>Footer</footer>

How to calculate the actual width of child elements of a flexbox container?

I have such html and css code. I have two questions:
When the window width is 600px, in theory, the left box should be of 150px width, because it's the 25% of the parent(600px), but the width is 120px.
The right box can not reach 100vw. It just takes the widthOfParent - widthOfLeft. I suppose it should somehow overflow and reach 100vw.
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
* {
margin: 0;
padding: 0;
}
.container {
height: 300px;
/* your code here */
display: flex;
justify-content: start;
}
.left {
background-color: #f44336;
height: 100%;
width: 25%;
min-width: 100px;
}
.right {
background-color: #2973af;
height: 100%;
width: 100vw;
}
codesanbox: https://codesandbox.io/s/admiring-darkness-cu99x?file=/src/styles.css:0-293
You are facing the shrink effect. In all the cases the total width 100vw + 25% is bigger than 100% so both items will shrink equally.
Since your container is also full width, the overflow will always be equal to 25% or 25vw. Both element have the default shrink value 1 so we have a sum equal to 125vw.
The first element width will be equal to: 25vw - (25vw * 25vw/125vw) = 20vw
and the width of the second item will be: 100vw - (25vw * 100vw/125vw) = 80vw
You can logically see that the total is 100vw (20vw + 80vw) and when the screen width is equal to 600px, 20vw is equal 120px.
To avoid this, disable the shrink effect on the first item by setting flex-shrink:0
* {
margin: 0;
padding: 0;
}
.container {
height: 300px; /* your code here */
display: flex;
}
.left {
background-color: #f44336;
width: 25%;
min-width: 100px;
flex-shrink:0;
}
.right {
background-color: #2973af;
width: 100vw;
}
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
Related: Why is a flex item limited to parent size?
use "flex" property instead of width for the flex items
.container {
height: 300px;
width: 600px; /* added fixed width for testing */
display: flex;
justify-content: start;
}
.left {
background-color: #f44336;
min-width: 100px;
flex: 1; /* 1/4 ratio within the parent element, as the other flex item in the parent is "flex: 3;" */
}
.right {
background-color: #2973af;
flex: 3; /* 3/4 ratio within the parent element, as the other flex item in the parent is "flex: 1;" */
}
The right element cannot take 100% of the width, as it is together with the left div inside the flex parent and we assigned the width parameter as "flex: value" for both
CSS flex Property
https://www.w3schools.com/cssref/css3_pr_flex.asp

Understanding auto in css grids [duplicate]

This question already has answers here:
Percentage Height HTML 5/CSS
(7 answers)
Closed 2 years ago.
I am pretty new to Web development and I am trying to learn CSS grids. While learning the CSS grid I tried to make one simple layout. It has one header section, one menu section, one sidebar section, and one footer section.
I used auto while defining grid template rows for the 2nd row, and gave conatiner height as 100%, so that 2nd row will stretch fully in the remaining space left by row 1 and 2.
But it didn't work that way, i am trying to figure out why 2nd row is not stretching vertically in the remaning space left.
Here is the conatiner css in which i defined the 2nd row as auto and conatiner height as 100%.
.container {
height: 100%;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 40px auto 40px;
}
fiddle link:
https://jsfiddle.net/791vtd4z/
That is because you did not give body a fixed height, yet you have .container a relative height: therefore, when the child .container simply stretches to its content height and not any further, since there's nothing absolute to compare against by using 100% (ask yourself: "100% of what?").
A solution will be to set .container { min-height: 100vh; } to fix that, which tells the element to at least be as tall as the viewport, and allow it to grow should the content inside menu or sidebar grow beyond what the viewport can contain.
* {
margin: 0;
top: 0;
bottom: 0;
font-family: sans-serif;
font-size: 1.2em;
}
title {
display: none;
}
.container {
min-height: 100vh;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 40px auto 40px;
}
.Header {
background-color: beige;
grid-column: 1/-1;
}
.Menu {
background-color: red;
}
.Sidebar {
background-color: burlywood;
grid-column: 2/-1;
}
.Footer {
background-color: aquamarine;
grid-column: 1/-1;
}
<div class="container">
<div class="Header">Header</div>
<div class="Menu">Menu</div>
<div class="Sidebar">Sidebar</div>
<div class="Footer">Footer</div>
</div>
To build on Terry's answer, you can achieve your desired result by giving body a height of 100vh, you could change the height of .container to 100vh, or you could give html and body a height of 100% (and keep the 100% height of .container).
This is because 100vh gives an element the full height of the viewport regardless of the height of its parents, while setting an element's full height using a percentage (i.e. 100%) means the element takes the full height of its parent, whatever that is. So an element with a height of 100% could still be zero, if its parent has no height.
To put this another way, when setting an element's height to 100% all of its parents need to be 100% as well for that element to take up the full viewport.
html, body{
height: 100%;
}
* {
margin: 0;
top: 0;
bottom: 0;
font-family: sans-serif;
font-size: 1.2em;
}
title {
display: none;
}
.container {
height: 100%;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: 40px auto 40px;
}
.Header {
background-color: beige;
grid-column: 1/-1;
}
.Menu {
background-color: red;
}
.Sidebar {
background-color: burlywood;
grid-column: 2/-1;
}
.Footer {
background-color: aquamarine;
grid-column: 1/-1;
}
<div class="container">
<div class="Header">Header</div>
<div class="Menu">Menu</div>
<div class="Sidebar">Sidebar</div>
<div class="Footer">Footer</div>
</div>

Flexbox exceeds its maincontainer in width

Okay, so i have a wrapper with 3 columns where the middle container is of a fixed width of 595px. The left and the right has a 2:1 size ratio. But when i put content into the left container which has width auto it exceeds the main wrappers width (width of screen 100%);
How do i solve this problem. I dont want it start grow like that i just want the colums to be responsive but width different width ratios.
If it helps, im in a angular project and i have a mat tab group inside the left column. I stil dont think this is the issue since it shouldnt exceed its main width
Here's the scass im using:
.row {
display: flex;
width: 100%;
justify-content: space-between;
flex-direction: row;
& .column {
margin: 5px;
&.left {
flex: 2;
margin-left: 50px;
}
&.middle {
min-width: 595px !important;
max-width: 595px !important;
flex:initial;
}
&.right {
min-width: 350px !important;
flex: 1;
}
}
}

css grid layout - height of rows never works

I want to use percentages in my css for my react app but it's causing such a headache. Width % work fantastic all the time but the height % is always an issue. It seems almost like I HAVE to specify a height in pixels for it to work work unless the element contains children.
Am I missing something fundamental here. Will a div not set itself to 100% of the remaining height without child elements. The below code doesn't work despite me setting the align-items property to stretch.
.search-container{
display: grid;
grid-template-columns: auto 900px 300px auto;
grid-template-rows: 30% auto auto;
grid-gap: 20px;
}
.promo-container {
background-color:blue;
grid-column-start: 2;
grid-column-end: 3;
align-items: stretch;
}
.form-container {
background-color:blue;
grid-column-start: 3;
grid-column-end: 4;
align-items: stretch;
}
.results-header {
background-color:rgb(255, 94, 0);
grid-column-start: 2;
grid-column-end: 4;
height: 90px;
}
.refine-search {
background-color:blue;
grid-column-start: 2;
grid-column-end: 3;
height: 100%;
}
.results-container{
background-color:rgb(0, 255, 42);
grid-column-start: 2;
grid-column-end: 3;
}
<div className="search-container">
<div className="promo-container">
</div>
<div className="form-container">
</div>
<div className="results-header">
</div>
<div className="refine-search">
</div>
<div className="results-container">
</div>
</div>
You need to set the height of your parent elements.
html, body, div {
height: 100%;
width: 100%;
}
The problem with height is that you need to have some childrens in order to work. So if you want to see the blank space where is supposed to be the childrens ( the rest of the elements) you indeed have to set pixels, rem, fr, etc. Otherwise The grid won't allow the blank space to be there if there's nothing in there. For example if I want to see my remaining space I can say:
`.someClass {
display: grid;
/* I'm setting the rows to be 3 row with a given space of 1 fr
each row will be placed with given space unit */
grid-templates-rows: repeat(3, 1fr);
grid-template-columns: repeat(5, 1fr);
}`
Otherwise if you want to create the grid depending automatically on the availaible space you can set everything to auto like so:
.someotherClass {
display: grid;
/* grid template areas is the combination of the template rows and columns */
grid-templates-areas: auto;
}
Of course with the auto I can still order my items on the rows and columns that I want but the space will be adjusted accordingly with each element
To give you some idea how the grid works check out this page: https://cssgridgarden.com/
Hope helps :)