A moving background makes a landing page feel alive — product shots drifting behind the headline, ambient footage setting the mood. Done right, it's modern design. Done wrong, it's a 40 MB file blocking first paint on phones. Here's the complete recipe: the HTML, the autoplay rules, the file prep, and the performance guardrails.
The basic pattern
The modern way is a `<video>` element layered behind content — not a CSS background (CSS can't hold video natively):
```html <section class="hero"> <video class="hero-bg" autoplay muted loop playsinline poster="hero-poster.jpg"> <source src="hero.webm" type="video/webm"> <source src="hero.mp4" type="video/mp4"> </video> <div class="hero-content"> <h1>Your Headline</h1> </div> </section> ```
```css .hero { position: relative; overflow: hidden; } .hero-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; } .hero-content { position: relative; z-index: 1; } ```
Every attribute in that video tag earns its place — read on.
The four attributes that make autoplay work
Browsers block autoplay to protect users from surprise sound. The unlock conditions:
1. **`muted`** — the big one. Muted videos may autoplay everywhere; unmuted ones cannot, full stop. 2. **`playsinline`** — stops iOS Safari from going fullscreen; without it, iPhone autoplay dies. 3. **`autoplay`** — starts playback when possible. 4. **`loop`** — background video runs forever; see our looping guide for what the attribute does.
`poster` shows while the video loads — your visual holding pattern. Skip the attribute and users see a black flash.
Preparing the file: size is design
A background video is decoration; treat its bytes like an image budget:
- **Duration 6-12 seconds**, seamlessly looped — nobody watches 60 seconds of background - **Resolution 1280×720 or less** — it sits behind a dark overlay and content; 4K detail is invisible - **Bitrate 1-2 Mbps** — motion stays smooth, gradients stay clean - **H.264 MP4 + VP9/WebM pair** — MP4 everywhere, WebM smaller where supported (order matters: list WebM first)
```bash ffmpeg -i raw.mp4 -vf "scale=1280:-2,fps=24" -c:v libx264 -crf 28 \ -an -movflags +faststart hero.mp4 ```
`-an` drops audio entirely (it's muted anyway — dead bytes), `+faststart` lets playback begin before the download finishes. A 10-second result lands around 2-4 MB. That's the target.
The mobile question
Consider swapping the video for the poster image on small screens or metered connections — same mood, zero cost:
```css @media (max-width: 768px) { .hero-bg { display: none; } } ```
Or gate it with `navigator.connection.saveData` in a few lines of JS. Desktop gets cinema; phones get speed.
Accessibility: the rules that aren't optional
- **`prefers-reduced-motion`**: users who opt out of animation must get the static poster — respect it:
```css @media (prefers-reduced-motion: reduce) { .hero-bg { display: none; } } ```
- **Never put essential content only in the video** — screen readers and search engines can't watch it. The headline exists as text. - Contrast: a dark overlay between video and text keeps copy readable as footage shifts.
The performance checklist
- File ≤ 4 MB after the encode above - `poster` always set - Video `preload="metadata"` or none — don't download before needed: add `preload="none"` and let autoplay trigger it - Lazy-mount below-the-fold heroes with IntersectionObserver - Lighthouse check: the video shouldn't shift LCP; poster and content paint first
The takeaway
`autoplay muted loop playsinline` + poster, 720p at ~2 Mbps, 8-second loop, mobile fallback, reduced-motion respect. The video is the garnish — the page's job is still to load fast and read clearly with the video switched off.
No comments yet