No Time Dad

A blog about web development written by a busy dad

Responsive Grid Feature Section

A demo of what I’ll be building and explaining in this post. Try resizing the screen to see how it changes.

Intro

The quest to determine whether flexbox or grid is better for large layouts continues. Currently, grid is winning. It is just so much easier to set a parent element as display: grid; and automatically have the children become grid items. You certainly can build this feature section using only flexbox. You would just need to apply flexbox properties to each child element.

For the feature section I’ll be working on today we want to start with a grid column on mobile that will contain all 8 sections. As the viewport expands a second column will be added, and eventually there will be four columns.

Building the feature section

We’ll have two main parts for this feature section; the header section (which provide a brief description), and the content section (which give an overview of the features of the product). The content section is where we will use css grid. The entire thing should be fully responsive and we’ll take a “mobile first” approach by starting our work on viewport that is 320px wide. I’m also on this gradient kick lately, so I’ll be using a dark blue background gradient with white text for this example. Hopefully, it isn’t too overwhelming.

Scaffolding

The header and content sections will sit inside of a Container element where we will apply the gradient background.

Here is the basic html structure of the feature section:

<div class="Container">
  <div class="Header">...</div>
  <div class="Content">...</div>
</div>

The Container selector will have the background: linear-gradient(...); property. It will also have the padding property. Putting both of these properties in the parent div ensures that there is consistency with all the child divs, and it allows us to not have to repeat properties throughout our css.

The Header selector does not do much. But, I think it important to explicitly declare class="Header" on the div even if it isn’t doing much because it makes the html easier to read and follow. This will save you time later.

Lastly, the Content selector defines the grid. It is the main workhorse of our feature section. Later, when we implement the media queries you’ll see that this is the primary selector that we are modifying to change the look of our elements on different size screens. Initializing the grid is done by adding display: grid;. Any child elements in the Content container are now grid items. Since we’re taking a mobile first approach, we will set the number of columns to 1 via grid-template-columns: repeat(1, 1fr); and the row height to 200px via grid-auto-rows: 200px;.

