Full-bleed background video seems straightforward until you get into the details: lazy loading for below-fold instances, accessibility considerations, giving mobile users a way out of a heavy video download, and a play/pause button that actually works well. These two components take care of all of that.
How to Use
Drop either component into any Etch section. The CSS positions the video behind the rest of the section content automatically, so placement in the structure panel doesn’t affect the output. That said, putting it last in the panel just makes the structure easier to read at a glance.
Which One Should I Use?
If your video is a file in your media library or any direct MP4 URL, use the Native Video component. No external dependencies, no setup beyond pointing it at your file.
If your video is hosted on Bunny Stream and you want adaptive bitrate streaming with automatic quality switching, use the Bunny Stream component. It also gives you a proper poster image when paused rather than freezing on the current frame.
If your video is on Bunny Stream but you don’t need adaptive bitrate, the Native Video component works fine with a direct MP4 URL too!
If you’re leaving playOnMobile enabled on, Bunny Stream is probably the better choice. Adaptive bitrate means mobile users get a smaller, lower-resolution file appropriate for their connection rather than the same full-resolution MP4 as desktop.
| Native Video | Bunny Stream | |
|---|---|---|
| Video uploaded to WordPress | ✅ | |
| Any direct MP4 URL | ✅ | |
| Hosted on Bunny Stream | ✅ | ✅ |
| Adaptive bitrate / auto quality | ✅ | |
| No external dependencies | ✅ | |
| Poster shown when paused | ✅ | |
| Freezes on current frame when paused | ✅ | |
| Lazy loading (below-fold) | ✅ |
Shared Behavior
Both components behave the same way in the areas that matter most:
- Autoplays muted and looped
- Pauses automatically when scrolled out of view, resumes when scrolled back in
- If the user manually pauses, the video won’t auto-resume on scroll
- Skips the video entirely if
prefers-reduced-motionis detected - Play/pause button is hidden at rest, revealed on hover or keyboard focus
- Dark overlay sits above the video but below any overlaid content
Native Video
The Native Video component uses a standard HTML <video> element with no external dependencies. When paused, the video freezes on the current frame.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
video |
Media | — | MP4 video file |
poster |
Media | — | Image shown before the video loads |
aboveFold |
Boolean | true |
Set to false for below-fold videos to enable lazy loading |
hasOverlay |
Boolean | true |
Dark overlay on top of the video |
enablePause |
Boolean | true |
Play/pause toggle button |
playOnMobile |
Boolean | true |
Set to false to disable video on mobile |
videoDescription |
Text | — | Optional screen-reader-only description of the video content |
Above the Fold
The aboveFold prop is worth paying attention to. When true, the video loads immediately on page load. When false, loading is deferred until the component is near the viewport. Any background video that isn’t visible on initial page load should have this set to false to avoid loading a video the user may never even see.
Bunny Stream
Most WordPress hosts aren’t configured with the right headers and caching rules to serve video efficiently, so offloading to a CDN like Bunny Stream is usually the better call.
The Bunny Stream component embeds your video via Bunny’s player iframe with adaptive bitrate streaming and automatic quality switching. When paused, the video fades out and the poster image is shown rather than freezing on a frame.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
libraryId |
Text | — | Your Bunny Stream library ID |
videoId |
Text | — | The video ID from your Bunny library |
cdnHostname |
Text | — | Your Bunny CDN hostname, used to display the auto-generate the poster |
posterUrl |
Text | Auto | Overrides the auto-generated Bunny thumbnail |
hasOverlay |
Boolean | true |
Dark overlay on top of the video |
enablePause |
Boolean | true |
Play/pause toggle button |
playOnMobile |
Boolean | true |
Set to false to show the poster only on mobile |
videoDescription |
Text | — | Optional screen-reader-only description of the video content |
Finding Your IDs
Log in to dash.bunny.net, go to Stream, and select your library. The Library ID and CDN Hostname are in the library settings. The Video ID is the UUID shown on any video’s detail page.
Since libraryId and cdnHostname are the same for every video in a given library, it’s worth setting them as defaults directly in the component. That way you only need to drop in the videoId for each instance rather than re-entering the same values every time.
Poster Image
The poster shows before the video loads and whenever the video is paused. If cdnHostname is set, the poster is auto-generated from Bunny’s thumbnail for that video. You can override it with any image URL using posterUrl.
To customize the auto-generated thumbnail: Stream → your library → click the video → Thumbnail. Scrub to any frame or upload a custom image.
Bunny CDN Settings
A few settings in your Bunny Stream library can affect how the component behaves. These are under Stream → your library → Security.
Allowed Domains
Restricts which domains can embed and play videos from your library. Add your production domain and any staging or dev domains you need. If left empty, all domains are allowed. The component will silently fail to load on any domain not on this list.
Block Direct URL File Access
When enabled, direct requests to CDN files are blocked unless they include a valid referrer header. This setting affects the auto-generated poster. If the poster fails to load, either disable this setting or use the posterUrl prop with a WordPress-hosted image instead.
Embed View Token Authentication
When enabled, the iframe embed requires a signed token. The component doesn’t support this out of the box, so leave it off unless you have server-side token generation in place.
CDN Token Authentication
Same situation as above. Requires server-side implementation and is not supported by the component as shipped.
Styling
Both components share the same CSS custom properties. Override them on the component itself or any ancestor element.
/* Overlay */
--bg-video-overlay-color: #000;
--bg-video-overlay-opacity: 0.65;
/* Button position — defaults to bottom-right */
--bg-video-btn-inset-block: auto 1rem;
--bg-video-btn-inset-inline: auto 1rem;
/* Z-index */
--bg-video-z-index: -2;
The mobile breakpoint for playOnMobile is set in CONFIG at the top of each component’s javascript. Both default to 768px.
mobileBreakpoint: 768,
Accessibility
The play/pause button uses aria-label and aria-pressed so screen readers announce the current state correctly, is fully keyboard accessible, and has a visible focus style. For anyone who has prefers-reduced-motion enabled, the video is paused by default.
The Video Description prop lets you add a screen-reader-only text description of the video content. This is optional, but worth filling in for meaningful context that a screen reader user would benefit from knowing about.