Build a Creative Animated Hero Header

Above is a static screenshot of the final hero header

I was browsing the University of London website recently and came across this fun little ‘What makes us unique’ section, and the subtle way the red background line animates into view as the section comes into the viewport is pretty nifty. So, I tried to build upon this concept with multiple ‘waves’ and integrate them into an animated hero header, that I think could look pretty cool and grab the user’s attention.

I’ve created a header section for a fake brand called ‘The Old Volks Home’ 🙂

You can take the demo for a spin here or watch the video on YouTube.

1. Inspiration Behind This Tutorial

Below is a video of the animation in action, and the inspiration for this tutorial. Also, hat tip to the designers/developers who worked on this website, beautiful.

2. Let’s Make Some Waves!

Let’s kick this creative hero tutorial off by designing some waves.

I was originally thinking about creating the background waves in Adobe Illustrator but decided against, as I’d be tweaking things until the cows come home. No, I took to Google and searched ‘svg wave generator’ or some variation, and came across this awesome waves generator that did the job for me!

With the sizing, and colours of my wave tweaked a little, I downloaded the SVG and imported it into Illustrator to separate the layers and export each of them, five in total.

Here is what we’re left with after export.

3. Exporting Individual Waves From Adobe Illustrator

Next, I just separated out each layer and exported the five layers into SVG files.

With our primary images ready to go, let’s begin coding this puppy up.

4. Create & Set Up The HTML and CSS

We’ll be using a HTML file, and a CSS file to complete this web development tutorial.

  • index.htm
  • style.css
<!doctype html>
<html>
   <head>

      <title>Demo: Creative Animated Hero | Frontend Hero</title>

      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <link rel="shortcut icon" href="/wp-content/themes/feh-v1/favicon.png" type="image/x-icon">
      <link rel="stylesheet" id="uniform-css" href="/wp-content/themes/feh-v1/fonts/fonts.css" type="text/css" media="all">
      <link rel="stylesheet" type="text/css" href="style.css">
   </head>

   <body>

      <header id="primary-header">...</header>

      <div id="hero-area">

      </div>

   </body>
</html>
/***********************************************************************
* Quick and dirty reset & wrapper set-up
***********************************************************************/

* { padding: 0; margin: 0; }
html, body { height: 100%; font-family: uniform; }

.wrapper {
   position: relative;
   width: 100%;
   max-width: 1100px;
   margin: 0 auto;
   padding: 0 15px;
   height: 100%;
}

Notes

  • In the index.htm file, we’ll only be concentrating on the hero area #hero-area, and the style.css for this tutorial. THe other areas, such as header are only for aesthetics.
  • I’ve reset the margins and paddings for all elements, and added a centered wrapper.

5. Creating the Waves HTML Structure

In our index.htm, or more specifically, inside of our #hero-wrapper – we’ll pop in our five wave divs.

So each wave image will be a div, with a generic class called wave, and a specific class called wave-1, and so on.

<div id="hero-area">

   <div class="wave wave-5"></div> 
   <div class="wave wave-4"></div>
   <div class="wave wave-3"></div>
   <div class="wave wave-2"></div>
   <div class="wave wave-1"></div>

</div>

6. Adding the Wave SVGs as Background Images

We’ve got our wave SVGs and our HTML markup ready, now let’s marry the two and use CSS to bring it to life.

6.1. Adding Some Background Colour to Our Animated Hero Header

First, we’ll add in the primary background colour to our #hero-area, give it a height, and set the position to relative – as we’ll be absolute positioning the waves (and eventually the content) inside.

  • style.css
  • Result
#hero-area {
   background: #de466c;
   height: 500px;
   position: relative;
}

6.2. Adding Our First Wave Background Image

Let’s add in our feh-waves-1.svg image, which will correspond to the wave-1 div, set the height/width, and position it to the bottom of the animated hero header.

  • style.css
  • Result
.wave-1 {
   background-image: url(images/feh-waves-1.svg);
   background-size: cover;
   width: 100%;
   height: 24%; 
   position: absolute; 
   bottom: 0;
}

Notes

  • background-image: url(images/feh-waves-1.svg); – We’ve included our first wave image.
  • background-size: cover; – We’ve set the background size of the image to cover as this will preserve the ratio of the image while scaling it to fill 100% width of our #hero-area.
  • width: 100%; – Make the .wave-1 div 100% width of the #hero-area.
  • height: 24%; – Make the .wave-1 24% height of the #hero-area. (I played around here and tweaked the height by eye).
  • position: absolute; – I’ve given the .wave-1 div an absolute position as this will be positioned relatively to the parent #hero-area.
  • bottom: 0; – I’ve positioned the very bottom of the parent #hero-area.

Below should help visualising things a bit better.

