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();