No Time Dad

A blog about web development written by a busy dad

Responsive Hero Section Example

Here is a demo of the responsive hero section. Check it out on small and large viewports via your browser’s dev tools (F12).

Intro

How many trendy designs can I fit into one page? A lot, it turns out. The hero section is the cornerstone of any good product site. It is the first thing potential customers see, and it gives them an idea of what your product is about, how to sign up, or how to learn more.

I’ve been admiring stripe’s homepage hero section for a while, and I decided to try and create my own version of it. Mine is not quite as fancy as theirs but I think it is nice, and seems to work well. If nothing else, my version is a solid starting point to build on. My version uses just plain html and css, as well as aplinejs for toggling the mobile menu. I grabbed the main svg from unDraw.

So, this will be less of a line-by-line tutorial and more of a walk-through of my approach and thought process for building this. Full code will be provided at the bottom.

Getting Started

So, Stripe’s hero section features a nice gradient background with this wavy effect happening. I am not sure how they are doing that effect, and I decided not to try to replicate it in my example. I did, however want a gradient background and landed on one that is sort of similar to Stripe’s. I also wanted to be sure that the gradient started on the toolbar and continued down into the rest of the hero section.

Hero Container

To accomplish the gradient effect I created a hero section container that will contain both the toolbar and hero section content. I can then set the background-color of the container which will carry over into the child elements.

<div class="Hero__Container">
...
</div>
.Hero__Container {
  background: linear-gradient(90deg, #FC466B 0%, #3F5EFB 100%);
}

Below is an example of how the gradient looks.

Toolbar

On larger screens Stripe’s toolbar has a lot of padding-left and padding-right. In fact, the toolbar content is almost centered on the page. I am not sure if this would be my first choice, but I did like the look of it. On mobile there is much less padding, which makes sense given the screen size.

So at this point I am writing the media query to handle the padding change. I have been taking a mobile first approach lately, so I wrote my media query to have a set min-width.

<div class="Hero__Container">
  <div class="Hero__Nav">
  ...
  </div>
</div>
.Hero__Nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: white;
  padding: 1rem;
}

@media (min-width: 800px) {
  .Hero__Nav {
    padding-left: 8rem;
    padding-right: 8rem;
  }
}

With those css selectors in place, the toolbar will have 1rem of padding on screens less than 800px, and 8rem of padding-left and padding-right on screens larger than 800px.

Some flexbox magic comes next. With justify-content: space-between; ensuring that the items in the toolbar are evenly spaced horizontally, and align-items: center; ensuring they are evenly spaced vertically.

The next semi-complicated piece of the toolbar was the mobile menu. Basically, there isn’t enough room on the toolbar for multiple links on small screens so I needed to create a dropdown menu to display them. This is mostly handled by alpinejs, which I really appreciate because dropdown menus can be difficult.

The Hero__Container element will contain our mobile menu’s state via x-data="{ mobileMenuOpen: false }" and alpine’s click handler @click="mobileMenuOpen = !mobileMenuOpen" on the hamburger button itself will toggle the menu opened and closed. I also used alpine’s click away handler @click.away="mobileMenuOpen = false" to close the menu if a user clicks outside of the mobile menu.

And of course, the mobile menu button should disappear on larger screens and the Sign In button should disappear on smaller screen. This can be handled by “toggling” display: none; and display: block; (or flex) via the media query.

/* Hidden on small screens */
.Hero__Nav_Signin_Button {
  display: none;
}

/* Displayed on small screens */
.Hero__Nav_Main_Icon {
  width: 2rem;
  height: 2rem;
}

@media (min-width: 800px) {
  ...
  /* Displayed on large screens */
  .Hero__Nav_Signin_Button {
    display: block;
  }
  /* Hidden on large screens */
  .Hero__Nav_Menu_Icon {
    display: none;
  }
  ...
}
Hero Content

The meat and potatoes of the hero section is the actual content. This turned out to be a bit less complicated the toolbar.

Mobile Menu

The mobile menu itself is just a div in the Hero__Container that contains the links. The menu also shares the gradient, and similar styling to the toolbar links. As mentioned above, the visibility of the mobile menu is controlled by the mobileMenuOpen state in Hero__Container.

The nav items themselve in the mobile menu have their own css applied to ensure that they look nice and that the entire li element is clickable.

.Mobile_Menu__Nav_Item {
  display: block;
  padding: 1rem;
  color: white;
  font-weight: 700;
}
<div class="Hero__Content_Mobile_Menu" x-show="mobileMenuOpen" @click.away="mobileMenuOpen = false">
  <nav>
    <ul>
      <li><a class="Mobile_Menu__Nav_Item" href="#pricing">Pricing</a></li>
      <li><a class="Mobile_Menu__Nav_Item" href="#overview">Overview</a></li>
      <li><a class="Mobile_Menu__Nav_Item" href="#updates">Updates</a></li>
      <li><a class="Mobile_Menu__Nav_Item" href="#signin">Sign In</a></li>
    </ul>
  </nav>
