Search code examples
htmlcssflexboxcss-gridgrid-layout

CSS Grid Layout with Max-Width, Centered Content


I'm in the midst of learning CSS Grid (long overdue, I know). I've challenged myself to convert a relatively standard float-based layout to a grid, but can't figure out this last part.

My goal is to have a layout where the content (logo + nav, and sidebar + content) is centered, with a max-width. For example, the logo + nav should have a max-width of 600px. I also have a requirement of having a solid fill background covering the full-width of the viewport (matching the height of the variable height logo/nav row).

The first column (logo and sidebar) should shrink to fit their content - so the first column is only as wide as the wider between logo/sidebar. The nav/content should then fill the remaining space allowed by the max-width.

The following is my best attempt. The width of the main content does not fill a max-width. Instead, the width of the main content is the width of the Logo + 250px (the width defined by the grid columns).

What I'm hoping to achieve is - define the max-width of Logo + Nav to a specific width (say 600px), and have the Logo column shrink to fit its content.

body {
  margin: 40px;
}

.fill {
  grid-column-start: 1;
  grid-column-end: 6;
  grid-row-start: 1;
  grid-row-end: 1;
  background-color: gray;
}

.logo {
  grid-area: logo;
  font-size: calc(1rem + 4vw);
}

.sidebar {
  grid-area: sidebar;
}

.content {
  grid-area: content;
}

.nav {
  grid-area: nav;
  text-align: right;
}

.footer {
  grid-area: footer;
}

.wrapper {
  display: grid;
  grid-gap: 10px;
  grid-template-columns: auto min-content 120px 120px auto;
  grid-template-areas: "... logo nav nav ..." "... sidebar content content ..." "... footer  footer  footer ...";
  background-color: #fff;
  color: #444;
}

.box {
  background-color: #444;
  color: #fff;
  border-radius: 5px;
  padding: 10px;
}

.header,
.footer {
  background-color: #999;
}
<div class="wrapper">
  <div class="fill"></div>
  <div class="box logo">Logo</div>
  <div class="box nav">Nav</div>
  <div class="box sidebar">Sidebar</div>
  <div class="box content">Content
    <br /> More content than we had before so this column is now quite tall.</div>
  <div class="box footer">Footer</div>
</div>

Is this possible with CSS Grid, and if so, how?


Solution

  • You can have a 2-column grid with grid-template-columns: auto 1fr so that the first column takes the width of its content (as wide as the wider between logo/sidebar) and the second column takes up the remaining space (Note that I have set max-width: 600px to the grid container).

    I also have a requirement of having a solid fill background covering the full-width of the viewport (matching the height of the variable height logo/nav row)

    For this you can do the following:

    1. First fix logo and nav in the first row by setting the grid-row and grid-column properties

    2. Now use a pseudo element for wrapper overlapping the first row (but stacked below using z-index property).

    3. Set margin-left property as calc(-50vw + 50%) and width as 100vw to stretch the solid fill background across the viewport.

    See demo below:

    body {
      margin: 40px;
    }
    
    .wrapper {
      display: grid;
      grid-gap: 10px;
      grid-template-columns: auto 1fr; /* 2-column grid */
      /* background-color: #fff;*/
      color: #444;
      max-width: 600px; /* max-width of the layout */
      margin: 0 auto; /* center in the viewport */
    }
    .logo {
      font-size: calc(1rem + 4vw);
      grid-row: 1; /* fix the logo in the first row */
      grid-column: 1; /* fix the logo in the first column */
    }
    .nav {
      text-align: right;
      grid-row: 1;  /* fix the nav in the first row */
      grid-column: 2;  /* fix the nav in the second column */
    }
    
    .footer {
      grid-column: span 2; /* footer spans the two columns */
    }
    
    .box {
      background-color: #444;
      color: #fff;
      border-radius: 5px;
      padding: 10px;
    }
    
    .header,
    .footer {
      background-color: #999;
    }
    
    .wrapper:after { /* position this in the first row */
      content: '';
      display: block;
      grid-row: 1;
      grid-column: 1/ 3;
      width: 100vw;
      margin-left: calc(-50vw + 50%);
      background: gray;
      z-index: -1; /* push it behind the first row */
    }
    <div class="wrapper">
      <div class="box logo">Logo</div>
      <div class="box nav">Nav</div>
      <div class="box sidebar">Sidebar</div>
      <div class="box content">Content
        <br /> More content than we had before so this column is now quite tall.</div>
      <div class="box footer">Footer</div>
    </div>