6.3. Cleaning Up CSS & Using Generic Wave Class

Things are starting to look pretty good with our first wave added. We could go ahead and create the rest of the waves but first let’s rejig some CSS around to make it more efficient.

You see, we are using code that will be used in the other four wave divs, so we want to create a class that we’ve already added in the index.htm and this generic wave will hold the same styles that each wave will need.

So this code…

  • style.css
.wave-1 {
   background-image: url(images/feh-waves-1.svg);
   background-size: cover;
   width: 100%;
   height: 24%; 
   position: absolute; 
   bottom: 0;
}

… will become

  • style.css
.wave {
   position: absolute;
   background-size: cover;
   width: 100%;
   bottom: 0;
}
.wave-1 { 
   background-image: url(images/feh-waves-1.svg);
   height: 24%;
}

So we’ve removed code that would have been repeated on every wave, and popped it into a newly created wave class. Now we don’t have to add all of these CSS declarations to each .wave-1, .wave-2 and so on – each wave will inherit these styles.

7. Add the Rest of the Wave Classes/Background Images

With our .wave-1 class created, let’s copy and paste this style block to create the other four classes. Then increment the numbers in both the class name, and the image URL value.

Once we’ve done that, tweak the height for each of the copied classes. Again, I’ve done this by eye and the following heights look pretty good to me.

  • style.css
  • Result
.wave-1 { 
   background-image: url(images/feh-waves-1.svg); 
   height: 24%; 
}
.wave-2 {
   background-image: url(images/feh-waves-2.svg);
   height: 43%;
}
.wave-3 {
   background-image: url(images/feh-waves-3.svg);
   height: 70%;
}
.wave-4 {
   background-image: url(images/feh-waves-4.svg);
   height: 82%;
} 
.wave-5 {
   background-image: url(images/feh-waves-5.svg);
   height: 92%;
}		

Looking pretty good! We have a tasty background made up of five images, and the #hero-area background colour too, so it looks like we have six waves in total 🌊.

8. Animating Our Waves

Now we’re going to add a psuedo ::after element to the .wave class, which every wave will inherit.

8.1. Adding ::after Psuedo Element to Each Wave Div

The following snippet will fill each .wave div with the same background colour as our #hero-area, essentially making the entire #hero-area appear as one colour.

  • style.css
  • Result
.wave::after {
   content: "";
   position: absolute;
   height: 100%;
   width: 100%;
   background-color: #de466c;
}

8.2. Creating CSS Keyframes

Now that every .wave div appears hidden, we’re going to create a CSS keyframe animation called waveWidth that will decrease the width of each .wave::after psuedo element, while at the same time, moving the psuedo element to the very right hand side of the parent element #hero-area.

This will give the effect of the wave coming into view from left to right.

  • style.css
@keyframes waveWidth {
   0% {
      left: 0;
      width: 100%
   }
   100% {
      left: 100%;
      width: 0
   }
}

Great stuff, but if you view the result, nothing has changed.

We need to hook up our waveWidth CSS keyframe function to our .wave::after psuedo elements.

8.3. Making CSS Keyframe Animation Come to Life

We’ll need to add in a few CSS declarations inside of our .wave::after element to make the animation work. We’ll separate out each animation sub-property, instead of using shorthand, just to make things clearer.

  • style.css
  • Result
.wave::after {

   ...

   animation-name: waveWidth;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 500ms;
}

Notes

  • animation-name: waveWidth; – Specifies our waveWidth CSS keyframes at-rules.
  • animation-timing-function; – How our animation will progress through the duration of animation iteration.
  • animation-fill-mode: forwards; – Specifies what happens after the animation ends.
    We use forwards as the animation will freeze the last keyframe.
  • animation-500ms; – The duration of our animation, in milliseconds.

9. Staggering our Animated Waves

We now have a nifty animation that works, with each wave ‘moving’ from left to right. But as all of the waves move at the same time, the effect looks pretty poor. Let’s stagger them to improve the experience.

We need to create five new psuedo ::after elements to target each wave, and set a little delay time of a few milliseconds for each, using the animation-delay property.

  • style.css
  • Result
.wave-1 {
   ...
}
.wave::after {
   ...
}
...

.wave-1::after { animation-delay: 650ms; }
.wave-2::after { animation-delay: 880ms; }
.wave-3::after { animation-delay: 950ms; }
.wave-4::after { animation-delay: 1110ms; }
.wave-5::after { animation-delay: 1250ms; }

Excellent, let’s carry on with adding some content in next.

10. Adding in the Hero Content

It wouldn’t be a hero section without some content! Below I’m going to add a heading, some text, a button, and an image.

You have many ways to vertically center these two hero area pieces #hero-content and the #hero-img, such as flex or grid, hell even floats – but I’m going to absolute position them and vertically center them.

  • index.htm
  • style.css
  • Result