</div>
Main Content

The main content section itself is a flexbox with two columns. The column on the left contains the “call to action” text and buttons, where as the column on the right contains a fun svg that I grabbed from unDraw.

The buttons are inside of the left column and are displayed side by side on larger screens and will be stacked on smaller screens. This is done by toggling the display from display: flex; on small screens to display: block; on larger screens via the min-width media query.

The image should not be shown on mobile so its container is set to display: none; outside of the mobile query. I also wanted the image to be bigger on very large screens so I added a new media query for min-width: 1400px that increases the size a little bit.

.Hero_Content__Main {
  display: flex;
  padding: 1.5rem;
}

/* Displayed on all screens */
.Hero_Content__Text_Container {
  display: flex;
  flex-direction: column;
  flex: 1;
  color: white;
}

/* Hidden on small screens */
.Hero_Content__Image_Container {
  display: none;
}

@media (min-width: 800px) {
  ...
  /* Displayed on large screens */
  .Hero_Content__Image_Container {
    display: flex;
    flex-direction: column;
    flex: 1;
    justify-content: center;
    align-items: flex-end;
  }
  ...
}

@media (min-width: 1400px) {
  .Hero__Content_Image {
    width: 650px;
    height: 450px;
  }
}
<div class="Hero_Content__Main">
  <div class="Hero_Content__Text_Container">
    ...
    <div class="Hero_Content__Action_Button_Container">
    ...
    </div>
  </div>
  <div class="Hero_Content__Image_Container">
    ...
</div>

Conclusion and Final Code

Overall a fun project. A bit abbreviated here because it was more of a demo and thought process dump, but I think this example could be dropped in as-is or spruced up a bit as needed. Alpinejs was definitely a huge help here if you aren’t using a larger framework. You could always remove alpine and do the mobile toggle with react, vue, or whatever framework but I like the small footprint and flexibility of alpine right now.

/* START RESETS */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  font-family: "Roboto", sans-serif;
  padding: 0;
  margin: 0;
}

