Image → Video Hover
Illustration tiles that crossfade to a muted video on hover. Video autoplays once per hover, resets on mouse-leave. Drop-in vanilla JS component — no build step, no dependencies.
Live Demo
Hover any tile to see the crossfade. Each tile takes its own image and video source.
Raised Arms
PNG illustration → MP4 video
Raised Arms (SVG)
SVG illustration → MP4 video
Dancing Woman
SVG illustration → MP4 video
Woman with Fans
SVG illustration → MP4 video
Tongue Out
SVG illustration → MP4 video
Sketch & Draw
SVG illustration → MP4 video
Interaction Spec
| Resting state | Static illustration image (<img>) at full opacity |
|---|---|
| Hover → in | Image fades out, video fades in. 1800ms crossfade using var(--ease-out) |
| Video behaviour | Autoplays once, muted. muted attribute + volume = 0 in JS |
| Hover → out | Video fades out, image fades in. Video pauses and resets to frame 0 |
| Re-hover | Video plays again from the start |
| Video preload | Eager — preload="auto" on page load for instant playback |
| Reduced motion | Transition duration collapses to 0.01ms (instant swap) |
| Touch devices | Hover listeners skipped via (hover: hover) media query |
Usage
Include the CSS and JS, then add video-tile containers with data attributes.
<!-- 1. Include the component --> <link rel="stylesheet" href="video-tile.css"> <script src="video-tile.js"></script> <!-- 2. Add a tile --> <div class="video-tile" data-image-src="path/to/illustration.png" data-video-src="path/to/clip.mp4" data-alt="Description"> </div> <!-- 3. Optional: overlay content --> <div class="video-tile" data-image-src="illustration.svg" data-video-src="clip.mp4"> <div class="video-tile__overlay"> <!-- Your content here — z-index 3, above both layers --> </div> </div>
Programmatic API
Use VideoTile.create() to init tiles dynamically.
// Init a single element with options VideoTile.create(el, { image: 'path/to/illustration.png', video: 'path/to/clip.mp4' }); // Re-init all tiles in a scope (e.g. after AJAX load) VideoTile.init(document.querySelector('.my-container')); // Control a tile instance el.videoTile.play(); el.videoTile.stop(); el.videoTile.preload();