DEV Community

loading...
Cover image for How to Create a Video Landing Page with HTML and CSS

How to Create a Video Landing Page with HTML and CSS

Teri Eyenike
Software engineer 💻․ In 💙 with React ․ NodeJs ․ JavaScript ․ Python
Updated on ・8 min read

Video Landing Page.gif

If you want to be a better front-end developer, one of the many projects you are bound to do is building a landing page. Whether it is embedded with a video or just a page mixed with images and text, you are encouraged to take on this task to challenge yourself.

This article will give you insights on how to create and include a video into a landing page using HTML and CSS with JavaScript for interactivity on the nav menu on mobile.

To utilise the power of a landing page for your marketing campaigns, it should spark an interest in your target audience. In all of this, let us know what a landing page is.

What is a landing page?

According to Mailchimp, they say:

A landing page is a standalone web page that a person "lands" on after clicking through from an email, ad, or other digital location.

At the end of this tutorial, we will gain mastery of how to create a landing page from scratch including an enticing headline to reel users in and a call-to-action (CTA).

What we will make

We completed the entire project on Codepen and here is what the final project will look like.

complete view

It is an interesting one, and I know you are already excited to begin.

super excited

Prerequisites

What do we need to complete this project?

  • Our good old friend, HTML and CSS skills.

  • A text editor (I'd suggest VS Code) or use any other editor you are comfortable with.

For this project, we wrote all the code snippets in codepen, an online text editor.

Shall we...begin?

Since we are using Codepen, in the index.html file, include all the necessary syntax needed. In the index.html file, we have:

  • A section tag with a class showcase.

  • Within the class showcase, we will use nested containers such as tag for our navigation menu.

  • The video HTML element embeds a media player that playback the video continuously in a loop.

  • The overlay class in a div container will help us to create an effect to reduce the opacity on the video HTML element and also add a blend mode to the video.

  • A div container text which includes the following.

  • A header <h2> which reads Never stop as the title leading to the tagline with another header <h3> and a <p> tag which describes the purpose of the header. Finally, we have a CTA in a link tag that says Explore.

  • An unordered list, <ul> with the class social will contain all the social links in an <img> tag.

  • Finally, <nav> tag will display all the menu on a mobile view and a <div> class toggle that switch with a close and a hamburger menu icon when clicked.

Let's have a look at our HTML file:

<!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" />
    <title>Travel video landing page</title>
  </head>
<body>
    <section class="showcase">
      <header>
        <h2 class="logo">Travel</h2>
        <nav class="desktop">
          <ul>
            <li>
              <a href="#" class="nav__link nav__link--active">Home</a>
            </li>
            <li>
              <a href="#" class="nav__link">News</a>
            </li>
            <li>
              <a href="#" class="nav__link">Destination</a>
            </li>
            <li>
              <a href="#" class="nav__link">Blog</a>
            </li>
            <li>
              <a href="#" class="nav__link">Contact</a>
            </li>
          </ul>
        </nav>
      </header>

      <video
      src="https://assets.mixkit.co/videos/preview/mixkit-tropical-island-landscape-view-4692-large.mp4"
        muted
        loop
        autoplay
      ></video>

      <div class="overlay"></div>

      <div class="text">
        <h2>Never Stop</h2>
        <h3>Exploring The World</h3>
        <p>
          View of the tropical island landscape, from a hill with houses, palm
          trees and many trees, and in the distance the hills that surround the
          sea, on a sunny day.
        </p>
        <a href="#">Explore</a>
      </div>

      <ul class="social">
        <li>
          <a href="#"><img src="https://i.ibb.co/x7P24fL/facebook.png" /></a>
        </li>
        <li>
          <a href="#"><img src="https://i.ibb.co/Wnxq2Nq/twitter.png" /></a>
        </li>
        <li>
          <a href="#"><img src="https://i.ibb.co/ySwtH4B/instagram.png" /></a>
        </li>
      </ul>
    </section>

    <nav class="menu mobile">
      <div class="toggle"></div>
      <ul>
        <li class="toggle__list">
          <a href="#" class="nav__link">Home</a>
        </li>
        <li class="toggle__list">
          <a href="#" class="nav__link">News</a>
        </li>
        <li class="toggle__list">
          <a href="#" class="nav__link">Destination</a>
        </li>
        <li class="toggle__list">
          <a href="#" class="nav__link">Blog</a>
        </li>
        <li class="toggle__list">
          <a href="#" class="nav__link">Contact</a>
        </li>
      </ul>
      <p class="footer">Designed and built by Teri</p>
    </nav>
    <script src="./app.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have just gone through everything we need for the rest of our build. What we will do next is to make the building block, the HTML more beautiful with CSS.

In our style.css file, we need to have the following properties which you can get from the complete stylesheet.

@import url('https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display=swap');

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

img {
  max-width: 100%;
}

body {
  font-family: 'Poppins', sans-serif;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 700;
}


a {
  text-decoration: none;
}

li {
  list-style-type: none;
}

header {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1em;
  z-index: 999;
}

h2, h3 {
  text-transform: uppercase;
}

header .logo {
  color: #fff;
  cursor: pointer;
}

.desktop {
  display: none;
}

.toggle {
  position: absolute;
  top: -0.5em;
  right: 1em;
  width: 60px;
  height: 60px;
  background: url(https://i.ibb.co/HrfVRcx/menu.png);
  background-repeat: no-repeat;
  background-size: 30px;
  background-position: center;
  cursor: pointer;
  z-index: 5;
  transition: all 0.3s ease-in 0.35s;
}

.toggle.active {
  background: url(https://i.ibb.co/rt3HybH/close.png);
  background-repeat: no-repeat;
  background-size: 25px;
  background-position: center;
  cursor: pointer;
}

.showcase {
  position: absolute;
  right: 0;
  width: 100%;
  min-height: 100vh;
  padding: 1em;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #111;
  transition: all 0.3s ease;
  z-index: 2;
}

.showcase.active {
  right: 100%;
}

.showcase video {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  object-fit: cover;
  opacity: 0.25;
}

.overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: #78c3fb;
  mix-blend-mode: overlay;
}

.text {
  position: relative;
  z-index: 10;
}

.text h2 {
  color: #fff;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1em;
}

.text h3 {
  font-size: 2.5em;
  font-weight: 700;
  color: #fff;
  line-height: 1em;
}

.text p {
  font-size: 1.1em;
  color: #fff;
  margin: 1.25em 0;
  font-weight: 400;
}

.text a {
  display: inline-block;
  font-size: 1rem;
  background: #fff;
  padding: 10px 30px;
  text-transform: uppercase;
  font-weight: 500;
  margin-top: 10px;
  color: #111;
  letter-spacing: 2px;
  transition: letter-spacing 0.2s ease-in;
}

.text a:hover {
  letter-spacing: 6px;
}

.social {
  position: absolute;
  z-index: 10;
  bottom: 1.25em;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.social li a {
  display: inline-block;
  filter: invert(1);
  transform: scale(0.5);
  transition: transform 0.3s ease-in;
}

.social li a:hover {
  transform: scale(0.5) translateY(-0.94em);
}

.social > * + * {
  margin-left: 1.25em;
}

.menu {
  position: absolute;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
}

.menu ul li a {
  font-size: 1.5rem;
  color: #111;
}

.menu ul li a:hover {
  color: #03a9f4;
}

.menu ul > * + * {
  margin-top: 2.5em;
}

.menu .toggle.active {
  filter: invert(1);
}

.menu .footer {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  bottom: 0;
  left: 0;
  font-size: 0.65rem;
  width: 100%;
  color: rgb(119, 118, 118);
}

@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 1) {
  header {
    padding: 3em;
  }
  .showcase {
    padding: 3em;
  }

  .text p {
    max-width: 650px;
    font-size: 1.3rem;
  }
  .text h2 {
    font-size: 5rem;
  }

  .text h3 {
    font-size: 4rem;
  }

  .text a {
    font-size: 1.5rem;
  }

  .toggle {
    top: 2.15em;
  }

  .menu ul li a {
    font-size: 3.5rem;
  }
}

@media (min-width: 1024px) {
  .text p {
    max-width: 700px;
  }

  .desktop {
    display: block;
  }

  .desktop ul {
    display: flex;
  }

  .nav__link.nav__link--active {
    color: #fff;
  }

  .nav__link {
    color: #afaeae;
    font-size: 1.5rem;
  }

  .desktop ul > * + * {
    margin-left: 1.5em;
  }

  .toggle {
    display: none;
  }
}

@media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (-webkit-min-device-pixel-ratio: 2) {
  .text h2 {
    font-size: 1.3rem;
  }

  .text h3 {
    font-size: 1rem;
  }

  .text p {
    font-size: 0.75rem;
    max-width: 500px;
  }

  .menu ul li a {
    font-size: 0.85rem;
  }

  .menu ul > * + * {
    margin-top: 1.5em;
  }
}

@media only screen and (min-device-width: 375px) and (max-device-width: 667px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: landscape) {
  .text h2 {
    font-size: 1rem;
  }
  .text h3 {
    font-size: 1.5rem;
  }

  .text p {
    font-size: 0.85rem;
    max-width: 600px;
  }

  .menu ul li a {
    font-size: 1rem;
  }

  .menu ul > * + * {
    margin-top: 1.5em;
  }
}

@media only screen and (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape) {
  .text h2 {
    font-size: 1rem;
  }
  .text h3 {
    font-size: 1.5rem;
  }

  .text p {
    font-size: 0.85rem;
    max-width: 600px;
  }
  .menu ul li a {
    font-size: 1rem;
  }

  .menu ul > * + * {
    margin-top: 1.5em;
  }
}

@media only screen and (min-device-width: 375px) and (max-device-width: 812px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape) {
  .text h2 {
    font-size: 1rem;
  }
  .text h3 {
    font-size: 1.5rem;
  }

  .text p {
    font-size: 0.85rem;
    max-width: 600px;
  }

  .text a {
    font-size: 0.85rem;
  }

  .menu ul li a {
    font-size: 1rem;
  }

  .menu ul > * + * {
    margin-top: 1.5em;
  }
}

@media only screen and (min-device-width: 411px) and (max-device-width: 823px) and (-webkit-min-device-pixel-ratio: 3) and (orientation: landscape) {
  .text h2 {
    font-size: 1rem;
  }
  .text h3 {
    font-size: 1.5rem;
  }

  .text p {
    font-size: 0.85rem;
    max-width: 600px;
  }

  .text a {
    font-size: 0.85rem;
  }

  .menu ul li a {
    font-size: 1rem;
  }

  .menu ul > * + * {
    margin-top: 1.5em;
  }
}
Enter fullscreen mode Exit fullscreen mode

From the above-declared properties used, we made use of media queries for the responsive layout to suit different devices and their orientation (either landscape or portrait). To find out more on how this works and its implementation, check this.

Interaction with JavaScript

This section is as important as other aspects that have been highlighted in this article. With JS, you can control how users interact with the website such as what happens when a button is clicked and so many other activities that can occur on a web app. Include this snippet of code in your app.js file.

const menuToggle = document.querySelector('.toggle')
const showcase = document.querySelector('.showcase')
const menus = document.querySelectorAll('.toggle__list')


const toggleMenu = () => {
  menuToggle.classList.toggle('active')
  showcase.classList.toggle('active')
  menus.forEach(menu => menu.classList.toggle('active'))
}


menuToggle.addEventListener('click', toggleMenu)
menus.forEach(menu => menu.addEventListener('click', toggleMenu))
Enter fullscreen mode Exit fullscreen mode

In the above code block, we queried our page using the Document Object Model (DOM) to target a specific element that we will add a click event listener that occurs when a user clicks on the menu icon, the nav menus comes into view and closes when the close icon or any of the menu links are clicked.

Conclusion

The use of a landing page for advertising campaigns is vital when you want users to get an understanding of your product with the ability for users to take quick action which is the primary objective of any landing page you find on the internet.

Resources

Discussion (2)

Collapse
lukeshiru profile image
LUKESHIRU

One other thing you could do (I'm not saying you should), is to create a function that plays the video in memory, and gives you the current frame as a CSS Custom property, so you can use it from CSS as a regular background image:

JS
/**
 * Takes a video and an element and sets the `--video-frame` custom property
 * to always match the video's current frame.
 * 
 * @param {ReadonlyArray<string> | string} videoSources Single source SRC or array of source SRCs
 */
export const videoToCustomProperty = videoSources => {
    // We create a `video` element in memory with the given sources.
    const videoElement = Object.assign(document.createElement("video"), {
        crossOrigin: "anonymous",
        innerHTML: (typeof videoSources === "string"
            ? [videoSources]
            : videoSources
        )
            .map(videoSource => `<source src="${videoSource}" />`)
            .join(""),
        loop: true,
        muted: true
    });

    // And also a canvas element
    const canvasElement = document.createElement("canvas");
    const context = canvasElement.getContext("2d");

    // Once the metadata of the video is loaded, we can update the width and
    // height of the canvas, and then start the video in memory.
    videoElement.addEventListener("loadedmetadata", () => {
        Object.assign(canvasElement, {
            width: videoElement.videoWidth,
            height: videoElement.videoHeight
        });

        return void videoElement.play();
    });

    /**
     * This function is used by requestAnimationFrame to get every frame of
     * the video.
     * 
     * @param {HTMLElement} element
     */
    const update = element => () => {
        // Draw current frame into the canvas in memory
        context?.drawImage(videoElement, 0, 0);

        // Update the `--video-frame` custom property
        element.style.setProperty(`--video-frame`, `url(${canvasElement.toDataURL("image/png")})`);

        // Keep looping
        return void requestAnimationFrame(update(element));
    };

    /**
     * Function that expects the element to set the `--video-frame` custom prop.
     * 
     * @param {HTMLElement} element
     */
    return element => void requestAnimationFrame(update(element));
};

// And then we use it:
videoToCustomProperty("url of video")(document.querySelector("element to set the background, can even be any element"));
Enter fullscreen mode Exit fullscreen mode

And then the CSS doesn't need the hacks of position for the video element, because now every frame is just an image, we can just do:

.example-element {
  background-image: var(--video-frame);
  background-size: cover;
  background-position: center center;
}
Enter fullscreen mode Exit fullscreen mode

Cheers!

Collapse
terieyenike profile image
Teri Eyenike Author

I think this is interesting. I would try this out.

Thanks for this.