CSS Grid Column Flexible for Collapsible Content - css

I am using CSS grid for my layout. 2 columns and three rows.
I would like the left column to be flexible so that the left column can contain a navigation bar that is collapsible, meaning that it opens/closes when I click a hamburger-menu button.
So far I have not found any examples.
Is it possible to make a column flexible from width 0 to any pixel width?

You can use min-content in grid-template-columns property and set width of sidebar div, then if you change width of this element, content div should fill the empty space. Check snippet below.
var collapsed = false
$('#btn').click(function() {
$('.sidebar').css('width', !collapsed ? '100px' : '150px')
collapsed = !collapsed
})
.grid-container {
display: grid;
grid-template-columns: min-content 1fr;
grid-template-rows: 1fr;
gap: 0px 0px;
grid-template-areas: "sidebar content";
width: 400px;
height: 300px;
}
.sidebar {
grid-area: sidebar;
background-color: red;
width: 150px;
}
.content {
grid-area: content;
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="grid-container">
<div class="sidebar">
<button id='btn'>collapse</button>
</div>
<div class="content">content</div>
</div>

Related

Grid item not expanding with inner content height

I have a menu grid layout with an inner item (in this case it's the .metadata div) that I want to expand and push down another item. See example here :
.wrapper {
display: grid;
grid-column-gap: 8px;
grid-row-gap: 4px;
grid-template-columns: 48px minmax(0px, 3fr) 1fr;
grid-template-rows: 24px 20px 44px;
grid-template-areas:
"icon title action-bar"
"icon metadata action-bar"
"tabs .... bottom-right";
padding: 16px 16px 0 16px;
}
.metadata {
grid-area: metadata;
display: flex;
align-items: baseline;
direction: ltr;
}
.innterTest {
height: 100px;
background-color: red;
}
.metadataItem {
display: flex;
}
.tabs {
grid-area: tabs;
grid-column-end: 3;
padding-top: 4px;
}
<div class="wrapper">
<div class="icon">icon
</div>
<div class="title">TITLE
</div>
<div class="action-bar">action bar
</div>
<div class="metadata">
<div class="metadataItem">
data node 1
<div class="innterTest">
testing
</div>
</div>
<div class="metadataItem">
data node 2
</div>
<div class="metadataItem">
data node 3
</div>
<div class="metadataItem">
data node 4
</div>
</div>
<div class="tabs">tabs
</div>
</div>
https://jsfiddle.net/c8wx2bgn/
If you inspect the outer .metadata wrapping div it seems to stay a small size. What I would like to happen is for it to expand and push down the .tabs grid item. The general grid layout has been working as I had hoped, but I've added more items inside metadata and want it to push tabs down when it expands.
I've tried enforcing a height on the metadata and metadata divs but this does not seem to effect the layout. New to grid so unsure what I am missing here.
You have grid-template-rows: 24px 20px 44px.
This means that the second row, which contains your metadata div, is limited in height to 20px.
Try this: grid-template-rows: 24px auto 44px.

Can't work out how to align logo and nav in a row using CSS grid

I'm trying to align a logo and navigation bar in one row across the top of a website using CSS grid.
I've written out the code but can't work out what I'm doing wrong as to why it's not working: https://codepen.io/chloewb/pen/wRRewQ
.logo{
grid-area: logo;
background:white;}
.navi{
grid-area: navi;
background:Yellow;}
.section1{
grid-area: features;
background:LightSalmon;}
.section2{
grid-area: technology;
background:PaleTurquoise;}
.section3{
grid-area: pricing;
background:LightPink;}
.section4{
grid-area: email;
background:PaleGreen;}
.container {
display: grid;
grid-template-rows: repeat (5, auto);
grid-template-columns: 1fr 1fr 1fr;
font-size: 40px;
width: 100%;
background: grey;
grid-template-areas:
"logo navi navi"
"features features features"
"technology technology technology"
"pricing pricing pricing"
"email email email";}
The first thing to notice is that, when you use display: grid on a container element, its direct children will become grid-items, and to these items is that the grid layout you build will apply.
So let's say we have the following:
<div class="container">
<div class="child-1">
<div class="child-2"></div>
<div class="child-2"></div>
</div>
<div class="child-1"></div>
<div class="child-1"></div>
<div class="child-1"></div>
</div>
And this CSS:
.container{
display: grid;
}
Then only the child-1 will become grid items and be able to get properties like grid-area applied to them; everything else inside .child-1, like .child-2 will behave normally, as if there's no Grid. Unless you also specify the .child-1 element to be a grid with display: grid.
In your case, you header element is a direct child of the .container element, so it is a grid item and can be positioned on any place on the grid, but the logo and navi elements are children of header, so the grid layout does not apply to them. You would either have to take them out of the header so the rules you wrote take effect, or create another grid in the header and let it use the full first row. See this example and notice how the nesting of the elements affect them.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: minmax(50px, auto);
grid-template-areas: "logo navi navi";
margin-bottom: 20px;
border: 1px solid black;
}
.logo {
border: 1px solid red;
grid-area: logo;
}
.navi {
border: 1px solid blue;
grid-area: navi;
}
<div class="container">
<div class="logo">Logo</div>
<div class="navi">Nav</div>
</div>
<div class="container">
<header>
<div class="logo">Logo</div>
<div class="navi">Nav</div>
</header>
</div>

Creating a nestable CSS grid that maintains centering without losing background

I'm trying to create a CSS Grid which centers all its items both horizontally and vertically and maintains a background which takes up the whole grid.
To do this, I am first creating CSS for each item which looks something like this:
.item1 {
grid-area: header;
background:yellow;
text-align: center;
align-items: center;
display: grid;
}
The only difference between each item is its item number (in the class), the grid-area name, and the background color. I added display: grid; because without it I can't seem to both center and have the background color cover the whole grid. I don't understand why this is, but it seems to work.
My container CSS looks like this:
.container {
display: grid;
grid-template-columns: 20vw 20vw 20vw 20vw;
grid-template-rows: 25vh 25vh 25vh;
grid-template-areas:
"header header header header"
"main main subg sidebar"
"footer footer footer footer";
}
Now when I create the grid everything looks the way I want it to:
<div class="container">
<div class="item1"><H1>Header</H1></div>
<div class="item2">Main</div>
<div class="item3">Sidebar</div>
<div class="item4">Footer</div>
<div class="item5">X</div>
</div>
Now I want to achieve the exact same effect in the central item. So first I create nearly identical CSS tags for the sub-items and sub-containers. The only differences are in the naming and changing the dimensions from absolute screen based (vh/vw) to percentages:
.sub_item1 {
grid-area: header1;
background:yellow;
text-align: center;
align-items: center;
display: grid;
}
...
.sub_container {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
grid-template-rows: 33% 33% 33%;
grid-template-areas:
"header1 header1 header1 header1"
"main1 main1 subg1 sidebar1"
"footer1 footer1 footer1 footer1";
}
I nest the sub-container in the center item in the top-level container:
<div class="container">
<div class="item1"><H1>Header</H1></div>
<div class="item2">Main</div>
<div class="item3">Sidebar</div>
<div class="item4">Footer</div>
<div class="item5 sub_container">
<div class="sub_item1">Header</div>
<div class="sub_item2">Main</div>
<div class="sub_item3">Side</div>
<div class="sub_item4">Footer</div>
<div class="sub_item5">X</div>
</div>
</div>
I have created a fiddle to demonstrate how it fails. The sub-container does not stretch the background color to fit the cells like the top-level does.
I tried changing the dimensions to screen based (e.g. 5vw, 11vh) and this does not work either.
Remove the "item5" class from the sub-container div.
<div class="sub_container">
Here is the updated fiddle.

How to repeat grid-template-rows for all rows

I'm trying create template for rows in my grid block:
grid-template-rows: repeat(3, 150px);
I know, this template should be work for first 3 rows.
However, from 4 row this template is not work.
Can i make template for all rows?
P.S.
This template work only for 1st row.
grid-template-rows: 150px;
Use grid-auto-rows (automatically generated rows) instead of grid-template-rows (manually generated rows). In current case grid-auto-rows: 150px will do the trick. Demo:
.grid {
display: grid;
grid-auto-rows: 150px;
/* space between columns for demo */
grid-gap: 10px;
}
/* just styles for demo */
.grid__item {
background-color: tomato;
color: white;
}
<div class="grid">
<div class="grid__item">One</div>
<div class="grid__item">Two</div>
<div class="grid__item">Three</div>
<div class="grid__item">Four</div>
<div class="grid__item">Five</div>
<div class="grid__item">Six</div>
<div class="grid__item">Seven</div>
<div class="grid__item">Eight</div>
<div class="grid__item">Nine</div>
</div>

How to make a column span full width when a second column is not there? (CSS Grid)

I know there are similar questions but this is specifically asking how to do this using CSS Grid Layout.
So we have this basic grid setup:
HTML (with sidebar):
<div class="grid">
<div class="content">
<p>content</p>
</div>
<div class="sidebar">
<p>sidebar</p>
</div>
</div>
CSS:
.grid {
display: grid;
grid-template-columns: 1fr 200px;
}
To create a layout that looks something like this:
| content | sidebar |
If the page doesn't have a sidebar though, ie. the html looks like this but with the same CSS:
HTML (no sidebar):
<div class="grid">
<div class="content">
<p>content</p>
</div>
</div>
The page layout looks like this (dashes represent empty space)
| content | ------- |
I know why it does that, the grid column is still defined in the grid-template-columns rule.
I'm just wondering how to tell the grid that if there is no content, then fill the remaining space similar to how flex-grow works for flexbox.
The desired result would look like this if no sidebar is present.
| content |
Don't define the columns explicitly with grid-template-columns.
Make the columns implicit instead and then use grid-auto-columns to define their widths.
This will allow the first column (.content) to consume all space in the row when the second column (.sidebar) doesn't exist.
.grid {
display: grid;
grid-auto-columns: 1fr 200px;
}
.content {
grid-column: 1;
}
.sidebar {
grid-column: 2;
}
.grid > * {
border: 1px dashed red; /* demo only */
}
<p>With side bar:</p>
<div class="grid">
<div class="content">
<p>content</p>
</div>
<div class="sidebar">
<p>sidebar</p>
</div>
</div>
<p>No side bar:</p>
<div class="grid">
<div class="content">
<p>content</p>
</div>
</div>
You can get closer by using content sizing keywords, something like:
.grid {
display: grid;
grid-template-columns: 1fr fit-content(200px);
}
.sidebar {
width: 100%;
}
The fit-content keyword will look at the size of the content and act like max-content until it gets to the value you pass in.
In reality you probably wouldn't need to stick a size on sidebar as the content is likely to dictate a size of at least 200 pixels (for example) but you can play around with this.
I think I know the definitive answer to this question now. The problem with the answers so far is that they don't explain how to handle a sidebar that is on the left side of the main content (mainly because I didn't ask for it in the original question).
<div class="grid">
<nav>
<p>navigation</p>
</nav>
<main>
<p>content</p>
</main>
<aside>
<p>sidebar</p>
</aside>
</div>
You can use this CSS:
.grid {
display: grid;
grid-template-columns: fit-content(200px) 1fr fit-content(200px);
}
nav, aside {
width: 100%;
}
/* ensures that the content will always be placed in the correct column */
nav { grid-column: 1; }
main { grid-column: 2; }
aside { grid-column: 3; }
This is also a good use case for grid-areas
.grid {
display: grid;
grid-template-columns: fit-content(200px) 1fr fit-content(200px);
grid-template-areas: "nav content sidebar";
}
nav, aside {
width: 100%;
}
/* ensures that the content will always be placed in the correct column */
nav { grid-area: nav; }
main { grid-area: content; }
aside { grid-area: sidebar; }
An IE compatible version would look like this:
.grid {
display: -ms-grid;
display: grid;
-ms-grid-columns: auto 1fr auto;
grid-template-columns: auto 1fr auto;
}
nav, aside {
width: 100%; /* Ensures that if the content exists, it takes up max-width */
max-width: 200px; /* Prevents the content exceeding 200px in width */
}
/* ensures that the content will always be placed in the correct column */
nav {
-ms-grid-column: 1;
grid-column: 1;
}
main {
-ms-grid-column: 2;
grid-column: 2;
}
aside {
-ms-grid-column: 3;
grid-column: 3;
}

Resources