Creating a pure responsive CSS Grid Hero Image or Banner Image
Tags: css, html, design
Published: 2020-05-13 02:44:32

_Quick note: I am an occasional developer, so if I did something wrong, please be gentle :-). Okay on with the article..._

Recently I decided to build my website from scratch and did so to learn Jekyll (a blog-aware web platform) and GitHub Pages. I also wanted to learn SASS/SCSS.

My last real website used `div` and `table` tags everywhere for layout, along with some JavaScript code to keep all the elements in check. While the sites generally looked great, changing anything was a courageous act that put fear in my heart and added pain to my typing fingers.

Before building my site, I decided to take a look at what people are using now for HTML and CSS code. That is when I learned stylesheets (CSS) could be fun with the SASS programming language, which is essentially CSS that is easier to read. While I was learning about SASS, I stumbled upon a few videos that extolled the virtues of responsive website design using CSS Grids. 

I never heard about CSS grids until very shortly before designing my site, but realizing they are the elegant implementation of my old `div-table-javascript` mashups of the past, I was sold.

For my new site, I decided to make it 100% based on CSS Grids and require no JavaScript, which is what I accomplished (the only JavaScript comes in the form of integrations with Calendly, Mailchimp, etc., but I have no managed JavaScript code for the site).

But...

## The hero image/banner image death spiral
You have all seen them before. You go to a website, and there is a big image on the page. That image calls attention to a text overlay. That text overlay may have other HTML elements, like buttons or form fields, to sign up for a newsletter, for example. Since the image could change, and the text might be hard to read, you will notice a slight gradient over the image.