a {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

button {
  background-color: transparent;
  color: inherit;
  border-width: 0;
  padding: 0;
  cursor: pointer;
}

input::-moz-focus-inner {
  border: 0;
  padding: 0;
  margin: 0;
}

ul,
ol,
dd {
  margin: 0;
  padding: 0;
  list-style: none;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}

p {
  margin: 0;
}

/* END RESETS */

:root {
  --btnColor: hsla(0, 0%, 100%, 0.2);
  --btnColorHover: hsla(0, 0%, 100%, 0.5);
  --btnActionColor: hsla(242, 95%, 62%, 0.7);
  --btnActionColorHover: hsl(242, 95%, 62%, 1);
}

.Hero__Container {
  background: linear-gradient(90deg, #FC466B 0%, #3F5EFB 100%);
}

.Hero__Nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: white;
  padding: 1rem;
}

.Hero__Nav_Main {
  display: flex;
  align-items: center;
}

.Hero__Nav_Main_Icon {
  width: 2rem;
  height: 2rem;
}

.Hero__Nav_Main_Text {
  font-weight: 700;
  font-size: 20px;
  padding-left: 0.5rem;
}

.Hero__Nav_Main_Text:hover {
  opacity: 0.8;
}

.Hero__Nav_Links {
  display: none;
}

.Hero__Nav_Links a:hover {
  opacity: 0.8;
}

.Hero__Nav_Links li a {
  padding-left: 1rem;
  padding-right: 1rem;
  font-weight: 500;
}

.Hero__Nav_Signin_Button {
  display: none;
}

.Hero__Nav_Menu_Icon {
  width: 20px;
  height: 20px;
  margin-top: 2px;
}

.Hero__Nav_Menu_Container {
  background-color: var(--btnColor);
  border-radius: 1rem;
  padding: 0.1rem 0.75rem;
}

.btn {
  background-color: var(--btnColor);
  padding: 0.5rem 1rem;
  border-radius: 1.5rem;
}

.btn-lg {
  padding: 1rem 1.5rem;
  font-size: 18px;
  font-weight: 700;
}

.btn:hover {
  background-color: var(--btnColorHover);
}

.btn-action {
  background-color: var(--btnActionColor);
}

.btn-action:hover {
  background-color: var(--btnActionColorHover);
}

.Mobile_Menu__Nav_Item {
  display: block;
  padding: 1rem;
  color: white;
  font-weight: 700;
}

.Bottom__Content {
  display: flex;
}

.Hero__Content_Image {
  width: 325px;
  height: 325px;
}

.Hero_Content__Main {
  display: flex;
  padding: 1.5rem;
}

.Hero_Content__Text_Container {
  display: flex;
  flex-direction: column;
  flex: 1;
  color: white;
}

.Hero_Content__Text {
  font-weight: 700;
  font-size: 44px;
  padding-bottom: 2rem;
}

.Hero_Content__SubText {
  font-size: 19px;
  font-weight: 500;
  padding-bottom: 2.75rem;
  opacity: 0.8;
}

.Hero_Content__Action_Button_Container {
  display: flex;
  flex-direction: column;
  text-align: center;
  padding-top: 1rem;
  padding-bottom: 1rem;
}

.Hero_Content__Action_Button_Container a:first-child {
  margin-bottom: 0.5rem;
}

.Hero_Content__Image_Container {
  display: none;
}

@media (min-width: 800px) {
  .Hero__Nav {
    padding-left: 8rem;
    padding-right: 8rem;
  }
  .Hero__Nav_Main_Text {
    font-size: 24px;
  }
  .Hero__Nav_Links {
    display: flex;
  }
  .Hero__Nav_Signin_Button {
    display: block;
  }
  .Hero__Nav_Menu_Icon {
    display: none;
  }
  .Hero__Nav_Menu_Container {
    display: none;
  }
  .Hero__Content_Mobile_Menu {
    display: none;
  }
  .Hero_Content__Main {
    padding-left: 8rem;
  }
  .Hero_Content__Action_Button_Container {
    display: block;
    text-align: start;
  }
  .Hero_Content__Image_Container {
    display: flex;
    flex-direction: column;
    flex: 1;
    justify-content: center;
    align-items: flex-end;
  }
}

@media (min-width: 1400px) {
  .Hero__Content_Image {
    width: 650px;
    height: 450px;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="./styles.css">
  <script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
  <title>Responsive Hero Section</title>
</head>

<body>
  <div class="Hero__Container" x-data="{ mobileMenuOpen: false }">

    <!-- START TOOLBAR -->
    <div class="Hero__Nav">
      <div class="Hero__Nav_Main">
        <svg class="Hero__Nav_Main_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="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
        </svg>
        <a href="#main" class="Hero__Nav_Main_Text">
          bored.io
        </a>
      </div>
      <ul class="Hero__Nav_Links">
        <li><a href="#pricing">Pricing</a></li>
        <li><a href="#overview">Overview</a></li>
        <li><a href="#updates">Updates</a></li>
      </ul>
      <a href="#signin" class="btn Hero__Nav_Signin_Button">Sign In</a>
      <a class="Hero__Nav_Menu_Container" href="#" @click="mobileMenuOpen = !mobileMenuOpen">
        <svg class="Hero__Nav_Menu_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="M4 6h16M4 12h16M4 18h16" />
        </svg>
      </a>
    </div>
    <!-- END TOOLBAR -->

    <!-- START HERO CONTENT CONTAINER-->
    <div class="Hero__Content_Container">

      <!-- START MOBILE NAV-->
      <div class="Hero__Content_Mobile_Menu" x-show="mobileMenuOpen" @click.away="mobileMenuOpen = false">
        <nav>
          <ul>
            <li><a class="Mobile_Menu__Nav_Item" href="#pricing">Pricing</a></li>
            <li><a class="Mobile_Menu__Nav_Item" href="#overview">Overview</a></li>
            <li><a class="Mobile_Menu__Nav_Item" href="#updates">Updates</a></li>
            <li><a class="Mobile_Menu__Nav_Item" href="#signin">Sign In</a></li>
          </ul>
        </nav>
      </div>
      <!-- END MOBILE NAV -->

      <!-- START HERO CONTENT -->
      <div class="Hero_Content__Main">
        <div class="Hero_Content__Text_Container">
          <div class="Hero_Content__Text">Awesome Hero Text Goes Here</div>
          <div class="Hero_Content__SubText">
            Description sub text would go here. Some words that describe the product or page would be a really good thing to put here.
          </div>
          <div class="Hero_Content__Action_Button_Container">
            <a class="btn btn-lg btn-action">Get Started</a>
            <a class="btn btn-lg">Learn More</a>
          </div>
        </div>
        <div class="Hero_Content__Image_Container">
          <img class="Hero__Content_Image" src="./photos.svg" alt="Visual Data Image"></div>
      </div>
      <!-- END HERO CONTENT -->

    </div>
    <!-- END HERO CONTENT CONTAINER -->

  </div>
  <div class="Bottom__Content">

  </div>
</body>

</html>