Search code examples
csscss-grid

How can I avoid overlap when assigning two elements the same CSS grid area?


I have markup with the following structure. I would like to have a responsive layout with three break points that works as follows:

  • Large viewport, three columns:
    ".......  nav      ........"
    "toc-pkg  preamble toc-page"
    "toc-pkg  content  toc-page"
    
  • Medium viewport, two columns, with in the toc grid area both toc-page and toc-pkg following one after the other:
    " nav      ..." 
    " preamble toc"
    " content  toc"
    
  • Narrow viewport one columns:
    " nav      "
    " preamble " 
    " toc-page " 
    " content  " 
    " toc-pkg  " 
    

While it's easy to get the first and third layout. I can't get the second one. If I try to assign both toc-page and toc-pkg to the toc grid area they overlap (as expected it seems), but I'd like them to stack.

Is there any solution that involves a single markup structure and only CSS?

.grid>* {
  display: flex;
  justify-content: center;
  align-items: center;
}

.nav {
  height: 1em;
  background: #3C354F;
  color: white;
  grid-area: nav;
}

.toc-page {
  height: 4em;
  width: 100%;
  background: #E0695E;
  grid-area: toc-page;
}

.toc-pkg {
  height: 2em;
  width: 100%;
  background: #E0695E;
  grid-area: toc-pkg;
}

.content {
  height: 10em;
  background: #DFD4C7;
  grid-area: content;
}

.preamble {
  height: 6em;
  background: #F3F3E3;
  grid-area: preamble;
}

.tocs {
  display: flex;
  grid-area: tocs;
  flex-direction: column;
  justify-content: start;
  align-items: center
}

.grid {
  display: grid;
  gap: 1rem;
}

.grid {
  grid-template-rows: auto;
  grid-template-areas: ". nav ." "toc-page preamble toc-pkg" "toc-page content toc-pkg";
}

@media (max-width: 40em) {
  .toc-page {
    grid-area: toc;
  }
  .toc-pkg {
    grid-area: toc;
  }
  .grid {
    grid-template-rows: auto;
    grid-template-areas: "nav ." "preamble toc" "content toc";
  }
}

@media (max-width: 30em) {
  .toc-page {
    grid-area: toc-page;
  }
  .toc-pkg {
    grid-area: toc-pkg;
  }
  .grid {
    grid-template-rows: auto;
    grid-template-areas: "nav" "preamble" "toc-page" "content" "toc-pkg";
  }
}

body {
  margin: 1rem;
}
<div class="grid medium">
  <div class="nav">NAV</div>
  <div class="preamble">PRE</div>
  <div class="toc-page">TOC-PAGE</div>
  <div class="toc-pkg">TOC-PKG</div>
  <div class="content">TXT</div>
</div>


Solution

  • @Paulie_D suggested using display: contents (thanks!). By tweaking the markup to

    <div class="grid medium">
      <div class="nav">NAV</div>
      <div class="preamble">PRE</div>
      <div class="tocs"
        <div class="toc-page">TOC-PAGE</div>
        <div class="toc-pkg">TOC-PKG</div>
      </div>
      <div class="content">TXT</div>
    </div>
    

    Allows this to be made to work as:

    .grid > * {  display: flex; justify-content: center; 
             align-items: center;  }
    .nav { height: 1em; background: #3C354F; color: white; grid-area: nav; }
    .toc-page { height: 4em; width: 100%; background: #E0695E; 
            grid-area: toc-page; }
    .toc-pkg  { height: 2em; width: 100%; background: #E0695E; 
            grid-area: toc-pkg; }
    .content { height: 10em; background: #DFD4C7; grid-area: content; }
    .preamble { height: 5em; background: #F3F3E3; grid-area: preamble; }
    .grid { grid-template-rows: auto; display: grid; gap: 1rem;}
    .tocs { display: contents; grid-area: unset; }
    .grid {
       grid-template-areas: 
     ". nav ."
     "toc-page preamble toc-pkg"
     "toc-page content toc-pkg";
     }
    
    @media (max-width: 80em) {
      .toc-page { grid-area: unset; }
      .toc-pkg { grid-area: unset; }
      .tocs { display: block; grid-area: toc }
      .grid {
      grid-template-areas: 
    "nav ."
    "preamble toc"
    "content toc";
    }
    }
    
    @media (max-width: 60em) {
      .toc-page { grid-area: toc-page; }
      .toc-pkg { grid-area: toc-pkg; }
      .tocs { display: contents; grid-area: unset }
      .grid {
     grid-template-areas: 
      "nav"
      "preamble" 
      "toc-page"
      "content"
      "toc-pkg";
      }
    }
    body { margin: 1rem; }
     <div class="grid medium">
      <div class="nav">NAV</div>
      <div class="preamble">PRE</div>
      <div class="tocs">
    <div class="toc-page">TOC-PAGE</div>
    <div class="toc-pkg">TOC-PKG</div>
      </div>
      <div class="content">TXT</div>
    </div>