Ive got a CSS grid that's two columns, five rows (at a display above 768px).
All the rows are set to "min-content" bar the last, being set to auto.
I've defined template grid areas, one for each "cell", with the exception of one that covers the 3rd to 5th row on the second column - named a6 (in the sample code)
When there is little or no content in a6, the grid behaves exactly as I desire. However, if when a bit more content is added to a6, the a5 and a7 areas expand in height, despite their content not changing.
CSS:
html,
body {
height: 100vh;
padding: 0;
margin: 0;
}
.maingrid {
height: 100%;
padding-left: 15px;
padding-right: 15px;
background-color: red;
display: grid;
grid-template-rows: min-content min-content min-content min-content min-content min-content min-content auto;
grid-template-areas: 'a1' 'a2' 'a3' 'a4' 'a5' 'a6' 'a7' 'a8';
grid-row-gap: .2em;
}
#media only screen and (min-width:768px) {
.maingrid {
grid-template-columns: 9fr 3fr;
grid-template-rows: min-content min-content min-content min-content auto;
grid-template-areas: 'a1 a2' 'a3 a4' 'a5 a6' 'a7 a6' 'a8 a6';
background-color: darkcyan;
}
}
.maingrid div {
background-color: black;
}
.a1 {
grid-area: a1;
background-color: pink !important;
}
.a2 {
grid-area: a2;
background-color: aliceblue !important;
}
.a3 {
grid-area: a3;
background-color: aqua !important;
}
.a5 {
grid-area: a4;
background-color: blue !important;
}
.a4 {
grid-area: a5;
background-color: brown !important;
}
.a6 {
grid-area: a6;
background-color: burlywood !important;
}
.a7 {
grid-area: a7;
background-color: chartreuse !important;
}
.a8 {
grid-area: a8;
background-color: darkorange !important;
}
HTML:
<main class="maingrid">
<div class="a1">BLAH</div>
<div class="a2">BLAH</div>
<div class="a3">BLAH</div>
<div class="a4">BLAH</div>
<div class="a5">BLAH</div>
<div class="a6">
at<br />at<br />
</div>
<div class="a7">BLAH</div>
<div class="a8">
<button type="button" onclick="BreakTheGrid();">click me :(</button>
</div>
</main>
JS (just to get the toggle button to work):
var isBroken = false;
function BreakTheGrid() {
if (!isBroken) {
$('.a6').html("the<br/>left<br />columns<br />have<br />expanded<br />boo!<br />");
} else {
$('.a6').html("no<br/>issue");
}
isBroken = isBroken == false;
}
Here's a JSFiddle replicating the issue: https://jsfiddle.net/up6afdj4/
If you click the button in a8, you can toggle the content of a6, thus toggling the issue.
I've only just started messing around with CSS grid, so I expect its something I've got completely wrong, but I can't figure it :)
By applying auto to the fifth row, which includes the a6 grid area, you trigger the Grid auto stretch algorithm, which distributes free space among rows covered by the grid area (spec §11.5, §11.5.1 and §11.8).
If you switch from auto to 1fr, the last row then consumes all free space, pinning the rows above to the top.
revised demo
For a more detailed explanation of auto space distribution, see my answers here:
Remove wide gaps in CSS Grid
How do you collapse unused row in a CSS grid?
(Illustrations generated by Firefox DevTools Grid Inspector.)
Changing auto to 1fr in the grid-template-rows definition solves the problem.
I don't know why however, and if someone could explain it better, I'll give you the accepted answer :)
Related
With Flex, I can wrap using flex-wrap. I'm experimenting with CSS grid and want to wrap an element but can't find a way to do that. Is it possible?
Here is my code
.item1 {
grid-area: topic;
}
.item2 {
grid-area: user;
}
.item3 {
grid-area: bin;
}
.bin-grid {
display: grid;
grid-template-columns: 200px auto;
grid-template-areas: 'topic topic topic' 'user bin bin';
gap: 0;
padding-bottom: 15px;
margin-bottom: 20px;
}
<div class="bin-grid">
<div class="item1">Header</div>
<div class="item2">Left</div>
<div class="item3">Right</div>
</div>
I tried using grid-template-columns:200px auto; but that isn't wrapping the elements. I need item2 and item3 to wrap.
After a link that was posted by a SO user for the same issue, I tried using grid-template-columns:repeat(auto-fill, 186px) but the elements will not wrap in my case. Yet, I can see the example works just fine. I'm not sure why it won't work in my case.
Is it possible to set a css grid row height based on if the grid area is being used?
.container {
height: 100vh;
grid-template-areas:
"mobile-head mobile-head mobile-head"
"nav app app"
"footer footer footer";
}
.nav {
grid-area: nav
}
<div class="container">
<div class="nav">Nav stuff</div>
</div>
Given the above grid template and html, I see the Nav stuff in the nav area, but naturally the mobile nav area is taking a 1/3rd of the screen height. Because there is no div that has a grid-area: mobile-head I want the height to be 0px.
But if I change the .nav to
...
.nav {
grid-area: mobile-head
}
I would want the row to be a fixed height of say 200px.
I can get close. I can get the row to have a height of 0 when there is nothing in the mobile-area by doing this:
...
grid-template-rows: minmax(0px, auto) 1fr 1fr
But then the height is dictated by the content, rather than the grid.
I'm fairly sure the only alternative to achieve what im trying to do is to use 2 grids, and media queries. (I want to place the nav on the left for desktop, or top if mobile)
I think grid-auto-rows: min-content is what you're after.
<button class="toggle" style="margin-bottom:1em">Toggle hidden row</button>
<div class="container">
<div class="nav">Nav stuff</div>
</div>
<style>
.container {
box-shadow: inset 0 0 0 1px lightgray;
background-color: #fafafb;
display: grid;
height: 100vh;
grid-auto-rows: min-content;
grid-template-areas:
"mobile-head mobile-head mobile-head"
"nav app app"
"footer footer footer";
}
.nav {
grid-area: nav;
background-color: lightblue;
}
.nav.mobile {
grid-area: mobile-head;
}
</style>
<script>
document.querySelector('.toggle').onclick = function(){
document.querySelector('.nav').classList.toggle('mobile');
};
</script>
I am trying to come up with a solution for advanced layout. I decided to use a css grid as it seemed to be the best match for my needs.
The requirements:
Header - consists of three elements - logo, search bar and menu
search bar should be aligned with the main content if space allows, i.e. on small screen search bar doesn't need to start where the main content starts but it should end where main content ends or take all available space
Main - consists of content and sidebar- should be centred and take max 100rem width space
content should be 2 times bigger than the sidebar
main content should have at least 1rem space from left and right
This is how it looks now. It matches my requirements on big screens (4k) but when the screen gets smaller it gets messy. I would really like to avoid any javascript and solve this with pure CSS if possible.
How would you approach this problem? I am now more inclining that this is not solvable with pure CSS and JS is needed. (Probably some resize observer on main-content element)
Examples:
Big screen -> alignment is correct ✔️
Small screen -> alignment is not correct. ❌ The search box should be within the black "brackets"
Even smaller screen -> alignment is not correct too. ❌ The search box should expand up to the black line
html,
body {
padding: 0;
margin: 0;
}
body {
font-family: sans-serif;
display: grid;
grid-template-columns: minmax(1rem, 1fr) minmax(min-content, 100rem) minmax(
1rem,
1fr
);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
". main .";
min-height: 100vh;
}
.header {
padding: 1rem 1rem;
background: green;
grid-area: header;
display: grid;
grid-template-columns: minmax(max-content, 1fr) minmax(min-content, 100rem) minmax(
max-content,
1fr
);
}
.search-box {
background: yellow;
max-width: calc(2/3 * 100%);
}
.logo {
background: yellowgreen;
min-width: 5rem;
}
.menu {
background: brown;
}
.main {
grid-area: main;
display: flex;
}
.main-content {
background: red;
flex: 2;
}
.sidebar {
background: blue;
flex: 1;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="header">
<div class="logo">Logo</div>
<div class="search-box">
Search box should should match main content position
</div>
<div class="menu">Menu with two submenus at least</div>
</header>
<main class="main">
<section class="main-content">Main content</section>
<aside class="sidebar">Sidebar</aside>
</main>
</body>
</html>
Sandbox available here
This is something that I've been struggling with for a while, but I can't seem to find a way to do it.
If you have an odd number of items in grid and you want 2 items per row (1fr 1fr), you end up with a single item in the last row that is left-centered.
I just want to make it centered so it looks nicer.
Here's a picture too.
You can try something like this jsfiddle:
/* visibility properties */
body {
width: 60%;
margin: 5% auto;
}
div {
margin: 3%;
width: 100px;
height: 100px;
background-color: black;
justify-self: center;
}
div:nth-of-type(2n) {
background-color: red;
}
/* actual code: */
section {
display: grid;
grid-template-columns: 1fr 1fr;
}
#last-div {
grid-column: 1 / span 2;
}
<section>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="last-div">
</div>
</section>
Get more info on CSS Grid: complete-guide-grid
You could try something like this since I faced a similar issue in one of my earlier projects.
grid-template-columns : repeat(auto-fit, minmax(<minSize>, 1fr));
Set minSize to whatever minimum width you want an element to occupy.
I am trying to achieve the below layout without using media queries. The reason I'd prefer to not use media queries is that this content is embedded on a site with a sidebar on the left that can expand and collapse. When it's expanded, it takes up about 400px, and it takes up maybe 50 when it's collapsed. It expands and collapses based off of a user interaction, not screen-width, so there's no good way for me to detect if it's open or not from CSS. Because of this, there could be some errors in my layout if I use a media query based on the screen width, instead of the actual width of the content. Is there any way I can use something like flex-basis or minmax to achieve this layout without media queries?
/* functional code */
.a1 {
grid-area: a1;
}
.a2 {
grid-area: a2;
}
.b1 {
grid-area: b1;
}
.grid {
display: grid;
grid-template-rows: 400px 200px 200px;
grid-template-columns: 1fr 300px;
grid-template-areas:
"a1 b1"
"a2 b1"
"a2 .";
}
/* Can I emulate this on the container width instead of the screen width? */
#media (max-width:1000px) {
.grid {
grid-template-columns: 1fr;
grid-template-areas: "a1"
"b1"
"a2";
grid-template-rows: 400px 600px 200px;
}
.b1 {
width:300px;
justify-self:center;
}
}
/* other styling */
.grid {
gap:8px;
}
.grid * {
border-radius: 5px;
background-color: lightgray;
display: grid;
place-items: center;
text-align: center;
}
.collapsed { display: none; }
#media (max-width:1000px) {
.uncollapsed {
display: none;
}
.collapsed {
display: block;
}
}
<div class="uncollapsed">Uncollapsed State (make screen smaller to see collapsed state)</div>
<div class="collapsed">Collapsed State (make screen bigger to see uncollapsed state)</div>
<div class="grid">
<div class="a1">
A1
<br>
Top-left normally
<br>
Top when collapsed
</div>
<div class="a2">
A2
<br>
Bottom-left normally
<br>
Bottom when collapsed
</div>
<div class="b1">
B1
<br>
Right side normally
<br>
Middle row when collapsed
</div>
</div>
FYI: I ended up just using a ResizeObserver polyfill and some JavaScript. Definitely not ideal, but it seems like container queries are currently impossible without JS.