<div id="hero-area">

   ...
   <div class="wrapper">
      <div id="hero-content">
         <h2>Welcome to<br>The Old Volks Home</h2>
         <p>Pellentesque id pellentesque lorem, id suscipit felis. Cras pellentesque sem id porttitor condimentum. Integer bibendum.</p>
         <a class="btn" href="#">Find out more</a>
      </div>
      <img id="hero-img" src="images/vw.png" alt="An ilustrated VW retro bus, in pink">
   </div>
</div>
...

#hero-content {
   position: absolute;
   width: 55%;
   transform: translateY(-50%);
   left: 0;
   top: 50%;
}
#hero-img {
   position: absolute;
   width: 40%;
   transform: translateY(-50%);
   right: 0;
   top: 50%;
}

Everything is in it’s correct position, we just need to style the various pieces of text, and re-size the image a tad in the next step.

Notes

  • I absolute position both the #hero-content div and the #hero-img image, use transform: translateY(-50%); and top: 50%, to vertically center them.
  • I give the content area a width of 55% and 40% for the image.

11. Styling our Hero Content

I won’t go into too much detail here as this should be pretty basic stuff for the majority of you, but I’m going to change all of the text to white, tweak sizes of text/line height and make a link look like a button.

  • style.css
  • Result
...

   #hero-content * {
      color: #fff;
   }
   #hero-content h2 {
      text-transform: uppercase;
      font-size: 40px;
      line-height: 52px;
      margin-bottom: 18px;
   }
   #hero-content p {
      font-size: 22px;
      line-height: 32px;
      margin-bottom: 18px;
   }
   #hero-content .btn {
      border-radius: 10px;
      padding: 15px 25px;
      display: inline-block;
      background-color: #4781DE;
      text-decoration: none;
      font-size: 18px;
   }

Very nice, all that’s left to do is animate and fade in our text and VW image.

12. Creating a Keyframes Animation for Our Text & Image

We’re going to create another keyframe animation, as we did previously with our waves, but this time we’re going to animate each element inside #hero-content which will include:

  • h2
  • p
  • a.btn
  • img

We’re creating two separate keyframes in the below snippet, for the text and image as I only want to image to fade into view, not animate’s its position.

  • style.css
...

@keyframes heroContent {
   0% {
      opacity: 0;
      transform: translateY(10px)
   }
   100% {
      opacity: 1;
      transform: translateY(0)
   }
}

@keyframes heroImg {
   0% {
      opacity: 0;
   }
   100% {
      opacity: 1;
   }
}

Notes

  • We’ve created two keyframe animations heroContent, and heroImg.
  • heroContent will move up 10px, and fade in to view on page load.
  • heroImg will just fade in.

13. Animating and Fading In Our Hero Content / Image

Now we’re going to wrap things up and add in our two newly created keyframes.

We’ll use a wildcard * to target everything inside #hero-content – to set up some animation defaults, and make all elements’s opacity hidden.

  • style.css
...

#hero-content * {
   color: #fff;
	
   opacity: 0;
   animation-name: heroContent;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 300ms;
   animation-delay: 1200ms;
}

Notes

  • We make all element’s opacity 0, making them hidden.
  • heroContent is the animation name that will be used for all text inside our #hero-content div.
  • I’ve given the animation a delay of 1,200 milliseconds, to account for the wave animation to basically finish.

13.1 Staggering the Text Animation

The text animation inside #hero-content looks pretty good, but we’ll stagger each element like before to make it things bit more polished.

  • style.css
#hero-content h2 {
   ...
   animation-delay: 1350ms;
}
#hero-content p {
   ...
   animation-delay: 1550ms;
}
#hero-content .btn {
   ...
   animation-delay: 1850ms;
}

Looking pretty darn good. Let’s sort out the image and we’re done for the day.

13.2 Fading In the Image

We’ll use our heroImg for the animation name here.

  • style.css
#hero-img {
   ...
   opacity: 0
   animation-name: heroImg;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 400ms;
   animation-delay: 1900ms; 
}

Notes

  • We’ll again, set the opacity to hidden for our VW bus.
  • The image of the VW bus will be the very last thing that gets animated, so we’ll give it a delay of nearly 2 seconds, or 1,900 milliseconds.

14. Conclusion & Code

And there you have it, a pretty cool use of CSS keyframes, a few images and some animation delays to create something that’s pretty unique – to showcase something important for your business etc.

You can either copy the code below or fork it from Github.

  • index.htm
  • style.css
