{"_id":"@copperdesign/lazy-video-backgrounds","name":"@copperdesign/lazy-video-backgrounds","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@copperdesign/lazy-video-backgrounds","version":"0.1.0","description":"Sequentially-looping background-video playlist with progressive lazy loading and pause-when-offscreen.","type":"module","main":"index.js","module":"index.js","exports":{".":"./index.js"},"keywords":["video","background","hero","playlist","lazy","loop","intersection-observer"],"author":{"name":"Christian Fillies","email":"christian@manolab.com","url":"https://christianfillies.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/copperdesign/lazy-video-backgrounds.git"},"homepage":"https://github.com/copperdesign/lazy-video-backgrounds#readme","bugs":{"url":"https://github.com/copperdesign/lazy-video-backgrounds/issues"},"publishConfig":{"access":"public","provenance":true},"_id":"@copperdesign/lazy-video-backgrounds@0.1.0","gitHead":"294ca142762a054d7d683775d327d4b483e93fee","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-jYrc+RLWB2slU+J9sTI9ZWszpz9hA6SwP9Sdvi5jVeDWW4oXrjjY/45UOYLi0QxO7qXi/04/9Nx/l5FyyaiwyA==","shasum":"c3ca98c5a007c8a8374f596b430aa20324f91541","tarball":"https://registry.npmjs.org/@copperdesign/lazy-video-backgrounds/-/lazy-video-backgrounds-0.1.0.tgz","fileCount":4,"unpackedSize":11887,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@copperdesign%2flazy-video-backgrounds@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFfQNpbG/nZ+BVWcOpUg/OKlkyfhXmIhvd/p2GfwNXUhAiEAsMfYK0A68uAFRKZlqgItQiy03dGg0HwhwVhLNqDoqD0="}]},"_npmUser":{"name":"copperdesign_parent","email":"contact@christianfillies.com"},"directories":{},"maintainers":[{"name":"copperdesign_parent","email":"contact@christianfillies.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lazy-video-backgrounds_0.1.0_1781519887842_0.5125089587632783"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-15T10:38:07.679Z","0.1.0":"2026-06-15T10:38:07.975Z","modified":"2026-06-15T10:38:08.418Z"},"maintainers":[{"name":"copperdesign_parent","email":"contact@christianfillies.com"}],"description":"Sequentially-looping background-video playlist with progressive lazy loading and pause-when-offscreen.","homepage":"https://github.com/copperdesign/lazy-video-backgrounds#readme","keywords":["video","background","hero","playlist","lazy","loop","intersection-observer"],"repository":{"type":"git","url":"git+https://github.com/copperdesign/lazy-video-backgrounds.git"},"author":{"name":"Christian Fillies","email":"christian@manolab.com","url":"https://christianfillies.com"},"bugs":{"url":"https://github.com/copperdesign/lazy-video-backgrounds/issues"},"license":"MIT","readme":"# @copperdesign/lazy-video-backgrounds\n\nSequentially-looping background-video playlist with progressive lazy loading and pause-when-offscreen.\n\nDrop a row of `<video>` elements into a container, point this at the container, and they'll play one at a time, loop the current one while the next buffers, and pause automatically when you scroll away. No framework. No build step. ~1 KB minified.\n\n```html\n<div id=\"hero\">\n  <video preload=\"auto\" muted playsinline>\n    <source src=\"clip-1.mp4\" type=\"video/mp4\">\n  </video>\n  <video preload=\"auto\" muted playsinline>\n    <source src=\"clip-2.mp4\" type=\"video/mp4\">\n  </video>\n  <video preload=\"auto\" muted playsinline>\n    <source src=\"clip-3.mp4\" type=\"video/mp4\">\n  </video>\n</div>\n\n<script type=\"module\">\n  import lazyVideoBackgrounds from '@copperdesign/lazy-video-backgrounds';\n  lazyVideoBackgrounds(document.getElementById('hero'));\n</script>\n```\n\nThat's the whole API.\n\n## What it does\n\n- **Plays one clip at a time.** Children of the root are taken in DOM order.\n- **Lazy-loads the rest.** Browsers usually only buffer the *playing* video. When one starts, the next one's `load()` is kicked, so by the time the current finishes, the next is ready.\n- **Loops the current while next is still buffering.** No black-frame stalls. Once the next is ready, it takes over on the following `ended`.\n- **Pauses when offscreen.** An `IntersectionObserver` on the root pauses the active video when it scrolls out of view and resumes when it scrolls back in.\n- **Tags state in CSS.** The currently-active video gets `.is-playing` or `.is-paused` — use these in your own styles for fades, overlays, etc.\n\n## Install\n\n```sh\nnpm install @copperdesign/lazy-video-backgrounds\n```\n\nOr vendor [`index.js`](./index.js) directly — it's a single file with no dependencies.\n\n## API\n\n```js\nimport lazyVideoBackgrounds from '@copperdesign/lazy-video-backgrounds';\n\nconst teardown = lazyVideoBackgrounds(root, options);\n```\n\n### `root`\n\nThe container element. Its direct `<video>` children are the playlist. Non-video children are ignored, so you can sprinkle in overlays, captions, etc.\n\n### `options`\n\n| Option | Default | Description |\n|---|---|---|\n| `autoplay` | `true` | Pick a starting clip and play it on init. Set `false` to take over playback control yourself. |\n| `random` | `true` | Starting clip is random. Set `false` to start at the first child. |\n| `pauseWhenOffscreen` | `true` | Pause the active video when the root scrolls out of the viewport. |\n| `classNames.playing` | `'is-playing'` | Class added to the active, playing video. |\n| `classNames.paused` | `'is-paused'` | Class added to the active, paused video. |\n\n### Return value\n\nA `teardown()` function. Call it to remove every event listener and disconnect the observer — useful in SPAs when the host element is unmounted.\n\n```js\nconst teardown = lazyVideoBackgrounds(root);\n// ...later\nteardown();\n```\n\n## Markup recommendations\n\n- Use `preload=\"auto\"` on every video. It's a hint browsers may ignore, but you give the lazy-load logic the best chance of finding things already-warm.\n- Use `muted` and `playsinline` — without them, mobile and autoplay policies will reject `play()`.\n- Don't put `loop` on the videos. The script chains them on `ended`; `loop` would prevent that event from firing.\n\n```html\n<video preload=\"auto\" muted playsinline>\n  <source src=\"clip.mp4\" type=\"video/mp4\">\n  <source src=\"clip.webm\" type=\"video/webm\">\n</video>\n```\n\n## CSS hooks\n\nThe currently active video carries `.is-playing` or `.is-paused`. Stack the videos absolutely, default them to `opacity: 0`, and fade in the playing one:\n\n```css\n#hero { position: relative; }\n#hero video {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  opacity: 0;\n  transition: opacity 200ms ease-out;\n}\n#hero video.is-playing { opacity: 1; }\n```\n\n## Why this exists\n\nA common hero pattern: several short clips that play back-to-back as a moving background. The naive implementation — five `<video>` tags with `preload=\"auto\"` and a sequencer that swaps them on `ended` — falls into two traps:\n\n1. **Browsers don't actually preload non-playing videos.** `preload=\"auto\"` is advisory. In practice only the playing video buffers; the others stay at `readyState 0`. The naive sequencer waits for `canplaythrough` on the next clip and waits forever.\n2. **`canplaythrough` is a fire-and-forget event.** If it fires before your handler attaches (a real race with small clips), you'll never observe it.\n\nThis module fixes both: it seeds readiness from `readyState` *and* listens for the event, and it actively triggers `next.load()` when the current clip starts playing.\n\n## Contributing\n\nPRs and issues welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for setup,\nthe PR workflow, and what fits the scope of the module. The repo follows\nthe [Contributor Covenant](CODE_OF_CONDUCT.md).\n\nQuick version: fork, branch off `main`, exercise your change against\n`example.html` in at least one non-Chromium browser, open a PR. I\n(@copperdesign) review and merge.\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n\nCreated by [Christian Fillies](https://www.christianfillies.de).\n","readmeFilename":"README.md","_rev":"1-eed14cebb418f2134b011091ae4e9f45"}