7

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?

kukkuz
  • 41,512
  • 6
  • 59
  • 95
Brett DeWoody
  • 59,771
  • 29
  • 135
  • 184

2 Answers2

4

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>
kukkuz
  • 41,512
  • 6
  • 59
  • 95
  • I'd still like the solid fill behind the logo/nav to extend the full width of the page, and match the height of the logo/nav row. Is that possible? – Brett DeWoody Feb 26 '19 at 02:26
  • Is the logo/nav row fixed height? – Michael Benjamin Feb 26 '19 at 02:40
  • @BrettDeWoody I don't think you can match the height of the `nav` and at the same time enforce *nav + logo* to have a `max-width` unless maybe the height of the logo+ nav is fixed... – kukkuz Feb 26 '19 at 02:59
  • @Michael_B The height of the logo/nav row is variable, like it is in the demo (based on vw). In the end it will contain an image which resizes with the screen width. – Brett DeWoody Feb 26 '19 at 09:20
  • 1
    @kukkuz very cool, I didn't realize pseudo elements would work with grid. – Brett DeWoody Feb 26 '19 at 17:05
0

Centering

Horizontally centering the grid container is relatively simple. Use flex alignment properties on the parent:

body {
  display: flex;
  justify-content: center;
}

Maximum Width

You can create a maximum width on the grid container with the max-width or flex property.

.wrapper {
  flex: 600px 0 1;
}

This rule says:

  • flex-basis: 600px (starting width)
  • flex-grow: 0 (the item cannot expand beyond 600px)
  • flex-shrink: 1 (the item can shrink)

This command is essentially equivalent to max-width: 600px.


2-Column Layout

You wrote:

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.

Try this:

.wrapper {
  flex: 600px 0 1;
  display: grid;
  grid-template-columns: min-content 1fr;
}

body {
  margin: 40px;
  display: flex;
  justify-content: center;
}

.wrapper {
  flex: 600px 0 1;
  display: grid;
  grid-gap: 10px;
  grid-template-columns: min-content 1fr;
  grid-template-areas: "logo  nav" 
                       "sidebar content" 
                       "footer footer";
  background-color: #fff;
  color: #444;
}

.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;
}

.fill {
  background-color: gray;
}


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

.header,
.footer {
  background-color: #999;
}
<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 /> These lines wrap when the text hits 600px maximum width. These lines wrap when the text hits 600px maximum width. These lines wrap when the text hits 600px maximum width. These lines wrap when the text hits 600px maximum width. These lines wrap when the text hits 600px maximum width. These lines wrap when the text hits 600px maximum width.</div>
  <div class="box footer">Footer</div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701