20

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                         |
Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64

3 Answers3

11

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>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 2
    This is a good answer, but unfortunately doesn't work if the sidebar
    comes before the content
    (along with updating the grid-columns to match the new layout). I can't figure out why that is the case.
    – Sean Beck Apr 03 '18 at 19:13
  • Because `grid-auto-columns` [repeats a pattern](https://www.w3.org/TR/css3-grid-layout/#auto-tracks). With `1fr 200px` as values, it will repeat that pattern to create as many implicit columns as necessary. If there is only one column, the `1fr` consumes all space on the row. – Michael Benjamin Apr 18 '18 at 15:23
  • However, if you reverse the values to `200px 1fr`, then the first implicit row is 200px wide. That's why your layout reversal doesn't work as expected. https://jsfiddle.net/ctgzgpu8/ @SeanBeck – Michael Benjamin Apr 18 '18 at 15:26
  • @Michael_B your fiddle example (in the comment above) doesn't work as expected. – evolutionxbox May 11 '18 at 17:08
  • Not sure what part isn't working. Can you be more specific? @evolutionxbox – Michael Benjamin May 16 '18 at 20:09
  • The content (with no sidebar) does not expand across the grid. – evolutionxbox May 17 '18 at 08:58
  • What browser are you using? In my demo above, the content expands with no side bar. Tested in Chrome, Firefox and Edge just now. @evolutionxbox – Michael Benjamin May 17 '18 at 14:32
  • 1
    FFx 60 (osx), Chrome 66 (osx) --- the content column is the same width with or without the sidebar. – evolutionxbox May 17 '18 at 16:01
  • I can confirm what @evolutionxbox says. The content doesnt expand! – callback Apr 19 '20 at 17:51
  • @callback, as you can see from my demo, it does indeed expand. But maybe your set-up is different. Consider posting a new question with your code. – Michael Benjamin Apr 19 '20 at 18:15
8

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.

Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64
Rachel Andrew
  • 81
  • 1
  • 1
  • I'm thinking that setting width: 100%; on the sidebar would probably allow the sidebar to base it's width on the grid layout value rather than have 200px stated twice. – Daniel Tonon Nov 10 '17 at 09:04
  • just don't set anything on it if you want that. – Rachel Andrew Nov 10 '17 at 09:56
  • Wouldn't that mean that the sidebar could end up being less than 200px wide though if there is not 200px worth of content in the side bar? – Daniel Tonon Nov 10 '17 at 10:14
5

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;
}
Daniel Tonon
  • 9,261
  • 5
  • 61
  • 64