.Container {
  padding: 1rem;
  /* The nice dark blue gradient */
  background: linear-gradient(90deg, #4b6cb7 0%, #182848 100%);
}

.Header {
  /* Mostly a placeholder since block is the default value for display */
  display: block;
}

.Content {
  /* Initialize the grid */
  display: grid;
  /* Only show 1 column on small screens */
  grid-template-columns: repeat(1, 1fr);
  /* Rows should be 200px heigh on small screens */
  grid-auto-rows: 200px;
  /* Slight gap between each grid item */
  gap: 10px;
  padding-top: 2rem;
  padding-bottom: 1rem;
}
Header section

The Header section itself is not overly complicated. It isn’t even in the grid. We basically want some large text to catch the user’s attention and some smaller text underneath the large text that gives a brief description to supplement the header text. An h1 tag is an ideal candidate for large header text and it is screen reader friendly (assuming you don’t have multiple h1 tags somewhere else on the page).

Below is our updated html and css selectors for the header.

...
.Header { 
  display: block;
}

.Header__Title {
  color: white;
}

.Header__Sub_Text {
  color: white;
  /* Slightly less pronounced */
  opacity: 0.8;
  font-size: 18px;
}
...
<div class="Container">
  <div class="Header">
    <h1 class="Header__Title">...</h1>
    <div class="Header__Sub_Text">...</div>
  </div>
  <div class="Content">...</div>
</div>
Content section

The Content section is really where the magic happens. We’ve defined the Content section as a grid, so now we need to add some grid items. For our feature-section we will have eight items total. I won’t go into detail on each item itself since the css for each item is mostly cosmetic with a little bit of flexbox, but it is important to note that each item contains description text for a “feature”.

In this example, my description text is short and consistent length on purpose. I realize this is easier said than done since I am using lorem ipsum fillter, but you should try to keep your descripptions the same length. If you have varying length descriptions then your grid items will appear to be different heights, even though we’re setting a value for grid-auto-rows.

Each item in the Content container has an icon, header, and description.

...
.Content {
  display: grid;
  grid-template-columns: repeat(1, 1fr);
  grid-auto-rows: 200px;
  gap: 10px;
  padding-top: 2rem;
  padding-bottom: 1rem;
}

.Content__Item {
  /* Use flexbox to help with alignment */
  display: flex;
  flex-direction: column;
  padding: 1rem;
}

.Item__Icon_Container {
  /* Create a box around the icon */
  position: absolute;
  display: flex;
  padding: 0.5rem;
  color: white;
  border-radius: 0.25rem;
  background-color: rgb(187, 184, 184, 0.3);
  
  /* Fix strange spacing issue */
  margin-top: 4px; 
}

.Item__Icon {
  height: 1.5rem;
  width: 1.5rem;
}

.Item__Text_Container {
  /* Push the right since the Icon_Container has position: absolute */
  margin-left: 3.25rem;
}

.Text__Title {
  color: white;
  font-weight: 600;
}

.Text__Sub_Text {
  color: white;
  opacity: 0.8;
}
...
<div class="Container">
  <div class="Header">
    <h1 class="Header__Title">...</h1>
    <div class="Header__Sub_Text">...</div>
  </div>
  <div class="Content">
    <div class="Content__Item">
      <div class="Item__Item_Container">
        <div class="Item__Icon">...</div>
      </div>
      <div class="Item__Text_Container">...</div>
      <div class="Text__Sub_Text">...</div>
    </div>
  </div>
</div>
Breakpoints

Structurally, the bulk of the work is done at this point. Our pentultimate task now is to construct the media queries. You might argue that I went a little overboard with the media queries, and you’re probably right, but I really like how the page looks at each size.

Here are the min-widths we’ll be targeting and their respective column count:

  • min-width: 375px (1 column)
  • min-width: 425px (1 column)
  • min-width: 768px (2 columns)
  • min-width: 1440px (4 columns)

The columns count actually doesn’t change until the 768px media query, but you’ll notice that as the screen gets wider the row width will start to change. This means that sometimes you’ll have a lot of extra space between rows. Not ideal.

Our html will remain the same, so below are the media queries we’ll be adding to our css.

...
@media (min-width: 375px) {
  .Content {
    /* Decrease the row height */
    grid-auto-rows: 175px;
  }
}

@media (min-width: 425px) {
  .Content {
    /* Decrease the row height */
    grid-auto-rows: 150px;
  }
}

@media (min-width: 768px) {
  .Content {
    /* Row height will be inherited from the 425px width selector, but add a second column */
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1440px) {
  .Content {
    /* Row height will still be inherited from the 425px width selector, but add two more columns */
    grid-template-columns: repeat(4, 1fr);
  }
}

Thoughts & Final Code

Overall another fun little project. I think it ended up with a little more white space than I’d prefer, but that probably isn’t the end of the world.

A little advice on dealing with grids, or any layout for that matter; Just start with boxes. You can figure out the content of the boxes later, but just having a colorful point of reference when working with css grid and breakpoints is extremely helpful. Once you have a rough implementation of how the layout will work on various breakpoints then you can go back in and add the content and fine-tune the sizes.

The icons used in this example are from heroicons.

.Container {
  padding: 1rem;
  background: linear-gradient(90deg, #4b6cb7 0%, #182848 100%);
}


.Header { 
  display: block;
}

.Header__Title {
  color: white;
}

.Header__Sub_Text {
  color: white;
  opacity: 0.8;
  font-size: 18px;
}


.Content {
  display: grid;
  grid-template-columns: repeat(1, 1fr);
  grid-auto-rows: 200px;
  gap: 10px;
  padding-top: 2rem;
  padding-bottom: 1rem;
}

.Content__Item {
  display: flex;
  flex-direction: column;
  padding: 1rem;
}

.Item__Icon_Container {
  position: absolute;
  display: flex;
  padding: 0.5rem;
  color: white;
  border-radius: 0.25rem;
  background-color: rgb(187, 184, 184, 0.3);
  
  /* Fix strange spacing issue */
  margin-top: 4px; 
}

.Item__Icon {
  height: 1.5rem;
  width: 1.5rem;
}

.Item__Text_Container {
  margin-left: 3.25rem;
}

.Text__Title {
  color: white;
  font-weight: 600;
}

.Text__Sub_Text {
  color: white;
  opacity: 0.8;
}


@media (min-width: 375px) {
  .Content {
    grid-auto-rows: 175px;
  }
}

@media (min-width: 425px) {
  .Content {
    grid-auto-rows: 150px;
  }
}

@media (min-width: 768px) {
  .Content {
    grid-template-columns: repeat(2, 1fr);
    /* grid-auto-rows: 150px; */
  }
}

@media (min-width: 1440px) {
  .Content {
    grid-template-columns: repeat(4, 1fr);
    /* grid-auto-rows: 150px; */
  }
}
<div class="Container">
  <div class="Header">
    <h1 class="Header__Title">All of the tools you need at your
      fingertips</h1>
    <div class="Header__Sub_Text">Lorem ipsum dolor sit amet,
      consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    </div>
  </div>
  <div class="Content">
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Unlimited users</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container--1OJ5b"><svg class="Item__Icon"
          xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Easy pricing</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item--HVfW1">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">24/7 support</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M13 10V3L4 14h7v7l9-11h-7z"></path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Fast downloads</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Monthly reports</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Multiple edits</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Group annotations</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
    <div class="Content__Item">
      <div class="Item__Icon_Container"><svg class="Item__Icon" xmlns="http://www.w3.org/2000/svg"
          fill="none" viewBox="0 0 24 24" stroke="currentColor">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4">
          </path>
        </svg></div>
      <div class="Item__Text_Container">
        <div class="Text__Title">Auto-save</div>
        <div class="Text__Sub_Text">Lorem ipsum dolor sit amet,
          consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
          aliqua.</div>
      </div>
    </div>
  </div>
</div>