![Sample hero image](https://dev-to-uploads.s3.amazonaws.com/i/yocsne90ly3skx663fru.png)

I thought it would be super easy to create this. Place a CSS Grid down, add HTML text, and I will be good to go. Not so fast.

If you search for a _banner image_ or _hero image_ on all the places (Google, YouTube, CodePen, etc.), you will see countless ways designers create them.

Some code samples were super complex, requiring JavaScript. Most examples, however, relied on a method where you put your text on the page, sans image. You use CSS to add the image as a background image, add a linear gradient over the background image, and then use absolute positioning to place the HTML overlay text onto the page.

Here is a straightforward example I found from W3Schools, but trust me, they get pretty complicated after that.

[W3Schools article on how to create a hero image](https://www.w3schools.com/howto/howto_css_hero_image.asp)

Those approaches are all fine and good, but I did not want it to feel like a hack, by putting a background image in CSS, using absolute positioning to place the content, or using JavaScript to ensure responsiveness. I wanted a pure CSS Grid option, which I could not find, so after far too much research, I decided to build it myself.

## My hero image requirements
Here are the basic requirements I laid out for my hero image:

1. The image must be placed as an `img` tag in HTML. I do not want background images or have the image defined in CSS while the rest of the images are defined in HTML.
2. I want the _option_ of a gradient overlay for any hero/banner images.
3. The HTML components cannot use absolute positioning. They must be responsive and CSS Grid "friendly."
4. The banner should work on Firefox, Chrome, Edge, and IE (or at least the version of IE I tested with).

## The solution
To be honest, I was getting so frustrated with the fact no one had a nice clean and straightforward approach until I came across the [CSS Grid Generator by Sarah Edo](https://cssgrid-generator.netlify.app/). I was playing around with creating various grid sizes and noticed that if I click on a grid area more than once, I would get more grid areas. Ahhh! You can overlap grid areas!

**NOTE: I later changed the height to a set number of pixels.**

![A CSS Grid definition with three overlapping grid areas](https://dev-to-uploads.s3.amazonaws.com/i/4x7dkn63ydtwknuucjvg.png)

With my new CSS Grid design in place, it is a matter of merely placing each element on top of the other. Here is a 3D view of what the site looks like:

![3D view of how the CSS Grid layout areas overlap each other to get the desired hero image look.](https://dev-to-uploads.s3.amazonaws.com/i/3c6fdwf77bcu5mtepi5e.png)

## Putting it all together
The following CodePen shows the final solution. In the following sections of this article, I will walk you through the code.

{% codepen https://codepen.io/BillRaymond/pen/MWaJzvN %}
The CodePen uses a randomly selected image from [Unsplash](https://unsplash.com). Note how most images display nicely even though they are portrait or landscape... more on that later.

## The html
I created an HTML file and placed the following code in the `body` area. There is a `section` that represents the grid itself, and then there are three `div` tags that contain the image, gradient overlay, and the content. In CSS, these `divs` are defined as grid areas, and they will overlap with each other. 

```
<!-- The CSS grid that makes up the entirety of the hero image/banner image area -->
<section class="top-banner-section">
    <!-- The CSS grid area that displays the image (layer 1) -->
    <div class="banner-image-div">
      <img class="banner-image" src="https://source.unsplash.com/random" alt="Banner Image" />
    </div>
    <!-- The CSS grid area that displays the semi-transparent gradient overlay (layer 2) -->
    <div class="banner-overlay-div"></div>
    <!-- The CSS grid area that displays the content (layer 3) -->
    <div class="banner-text-div">
      <span class="banner-text">
        <p class="banner-h1-text">Remain relevant in today's technology-driven economy</p>
        <p class="banner-body-text">Learn how agile can give you a competitive edge.</p>
        <p class="banner-btn"><a class="banner-btn-item" href="https://www.cambermast.com">Get started &#8594;</a></p>
      </span>
    </div>
  </section>
```

## The CSS Grid
Using the CSS Grid code I pulled from the aforementioned CSS Grid generator, I modified the CSS a bit, making the height 350px and aligned and justified the content contained within so it is centered. While I wanted a responsive site, I also put some limits on just how small the image could get.

As you can see in the following code snippet, the CSS Grid 'connects' with the corresponding `section` in the HTML code.

```
// Create three grid areas (boxes) that overlap each other, essentially creating three layers.
// Grid box 1 (layer 1): Image
// Grid box 2 (layer 2): Gradient overlay
// Grid box 3 (layer 3): Call to action text/content
.top-banner-section {
  display: grid;
  grid-template-columns: 1fr; // stretch to the full frame
  grid-template-rows: 350px; // 350 pixels tall
  grid-column-gap: 0px;
  grid-row-gap: 0px;
  align-content: center;
  justify-content: center;

  .banner-image-div {
    grid-area: 1 / 1 / 2 / 2;
  } // image
  .banner-overlay-div {
    grid-area: 1 / 1 / 2 / 2;
  } // gradient or other overlay
  .banner-text-div {
    grid-area: 1 / 1 / 2 / 2;
  } // overlay objects like text, buttons, etc.
}
```

At this point, you have HTML that creates a CSS Grid with three grid areas. One thing that may not be apparent is how the `grid-area` knows to display the image first, the gradient second, and the content third. That is defined by order of the `div` tags in the HTML. So if I put the gradient `div` tag first, then the image would just draw over it.

## Layer 1: The banner image
As I mentioned earlier in this article, I want the banner to be responsive. For the image, I would not let it get smaller than 350px because at some point the font would be too small to read. Otherwise, it can stretch the entire width and height of the CSS Grid it lives in.


```
// Banner image (layer 1)
.banner-image {
  display: grid;
  min-width: 350px; // Do not resize to smaller than this.
  width: 100%;
  height: 100%;
  object-fit: cover; // Using this so the image can be any size and still look halfway decent.
}
```

Something I wanted but did not articulate in my list of requirements was the ability for my grid to easily handle any image I throw into the `img` tag in the HTML. Whether I place a large portrait photo or a horizontal one, I wanted the image to look good. It turns out you can use the `object-fit: cover;` css rule. The basic idea is the browser stretches the image proportionally and crops out parts that cannot display because of height and width restrictions.

Since I want the image to be responsive, I made the width and height 100%. That means as the browser size changes, so will the height and width of the image. Since the image is contained within a grid, it will never be larger than its container.

_Note: I am sure throwing up any image of any file size, width and height, is bad for optimization and design, but this generic approach of using object-fit worked great for me._

## Layer 2: The semi-transparent gradient overlay
As the following code implies, I found a gradient generator online (there are many, and I forgot to write down the one I used). The basic idea here is to create a linear gradient fill that goes from black to white at a 60-degree angle (why 60 degrees? it looked good to me.). 

```
// Gradient overlay (layer 2)
// gradient overlay going from black to transparent.
// note: search for a gradient overlay generator to make this easier.
.banner-overlay-div {
  display: grid;
  max-width: 100%;
  background: black;
  background: linear-gradient(
    60deg,
    rgba(0, 0, 0, 0.7777485994397759) 30%,
    rgba(255, 255, 255, 0) 100%
  ); // start at black at the bottom left'ish and goes at a 60% angle. This will make the white easy to read with nearly any image.
}
```

The black (0,0,0) is 30% transparent. The white (255, 255, 255) is 100% transparent. With those transparencies in place, you can see the underlying image while also making it easier to read the white content that displays in layer 3.

## Layer 3: The content
At this point, we have an image that displays on the screen. A layer above that is the semi-transparent gradient. Now, we want to display the content. As you can see, I just center the content and put a little margin on the right and left, so the text does not pin directly with the edge of the image.


```
// Banner html components (layer 3)
// banner text
.banner-text-div {
  display: grid;
  align-items: center;
  margin-left: 15px;
  margin-right: 15px;
}
```

In the spirit of responsiveness, I could have made the `margin-left` and `margin-right` a percentage, em, rem, or something other than a hard-coded number of pixels, but it worked for me.

## Styling the content
Since I said one of my requirements was to make this banner responsive, I might as well talk about how I format the content. Of course, this is using SASS, so apologies if you want the CSS code here.

I am not going to go through each element, but you can see I used a little `calc` trick to say, "I don't want the font to be smaller than this point size, but it is okay to make it larger.". I will be curious to see if anyone has comments about how good or bad this is or if I should have used a different approach, but again, it worked for me.

```
// Typograhy: *** This is all the stuff you change
.banner-h1-text {
  // font can get larger, but no smaller than 10 points.
  font-size: calc(10pt + 0.15vw);
  letter-spacing: 0.05em;
  font-weight: bolder;
  text-transform: uppercase;
  color: white;
}

.banner-body-text {
  // font can get larger, but no smaller than 10 points.
  font-size: calc(10pt + 0.15vw);
  margin-top: 0.5em;
  color: white;
  text-decoration: none;

  &:hover {
    color: white;
  }
  &:visited {
    color: white;
  }
  &:active {
    color: white;
  }
}

.banner-btn {
  margin-top: 1em;
}

.banner-btn-item {
  font-size: calc(8pt + 0.15vw); // responsive size, but keep a minimum.
  padding-top: calc(0.5em + 0.08vw);
  padding-bottom: calc(0.5em + 0.08vw);
  padding-left: calc(0.5em + 0.08vw);
  padding-right: calc(0.5em + 0.08vw);
  color: blue;
  background-color: white;
  text-align: center;
  text-transform: uppercase;
  font-weight: bold;
  border: 1px solid white;

  &:link {
    text-decoration: none;
  }
  &:visited {
    text-decoration: none;
  }
}
```

## Wrapup
As I mentioned at the top of this article, I am not a full-time programmer, but I do like to learn. I had no idea SASS or CSS Grids even existed until I started this project, but it sure was fun to learn.

I am sure there are things I did wrong and will bite me later, but so far, my minimal browser testing shows the site works pretty well.

If you would like to see the final website design, that only uses CSS Grid, check it out at [Cambermast.com](https://www.cambermast.com).

Any recommended changes to my site are welcome and truly appreciated, so feel free to send a pull request or log an issue if you like. [GitHub Pages code](https://github.com/BillRaymond/cambermast-website).