@the-w4/responsive-video-source
v0.1.0
Published
Switches <video> <source> elements based on a data-resolution media-query attribute, so the right mp4/webm loads per breakpoint.
Maintainers
Readme
@the-w4/responsive-video-source
Switches a <video>'s <source> children based on a data-resolution
media-query attribute, so the right mp4/webm pair loads for the current
viewport instead of relying on the browser's (resolution-blind) native
<source> matching.
The problem this solves
<video> lets you list multiple <source> elements, but the browser only
uses type to pick one — it has no concept of viewport size. If you want a
different clip (or just a different encode) on mobile vs. desktop, you need
JS. This package does that by reading a data-resolution attribute you put
on each <source>:
<video autoplay muted loop playsinline>
<source src="/desktop.mp4" type="video/mp4" data-resolution="min-width: 1024px">
<source src="/desktop.webm" type="video/webm" data-resolution="min-width: 1024px">
<source src="/mobile.mp4" type="video/mp4" data-resolution="max-width: 1023px">
<source src="/mobile.webm" type="video/webm" data-resolution="max-width: 1023px">
</video>Sources are grouped by their data-resolution value. When a group's query
matches, the video is given only that group's <source> elements (so the
browser still gets to pick mp4 vs. webm based on support within the matched
group) and reloaded. Listeners are added per unique breakpoint via
matchMedia, so switching happens automatically on resize/orientation
change — no manual resize polling.
Videos with a partial data set are skipped. If a <video> has some
<source> children with data-resolution and some without, the package
leaves it completely untouched (native behavior applies). Only videos where
every <source> carries the attribute are managed. A video with no
data-resolution attributes at all is also left alone.
The attribute value can be a bare media feature ("min-width: 1024px",
wrapped in parens automatically) or a full query you write yourself
("(min-width: 1024px) and (orientation: landscape)", used as-is). Keep
breakpoints mutually exclusive (e.g. min-width / max-width pairs) —
if more than one group matches at once, the first one declared in the
markup wins.
Install
npm install @the-w4/responsive-video-sourceUsage with a bundler (e.g. @wordpress/scripts, webpack, Vite)
import { init } from '@the-w4/responsive-video-source';
const controller = init();
// later, if needed:
controller.refresh(); // re-check all managed videos now
controller.destroy(); // remove all matchMedia listenersinit(options) accepts:
| option | default | description |
|-------------|----------------------|-----------------------------------------------|
| root | document | scope to search within |
| selector | 'video' | selector for video elements |
| attribute | 'data-resolution' | attribute holding the media-query value |
Usage directly in WordPress (no build step)
Copy dist/index.global.min.js into your theme and enqueue it — it
auto-runs on DOMContentLoaded with the defaults shown above:
wp_enqueue_script(
'responsive-video-source',
get_stylesheet_directory_uri() . '/js/responsive-video-source.min.js',
array(),
'0.1.0',
true
);To opt out of auto-init (e.g. you want a custom root/selector, or to
call init() yourself), add data-no-autoinit to the script tag. WordPress
doesn't expose a clean way to add arbitrary attributes to wp_enqueue_script
output, so either filter the script_loader_tag or just init() is also
fully exposed on window.ResponsiveVideoSource for manual control:
window.ResponsiveVideoSource.init({ root: document.querySelector('.hero') });Development
npm install
npm run build # writes dist/index.mjs, .cjs, .global.js, .global.min.js
npm test # jsdom-based unit testsPublishing
npm login # if not already authenticated
npm publish # prepublishOnly runs build + test automaticallyThis is a scoped package (@the-w4/...); publishConfig.access is already
set to public so it publishes for free under the the-w4 npm scope.