<!doctype html>
<html>

   <head>

      <meta charset="UTF-8">
      <title>Animated Hero Area | Frontend Hero</title>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <link rel="shortcut icon" href="/wp-content/themes/feh-v1/favicon.png" type="image/x-icon">
		<link rel="stylesheet" id="uniform-css" href="/wp-content/themes/feh-v1/fonts/fonts.css" type="text/css" media="all">

   <link rel="stylesheet" type="text/css" href="style.css">

   </head>
 
   <body>
		
      <div id="hero-area">
         <div class="wave wave-5"></div> 
         <div class="wave wave-4"></div>
         <div class="wave wave-3"></div>
         <div class="wave wave-2"></div>
         <div class="wave wave-1"></div>
			
         <div class="wrapper">
            <div id="hero-content">
               <h2>Welcome to<br>The Old Volks Home</h2>
               <p>Pellentesque id pellentesque lorem, id suscipit felis. Cras pellentesque sem id porttitor condimentum. Integer bibendum.</p>
               <a class="btn" href="#">Find out more</a>
            </div>
            <img id="hero-img" src="images/vw.png" alt="An ilustrated VW retro bus, in pink">
         </div>
      </div>
			
   </body>

</html>
/**
* Quick and dirty reset & wrapper set-up
*/
* {
   padding: 0;
   margin: 0;
}
html,
body {
   height: 100%;
   font-family: uniform;
}

.wrapper {
   position: relative;
   width: 100%;
   max-width: 1100px;
   margin: 0 auto;
   padding: 0 15px;
   height: 100%;
}

/**
* Header
*/
#primary-header {
   background: #eee;
   padding: 35px 0px;
}
.logo {
   height: 45px;
   display: block;
}

/** 
* Hero area
*/
#hero-area {
   background: #de466c;
   height: 500px;
   position: relative;
}

/** 
	* Wave background images
	*/
.wave {
   height: 100%;
   width: 100%;
   position: absolute;
   background-size: cover;
   bottom: 0;
}
.wave::after {
   content: "";
   position: absolute;
   height: 100%;
   width: 100%;
   background-color: #de466c;
   animation-name: waveWidth;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 1200ms;
}

/** 
	* Individual waves & ::after pseudo elements
	*/
.wave-1 {
   background-image: url(images/feh-waves-1.svg);
   height: 24%;
}
.wave-1::after {
   animation-delay: 650ms;
}

.wave-2 {
   background-image: url(images/feh-waves-2.svg);
   height: 43%;
}
.wave-2::after {
   animation-delay: 800ms;
}

.wave-3 {
   background-image: url(images/feh-waves-3.svg);
   height: 70%;
}
.wave-3::after {
   animation-delay: 950ms;
}

.wave-4 {
   background-image: url(images/feh-waves-4.svg);
   height: 82%;
}
.wave-4::after {
   animation-delay: 1100ms;
}

.wave-5 {
   background-image: url(images/feh-waves-5.svg);
   height: 92%;
}
.wave-5::after {
   animation-delay: 1250ms;
}

/** 
	* Hero content
	*/
#hero-content {
   position: absolute;
   width: 55%;
   transform: translateY(-50%);
   left: 0;
   top: 50%;
}
#hero-content * {
   color: #fff;
   animation-name: heroContent;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 300ms;
   animation-delay: 1200ms;
   opacity: 0;
}
#hero-content h2 {
   text-transform: uppercase;
   font-size: 40px;
   line-height: 52px;
   margin-bottom: 18px;
   animation-delay: 1350ms;
}
#hero-content p {
   font-size: 22px;
   line-height: 32px;
   margin-bottom: 18px;
   animation-delay: 1550ms;
}
#hero-content .btn {
   border-radius: 10px;
   padding: 15px 25px;
   display: inline-block;
   background-color: #4781de;
   text-decoration: none;
   font-size: 18px;
   animation-delay: 1850ms;
}

/** 
	* Hero image
	*/
#hero-img {
   position: absolute;
   width: 40%;
   transform: translateY(-50%);
   right: 0;
   top: 50%;
   opacity: 0;

   animation-name: heroImg;
   animation-timing-function: ease-in-out;
   animation-fill-mode: forwards;
   animation-duration: 400ms;
   animation-delay: 1900ms;
}

/**
* Keyframes animations
*/
@keyframes waveWidth {
   0% {
      left: 0;
      width: 100%;
   }
   100% {
      left: 100%;
      width: 0;
   }
}

@keyframes heroContent {
   0% {
      opacity: 0;
      transform: translateY(10px);
   }
   100% {
      opacity: 1;
      transform: translateY(0);
   }
}

@keyframes heroImg {
   0% {
      opacity: 0;
   }
   100% {
      opacity: 1;
   }
}

If this tutorial has helped you, I wouldn't say no to a coffee as a tip ☕️

Buy Me a Coffee at ko-fi.com

Leave a Reply

Your email address will not be published. Required fields are marked *