Responsive video header hero with a pink flower against a dark green background
Blog

How to Create a Background Video for Header

A background video can add movement to a website header, but it should never make the message hard to read or force motion on visitors who prefer less animation. In this tutorial, you’ll build a responsive video hero with HTML, CSS, and JavaScript. It includes a still-image fallback, a manual play/pause control, and support for the reduced-motion preference.

What you’ll build

The video sits behind real HTML text. A dark overlay helps keep the heading readable, and a control lets visitors start or pause the decorative motion. The poster remains visible when video playback is unavailable. Keep important information in HTML instead of embedding it in the video.

Before you begin

Use a short, compressed video that you own or are licensed to use. Remove its audio track and prepare a still image for the poster. Large autoplay videos can use bandwidth, and browser playback may be blocked. This example is muted and includes a manual control. See MDN’s video element reference.

1. Add the HTML, CSS, and JavaScript

Copy the complete example below into an .html file to try it on its own. Replace the sample video URL and poster path with your own assets. For a larger project, keep the styles and script in separate files as shown in the download bundle.

How the example works

  • muted and playsinline make inline, silent playback possible on supported browsers.
  • preload="none" asks the browser to defer loading video data.
  • The overlay and responsive sizing keep the text readable across screen sizes.
  • The button starts or pauses playback; the poster remains as a fallback.
  • The script does not autoplay when the visitor prefers reduced motion.

The reduced-motion setting is checked with the prefers-reduced-motion media feature. Playback can still be blocked by a browser, so the control remains useful.

2. Use your own video

Replace the sample source and images/hero-poster.jpg with public URLs for your media. Keep the clip short and silent, and test the poster and video on a slower connection. Use object-position in the CSS if the subject needs a different crop.

Troubleshooting

  • The video does not autoplay: Confirm it is muted. Autoplay may still be blocked, so visitors can use the play button.
  • The hero looks blank: Check that the video and poster URLs are correct and publicly accessible.
  • The subject is cropped: Adjust object-position, for example center 35%.
  • The page feels slow: Use a shorter, lower-resolution clip, remove its audio track, and keep preload="none".

Frequently asked questions

Can I use a background video on mobile?

Yes. Use a short, muted clip with playsinline. Some devices may still block automatic playback, so keep a poster image and a manual play option.

Should a background video include sound?

A decorative background should be silent. If the media conveys information, use a regular video player with controls and captions.

What happens when reduced motion is enabled?

The example starts with the video paused for visitors who request reduced motion. They can still start it using the visible control.

Can I use this in Elementor?

Yes. Add the example as an HTML widget and place the CSS and JavaScript in an approved custom-code location. If your role or setup removes scripts from widgets, use your site’s approved custom-code method.

Related tutorials

Conclusion

A background video header works best when the text stays readable and visitors remain in control. Start with a poster, use a small muted clip, and respect reduced-motion preferences. Open the demo or download the starter files to adapt the example.

Complete code example

Copy the snippet into a standalone HTML file, then update the media paths and text for your project.

HTML: Responsive video header
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Responsive video header</title>
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; font-family: system-ui, sans-serif; }
    .video-hero { position: relative; isolation: isolate; display: grid; min-height: min(76vh, 48rem); min-height: min(76svh, 48rem); align-items: center; overflow: hidden; color: #fff; background: #152536; }
    .video-hero__media, .video-hero__shade { position: absolute; z-index: -1; inset: 0; width: 100%; height: 100%; }
    .video-hero__media { object-fit: cover; }
    .video-hero__shade { background: linear-gradient(90deg, rgb(7 17 28 / 82%), rgb(7 17 28 / 35%) 68%, rgb(7 17 28 / 18%)); }
    .video-hero__content { width: min(100% - 2rem, 70rem); margin-inline: auto; padding-block: clamp(5rem, 12vw, 10rem); }
    .video-hero__eyebrow { margin: 0 0 1rem; font-size: .875rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; }
    .video-hero h1 { max-width: 13ch; margin: 0; font-size: clamp(2.5rem, 7vw, 5.5rem); line-height: 1.02; }
    .video-hero__copy { max-width: 42rem; margin: 1.25rem 0 0; font-size: clamp(1rem, 2vw, 1.25rem); line-height: 1.6; }
    .video-hero__link { display: inline-block; margin-top: 1.5rem; padding: .85rem 1.1rem; border-radius: .35rem; color: #10202d; background: #fff; font-weight: 700; text-decoration: none; }
    .video-hero__toggle { position: absolute; right: 1rem; bottom: 1rem; padding: .65rem .85rem; border: 1px solid #fff; border-radius: .35rem; color: #fff; background: #111b; font: inherit; cursor: pointer; }
    .video-hero__link:focus-visible, .video-hero__toggle:focus-visible { outline: 3px solid #ffdd57; outline-offset: 4px; }
    @media (max-width: 40rem) { .video-hero { min-height: 34rem; } .video-hero__shade { background: linear-gradient(0deg, rgb(7 17 28 / 88%), rgb(7 17 28 / 30%)); } }
    @media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; } }
  </style>
</head>
<body>
  <section class="video-hero" aria-labelledby="hero-title">
    <video class="video-hero__media" muted loop playsinline preload="none" poster="images/hero-poster.jpg" aria-hidden="true">
      <source src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4" type="video/mp4">
    </video>
    <div class="video-hero__shade" aria-hidden="true"></div>
    <div class="video-hero__content">
      <p class="video-hero__eyebrow">A short introduction</p>
      <h1 id="hero-title">Make your first impression move.</h1>
      <p class="video-hero__copy">Use a concise message over a subtle, decorative background video.</p>
      <a class="video-hero__link" href="#more">Explore the example</a>
    </div>
    <button class="video-hero__toggle" type="button" aria-pressed="false">Play background video</button>
  </section>
  <script>
    const video = document.querySelector('.video-hero__media');
    const toggle = document.querySelector('.video-hero__toggle');
    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
    function updateToggle() { const playing = !video.paused; toggle.textContent = playing ? 'Pause background video' : 'Play background video'; toggle.setAttribute('aria-pressed', String(playing)); }
    async function playVideo() { try { await video.play(); } catch (error) { /* Keep the poster and manual play button if playback is blocked. */ } updateToggle(); }
    toggle.addEventListener('click', () => video.paused ? playVideo() : video.pause());
    video.addEventListener('play', updateToggle);
    video.addEventListener('pause', updateToggle);
    if (!reducedMotion.matches) playVideo();
    reducedMotion.addEventListener('change', event => event.matches ? video.pause() : playVideo());
  </script>
</body>
</html>