Search code examples
cssflexboxcss-grid

CSS responsive grid layout with bigger cells


I need to display a product listing (shopify, but the question is about CSS) with this particular grid template

enter image description here

I need to display the products "flowing" next to the larger cells which will contain images.

The problem occurs in the 3-column view from tablets, since only 2 products should be displayed next to the large cells instead of 4.

From mobile simply large cells will be full width.

The Shopify theme is completely custom (I'm building it from scratch) using Bootstrap

I tried to use the Bootstrap grid-system but in the responsive version I was unable to show only 2 products.

I would like to do it all with pure css without using javascript


Solution

  • A trick you may want to know: The first step of programming is to find similarities out of differences.

    Our two layouts both have two big blocks, one bar and a number of smaller blocks (12 in desktop and 10 in tablet). That makes our HTML look like this (the order mostly doesn't matter, but only mostly; we'll address that later):

    <div id="layout">
      <div class="square" id="square-1">Content</div>
      <div class="square" id="square-2">Content</div>
      <div class="bar">Content</div>
      <div class="product">Product 1</div>
      <div class="product">Product 2</div>
      <div class="product">Product 3</div>
      <div class="product">Product 4</div>
      <div class="product">Product 5</div>
      <div class="product">Product 6</div>
      <div class="product">Product 7</div>
      <div class="product">Product 8</div>
      <div class="product">Product 9</div>
      <div class="product">Product 10</div>
      <div class="product">Product 11</div>
      <div class="product">Product 12</div>
    </div>
    

    We have 2 big blocks, both span 2 rows and 2 columns. One starts at the 2nd row and span the last 2 columns, the other at the 5th row and span the first 2 columns. We translate that into CSS accordingly:

    .square {
      grid-row-end: span 2;
      grid-column-end: span 2;
    }
    
    #square-1 {
      grid-row-start: 2; /* The 2nd horizontal line */
      grid-column-start: -3; /* The 3rd from last vertical line */
    }
    
    #square-2 {
      grid-row-start: 5; /* The 5th horizontal line */
      grid-column-start: 1; /* The first vertical line */
    }
    

    The bar starts at the 4th row and span all columns. Make sure we add that as well:

    .bar {
      grid-row: 4 / span 1;
      grid-column: 1 / -1;
    }
    

    Our layout is a grid which originally has 4 columns. Since we need to change this in tablet view, let's use a CSS variable. We'll also use grid-auto-rows: 1fr as the rows need to have equal height:

    #layout {
      --columns: 4;
      display: grid;
      grid-template-columns: repeat(var(--columns), 1fr);
      grid-auto-rows: 1fr;
    }
    

    On tablet, we expects 3 columns. Simply change the CSS variable we specified above to reflect that. Also, the last two products need to be hidden as they will ruin our layout; note that these two must be the last two children of #layout for the selector to work:

    @media only screen and (max-width: 1000px) {
      #layout {
        --columns: 3;
      }
      .product:nth-last-child(-n + 2) {
        display: none;
      }
    }
    

    Try it:

    #layout {
      --columns: 4;
      display: grid;
      grid-template-columns: repeat(var(--columns), 1fr);
      grid-auto-rows: 1fr;
    }
    
    .square {
      grid-row-end: span 2;
      grid-column-end: span 2;
    }
    
    .bar {
      grid-row: 4 / span 1;
      grid-column: 1 / -1;
    }
    
    #square-1 {
      grid-row-start: 2;
      grid-column-start: -3;
    }
    
    #square-2 {
      grid-row-start: 5;
      grid-column-start: 1;
    }
    
    @media only screen and (max-width: 1000px) {
      #layout {
        --columns: 3;
      }
      .product:nth-last-child(-n + 2) {
        display: none;
      }
    }
    
    /* Demo only */
    
    #layout {
      gap: 5px;
    }
    
    :where(#layout) > * {
      display: flex;
      align-items: center;
      justify-content: center;
      background: #ddd;
    }
    
    .product, .square {
      aspect-ratio: 1 / 1;
    }
    <div id="layout">
      <div class="square" id="square-1">Content</div>
      <div class="square" id="square-2">Content</div>
      <div class="bar">Content</div>
      <div class="product">Product 1</div>
      <div class="product">Product 2</div>
      <div class="product">Product 3</div>
      <div class="product">Product 4</div>
      <div class="product">Product 5</div>
      <div class="product">Product 6</div>
      <div class="product">Product 7</div>
      <div class="product">Product 8</div>
      <div class="product">Product 9</div>
      <div class="product">Product 10</div>
      <div class="product">Product 11</div>
      <div class="product">Product 12</div>
    </div>