npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@smartimpact-it/modern-video-embed

v2.0.9

Published

Modern YouTube and Vimeo embed web components with lazy loading, accessibility, quality control, and comprehensive API

Readme

Video Embed Components

Modern, performant YouTube, Vimeo, and HTML5 video embed web components with lazy loading, event-driven architecture, and comprehensive API control.

📋 Quick Reference

| Component | Best For | Bundle Size (Min) | External API | | ----------------- | ------------------ | ----------------- | ------------------ | | <youtube-embed> | YouTube videos | 15KB | YouTube iframe API | | <vimeo-embed> | Vimeo videos | 14KB | Vimeo Player API | | <video-embed> | Self-hosted videos | 12KB | Native HTML5 |

Need help? See AI Instructions for development guidance.

✨ Features

  • 🎮 Full Control API - Play, pause, stop, mute, and load videos programmatically
  • 📡 Event-Driven - Listen to player events (ready, play, pause, error, etc.)
  • ⚡ Lazy Loading - Improve performance with poster images and on-demand loading
  • 🔗 Flexible URLs - Support for full URLs, short links, and video IDs
  • 📱 Responsive - Mobile-first design with touch-friendly controls
  • ♿ Accessible - Keyboard navigation and screen reader support
  • 🎨 Customizable - Custom overlay controls or native player controls
  • 🎬 Multi-Platform - YouTube, Vimeo, and self-hosted HTML5 video support

� Project Structure

si_video_embed/
├── src/
│   ├── components/          # Web Components
│   │   ├── BaseVideoEmbed.ts       # Abstract base class (shared functionality)
│   │   ├── YouTubeEmbed.ts         # YouTube iframe component
│   │   ├── VimeoEmbed.ts           # Vimeo iframe component
│   │   └── VideoEmbed.ts           # Native HTML5 video component
│   ├── types/               # TypeScript type definitions
│   └── styles/              # SCSS stylesheets
│       ├── _shared-functions.scss  # Reusable SVG encoding functions
│       ├── _embed-base.scss        # Shared component mixins
│       ├── youtube-embed.scss      # YouTube-specific styles
│       ├── vimeo-embed.scss        # Vimeo-specific styles
│       └── video-embed.scss        # Video-specific styles
├── dist/                    # Compiled output (auto-generated)
│   ├── index.js/.min.js     # Full bundle (YouTube + Vimeo + Video)
│   ├── youtube-only.js      # YouTube-only bundle (15KB min)
│   ├── vimeo-only.js        # Vimeo-only bundle (14KB min)
│   ├── video-only.js        # Native video-only bundle (12KB min)
│   └── css/                 # Compiled styles
├── examples/                # Live demos and usage examples
├── test/                    # Automated test suite (67+ tests)
└── scripts/                 # Build validation and cache-busting scripts

Key Files:

  • .ai-instructions.md - Development guide for AI agents and contributors
  • package.json - NPM scripts and dependencies
  • tsconfig.json - TypeScript configuration (strict mode)

�🚀 Quick Start

Installation

Via NPM (recommended for projects)

npm install @smartimpact-it/modern-video-embed

Via CDN (for quick prototypes)

| ----------------- | ------------------------- | ---------- | ----------- |
----------------------- | | **Full** | `dist/index.js` | 58KB | **29KB** | All
three components | | **YouTube Only** | `dist/youtube-only.js` | 29KB | **15KB**
| Only YouTube embeds | | **Vimeo Only** | `dist/vimeo-only.js` | 29KB |
**14KB** | Only Vimeo embeds | | **Video Only** | `dist/video-only.js` | 24KB |
**12KB** | Only HTML5 video embeds | | **Styles (Core)** |
`dist/css/components.css` | 9KB | **7KB** | Component styles only | | **Styles
(Full)** | `dist/css/main.css` | 276KB | **224KB** | With Bootstrap (demos)
href="https://unpkg.com/@smartimpact-it/modern-video-embed@latest/dist/css/components.css"
/>

Manual Build

git clone https://github.com/siit-dev/si_video_embed.git
cd si_video_embed
npm install
npm run build

Bundle Options

Choose the right bundle for your needs:

| Bundle | File | Size (Dev) | Size (Prod) | Use Case | | ----------------- | ------------------------- | ---------- | ----------- | ---------------------- | | Full | dist/index.js | 58KB | 29KB | Both YouTube and Vimeo | | YouTube Only | dist/youtube-only.js | 29KB | 15KB | Only YouTube embeds | | Vimeo Only | dist/vimeo-only.js | 29KB | 14KB | Only Vimeo embeds | | Styles (Core) | dist/css/components.css | 9KB | 7KB | Component styles only | | Styles (Full) | dist/css/main.css | 276KB | 224KB | With Bootstrap (demos) |

Production Files use minified versions (.min.js / .min.css) for ~50% size reduction.

Recommended for Production:

  • Use YouTube-only or Vimeo-only bundles when possible (~14-15KB minified)
  • Use Core styles (components.css) instead of full Bootstrap (~7KB minified)
  • Enable gzip compression on your server (reduces size by additional 60-70%)

Basic Usage

Include Required Files

For Production (recommended - use minified files):

<!-- Full bundle: Both YouTube and Vimeo (29KB minified) -->
<script type="module" src="dist/index.min.js"></script>
<link rel="stylesheet" href="dist/css/components.min.css" />

<!-- OR YouTube only (15KB minified) -->
<script type="module" src="dist/youtube-only.min.js"></script>
<link rel="stylesheet" href="dist/css/components.min.css" />

<!-- OR Vimeo only (14KB minified) -->
<script type="module" src="dist/vimeo-only.min.js"></script>
<link rel="stylesheet" href="dist/css/components.min.css" />

For Development (use unminified files for debugging):

<!-- Full bundle: Both YouTube and Vimeo -->
<script type="module" src="dist/index.js"></script>

<!-- OR YouTube only (smaller) -->
<script type="module" src="dist/youtube-only.js"></script>

<!-- OR Vimeo only (smaller) -->
<script type="module" src="dist/vimeo-only.js"></script>

<!-- Component CSS only -->
<link rel="stylesheet" href="dist/css/components.css" />

For Demos/Examples (includes Bootstrap):

<!-- Component JavaScript -->
<script type="module" src="dist/index.js"></script>

<!-- Full CSS with Bootstrap (~276KB) -->
<link rel="stylesheet" href="dist/css/main.css" />

YouTube

<!-- Use in HTML -->
<youtube-embed url="https://www.youtube.com/watch?v=dQw4w9WgXcQ" controls>
</youtube-embed>

Vimeo

<!-- Use in HTML -->
<vimeo-embed url="https://vimeo.com/76979871" controls> </vimeo-embed>

HTML5 Video (Self-hosted)

<!-- Use in HTML with self-hosted videos -->
<video-embed
  url="https://example.com/videos/demo.mp4"
  controls
  poster="thumbnail.jpg"
>
</video-embed>

Advanced Usage

YouTube

<!-- Lazy loading with custom poster -->
<youtube-embed
  video-id="dQw4w9WgXcQ"
  lazy
  autoplay
  muted
  poster="custom-poster.jpg"
>
</youtube-embed>

Vimeo

<!-- Lazy loading with custom poster -->
<vimeo-embed
  ## HTML5 Video

```html
<!-- Self-hostedAll Components)

| Attribute     | Type    | YouTube | Vimeo | Video | Description                                  |
| ------------- | ------- | ------- | ----- | ----- | -------------------------------------------- |
| `url`         | String  | ✅      | ✅    | ✅    | Full video URL                               |
| `video-id`    | String  | ✅      | ✅    | ❌    | Video ID (YouTube: 11 chars, Vimeo: numeric) |
| `autoplay`    | Boolean | ✅      | ✅    | ✅    | Auto-play video when loaded                  |
| `controls`    | Boolean | ✅      | ✅    | ✅    | Show native player controls                  |
| `lazy`        | Boolean | ✅      | ✅    | ✅    | Enable lazy loading with poster              |
| `muted`       | Boolean | ✅      | ✅    | ✅    | Start video muted                            |
| `poster`      | String  | ✅      | ✅    | ✅    | Custom poster image URL                      |
| `background`  | Boolean | ✅      | ✅    | ✅    | Enable background video mode                 |
| `player-vars` | JSON    | ✅      | ✅    | ❌    | JSON object of extra player parameters       |
| `loop`        | Boolean | ❌      | ❌    | ✅    | Loop video (native video only)               |
| `preload`     | String  | ❌      | ❌    | ✅    | Preload strategy: "none", "metadata", "auto"
  lazy
  autoplay
  muted
  poster="custom-poster.jpg"
>
</vimeo-embed>

📖 API Reference

Attributes (Both Components)

| Attribute | Type | Description | | ------------- | ------- | -------------------------------------------- | | url | String | Full video URL | | video-id | String | Video ID (YouTube: 11 chars, Vimeo: numeric) | | autoplay | Boolean | Auto-play video when loaded | | controls | Boolean | Show native player controls | | lazy | Boolean | Enable lazy loading with poster | | muted | Boolean | Start video muted | | poster | String | Custom poster image URL | | background | Boolean | Enable background video mode | | player-vars | JSON | JSON object of extra player parameters |

Methods

YouTube Embed

const embed = document.querySelector("youtube-embed");

// Playback control (wraps YouTube IFrame API Player methods)
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_controls
embed.play(); // ▶️ Play video (calls playVideo())
embed.pause(); // ⏸️ Pause video (calls pauseVideo())
embed.stopVideo(); // ⏹️ Stop video (calls stopVideo())
embed.togglePlay(); // ⏯️ Toggle play/pause

// Audio control
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_volume
embed.mute(); // 🔇 Mute audio (calls mute())
embed.unmute(); // 🔊 Unmute audio (calls unMute())
embed.toggleMute(); // 🔇 Toggle mute
const isMuted = embed.isMuted(); // Check if muted (calls isMuted())

// Playback time control
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_status
const currentTime = embed.getCurrentTime(); // Get current time in seconds (calls getCurrentTime())
embed.seekTo(30); // Seek to 30 seconds (calls seekTo())
embed.currentTime = 45; // Seek to 45 seconds using property setter

// Video management
embed.loadVideo("dQw4w9WgXcQ"); // Load by video ID
embed.loadVideo("https://youtu.be/dQw4w9WgXcQ"); // Load by URL

// State inspection
const state = embed.getPlayerState();
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }

// Quality control (DEPRECATED as of October 2019 - YouTube controls quality automatically)
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_quality
// WARNING: These methods are NO-OPS and have no effect on YouTube videos
const qualities = embed.getAvailableQualities(); // ⚠️ DEPRECATED - Returns empty array
const current = embed.getCurrentQuality(); // ⚠️ DEPRECATED - Returns "auto"
embed.setQuality("hd1080"); // ⚠️ DEPRECATED - Does nothing (no-op function)

// Fullscreen control
embed.enterFullscreen(); // Enter fullscreen mode
embed.exitFullscreen(); // Exit fullscreen mode
embed.toggleFullscreen(); // Toggle fullscreen
const isFs = embed.isFullscreen(); // Check fullscreen state

// Set extra player parameters
// Reference: https://developers.google.com/youtube/player_parameters
embed.playerVars = { loop: 1, playlist: "dQw4w9WgXcQ" };

Passing Extra Parameters:

You can pass additional parameters to the YouTube or Vimeo player using the player-vars attribute or playerVars property.

For YouTube, see the YouTube Player Parameters Reference for all available options.

<!-- YouTube: Loop a video -->
<youtube-embed
  video-id="dQw4w9WgXcQ"
  player-vars='{"loop": 1, "playlist": "dQw4w9WgXcQ"}'
></youtube-embed>

<!-- YouTube: Custom start time and quality -->
<youtube-embed
  video-id="dQw4w9WgXcQ"
  player-vars='{"start": 30, "end": 90, "quality": "hd720"}'
></youtube-embed>
// Set via JavaScript
const embed = document.querySelector("youtube-embed");
embed.playerVars = {
  loop: 1,
  playlist: "dQw4w9WgXcQ", // Required for loop to work
  start: 10, // Start at 10 seconds
  modestbranding: 1, // Minimal YouTube branding
  rel: 0, // Don't show related videos
};

See YouTube Player Parameters for all available options.

Vimeo Embed

const embed = document.querySelector("vimeo-embed");

// Playback control
embed.play(); // ▶️ Play video
embed.pause(); // ⏸️ Pause video
embed.stopVideo(); // ⏹️ Stop video
embed.togglePlay(); // ⏯️ Toggle play/pause

// Audio control
embed.mute(); // 🔇 Mute audio
embed.unmute(); // 🔊 Unmute audio
embed.toggleMute(); // 🔇 Toggle mute

// Video management
embed.loadVideo("123456789"); // Load by video ID
embed.loadVideo("https://vimeo.com/123456789"); // Load by URL

// State inspection
const state = embed.getPlayerState();
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }

// Async methods
const currentTime = await embed.getCurrentTime();
const isMuted = await embed.isMuted();

// Quality control (async)
const qualities = await embed.getAvailableQualities(); // Get available quality levels
const current = await embed.getCurrentQuality(); // Get current quality
await embed.setQuality("720p"); // Set quality to 720p

// Fullscreen control
await embed.enterFullscreen(); // Enter fullscreen mode
await embed.exitFullscreen(); // Exit fullscreen mode
await embed.toggleFullscreen(); // Toggle fullscreen
const isFs = embed.isFullscreen(); // Check fullscreen state

Passing Extra Parameters:

You can pass additional parameters to the Vimeo player using the player-vars attribute or playerVars property:

<!-- Vimeo: Loop a video -->
<vimeo-embed video-id="76979871" player-vars='{"loop": true}'></vimeo-embed>

<!-- Vimeo: Custom color and quality -->
<vimeo-embed
  video-id="76979871"
  player-vars='{"color": "ff0000", "quality": "720p"}'
></vimeo-embed>

<!-- Vimeo: Privacy and customization -->
<vimeo-embed
  video-id="76979871"
  player-vars='{"dnt": true, "title": false, "byline": false, "portrait": false}'
></vimeo-embed>
```# Video Embed (HTML5) All three components support the same event interface:
```javascript // Listen to player events (works for youtube-embed, vimeo-embed,
and video-embed) embed.addEventListener("ready", () => console.log("Player
ready")); embed.addEventListener("play", () => console.log("Playing"));
embed.addEventListener("pause", () => console.log("Paused"));
embed.addEventListener("ended", () => console.log("Video ended"));
embed.addEventListener("error", (e) => console.error("Error:", e.detail));
embed.addEventListener("qualitychange", (e) => { console.log( "Quality
changed:", e.detail.oldQuality, "→", e.detail.newQuality, );
console.log("Available qualities:", e.detail.availableQualities); });

Supported Events:

  • ready - Player initialized and ready to use (wraps YouTube's onReady)
  • play - Video playback started (wraps YouTube's onStateChange with state YT.PlayerState.PLAYING)
  • pause - Video playback paused (wraps onStateChange with state YT.PlayerState.PAUSED)
  • stop - Video playback stopped
  • ended - Video playback finished (wraps onStateChange with state YT.PlayerState.ENDED)
  • error - An error occurred (wraps YouTube's onError, check event.detail for details)
  • timeupdate - Current time position updated
  • qualitychange - Video quality level changed (wraps YouTube's onPlaybackQualityChange)
  • loadstart - Video loading started (native video)
  • connected - Component connected to DOM
  • disconnected - Component disconnected from DOMst state = embed.getPlayerState(); // Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }

const currentTime = embed.getCurrentTime(); const isMuted = embed.isMuted();

// Native video attributes embed.loop = true; // Enable looping embed.preload = "metadata"; // Set preload strategy


**Native HTML5 Video Attributes:**

```html
<!-- HTML5-specific attributes -->
<video-embed
  url="video.mp4"
  loop
  preload="metadata"
  controls
  poster="thumb.jpg"
>
</video-embed>

// Set via JavaScript
const embed = document.querySelector("vimeo-embed");
embed.playerVars = {
  loop: true, // Enable looping
  color: "00adef", // Custom player color (hex without #)
  title: false, // Hide video title
  byline: false, // Hide author
  portrait: false, // Hide author portrait
  dnt: true, // Do Not Track
};

See Vimeo Embed Options for all available options.

Events

Both components support the same event interface:

// Listen to player events
embed.addEventListener("ready", () => console.log("Player ready"));
embed.addEventListener("play", () => console.log("Playing"));
embed.addEventListener("pause", () => console.log("Paused"));
embed.addEventListener("ended", () => console.log("Video ended"));
embed.addEventListener("error", (e) => console.error("Error:", e.detail));
embed.addEventListener("qualitychange", (e) => {
  console.log(
    "Quality changed:",
    e.detail.oldQuality,
    "→",
    e.detail.newQuality,
  );
  console.log("Available qualities:", e.detail.availableQualities);
});

Video Quality

Both components support programmatic quality control and emit events when quality changes.

⚠️ IMPORTANT: YouTube Quality APIs Deprecated (October 2019)

YouTube deprecated manual quality control methods as of October 24, 2019. The quality methods for YouTube (getAvailableQualities(), getCurrentQuality(), setQuality()) are no-op functions that have no effect. YouTube now automatically adjusts quality based on network conditions. See the YouTube Help Center for details.

Use Vimeo or HTML5 Video if you need manual quality control.

YouTube Quality Levels (Reference Only - APIs Deprecated)

| Quality Level | Resolution | Description | | ------------- | ---------- | ------------------- | | tiny | 144p | Lowest quality | | small | 240p | Low quality | | medium | 360p | Medium quality | | large | 480p | Standard quality | | hd720 | 720p | HD quality | | hd1080 | 1080p | Full HD | | hd1440 | 1440p | 2K quality | | hd2160 | 2160p | 4K quality | | highres | >1080p | Highest available | | auto | Auto | Automatic selection |

Vimeo Quality Levels

| Quality Level | Resolution | Description | | ------------- | ---------- | ------------------- | | 240p | 240p | Low quality | | 360p | 360p | Medium quality | | 540p | 540p | Standard quality | | 720p | 720p | HD quality | | 1080p | 1080p | Full HD | | 2k | 1440p | 2K quality | | 4k | 2160p | 4K quality | | auto | Auto | Automatic selection |

Usage Examples

<!-- Set initial quality via attribute -->
<!-- WARNING: YouTube quality attribute is ignored (APIs deprecated Oct 2019) -->
<youtube-embed video-id="dQw4w9WgXcQ" quality="hd1080"></youtube-embed>
<!-- Vimeo quality control works normally -->
<vimeo-embed video-id="76979871" quality="720p"></vimeo-embed>
// YouTube quality control (DEPRECATED - These are no-op functions)
const ytEmbed = document.querySelector("youtube-embed");
const qualities = ytEmbed.getAvailableQualities(); // ⚠️ Returns empty array - DEPRECATED
const current = ytEmbed.getCurrentQuality(); // ⚠️ Returns "auto" - DEPRECATED
ytEmbed.setQuality("hd1080"); // ⚠️ Does nothing - DEPRECATED (no-op)

// Vimeo quality control (Works normally - async)
const vimeoEmbed = document.querySelector("vimeo-embed");
const qualities = await vimeoEmbed.getAvailableQualities(); // ["auto", "1080p", "720p", "540p"]
const current = await vimeoEmbed.getCurrentQuality(); // "720p"
await vimeoEmbed.setQuality("1080p"); // Switch to 1080p

Quality Change Event

embed.addEventListener("qualitychange", (event) => {
  const { oldQuality, newQuality, availableQualities } = event.detail;
  console.log(`Quality changed from ${oldQuality} to ${newQuality}`);
  console.log(`Available: ${availableQualities.join(", ")}`);
});

⚠️ CRITICAL: YouTube Quality Control APIs Are DEPRECATED (October 2019)

As of October 24, 2019, YouTube deprecated all manual quality control functions. According to the official documentation update:

These methods are NO LONGER FUNCTIONAL:

Why these were deprecated: YouTube now automatically adjusts video quality based on viewing conditions (network bandwidth, device capabilities, viewport size) to provide the best viewing experience. Manual quality selection interferes with YouTube's adaptive streaming algorithms.

What still works:

  • onPlaybackQualityChange event - Still fires when YouTube automatically changes quality
  • The qualitychange event in this library still works and reports YouTube's automatic quality changes

For developers: The quality methods (getAvailableQualities(), getCurrentQuality(), setQuality()) are kept in this library for API compatibility but have no effect on YouTube videos. They are maintained only to prevent breaking existing code. Use the Vimeo or native HTML5 video components if manual quality control is required.

Official YouTube Help Article: Why does YouTube adjust video quality?

Vimeo's quality control is more reliable and respects quality changes consistently.

Error Handling

Components include comprehensive error handling with automatic retry and user-friendly feedback:

Features

  • Automatic Retry: API loads retry up to 3 times with 2-second delays
  • Timeout Protection: 10-second timeout prevents indefinite waiting
  • Visual Feedback: Error UI with descriptive messages and retry button
  • Event Notifications: Detailed error events with codes and retry status

Error Event Details

embed.addEventListener("error", (e) => {
  console.log(e.detail.message); // Human-readable error message
  console.log(e.detail.code); // Error code (e.g., "API_LOAD_ERROR")
  console.log(e.detail.retryable); // Whether error is retryable
});

Common Error Scenarios

| Error Type | Cause | Solution | | ------------------ | -------------------------- | ------------------------------------ | | API Load Timeout | Ad blocker or slow network | Disable ad blocker, check connection | | Script Load Failed | Network issue or CORS | Verify internet connection | | Video Not Found | Invalid video ID | Check video ID/URL |

| EmHTML5 Video Demos:**

  • video-basic-demo.html - Self-hosted video usage
  • video-lazy-loading-demo.html - Native video lazy loading
  • video-background-demo.html - Background video patterns
  • platform-comparison-demo.html - Side-by-side platform comparison
  • quality-control-demo.html - Quality selection and switching
  • fullscreen-accessibility-demo.html - Fullscreen and accessibility featureso |

Accessibility

Both components are fully accessible with keyboard navigation and screen reader support:

Keyboard Shortcuts

| Key | Action | | ----------------- | -------------------------- | | Tab | Focus player | | Enter / Space | Activate lazy-loaded video | | k / Space | Play/Pause video | | m | Mute/Unmute | | f | Toggle fullscreen |

ARIA Support

  • ARIA Roles: Components use role="region" with aria-label="Video player"
  • Live Regions: Screen reader announcements for state changes (playing, paused, muted, etc.)
  • Button Labels: Play buttons have descriptive aria-label attributes
  • Focus Management: Proper tabindex and focus indicators
  • Keyboard Accessible: All interactive elements support keyboard navigation
<!-- Example: Accessible video embed -->
<youtube-embed
  url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  controls
  aria-label="Product demo video"
  tabindex="0"
>
</youtube-embed>

Properties

All attributes are accessible as JavaScript properties:

embed.autoplay = true; // Enable autoplay
embed.muted = false; // Unmute video
embed.url = "https://..."; // Change video URL
console.log(embed.playing); // Get current playing state

🎯 Examples

Comprehensive examples are available in the examples/ directory:

  • examples/index.html - Overview and navigation to all demos
  • YouTube Demos:
    • youtube-basic-demo.html - Basic usage patterns and modal integration
    • youtube-lazy-loading-demo.html - Performance optimization techniques
    • youtube-background-demo.html - Background video integration
  • Vimeo Demos:
    • vimeo-basic-demo.html - Comprehensive Vimeo features
    • vimeo-lazy-loading-demo.html - Performance-focused lazy loading
    • vimeo-background-demo.html - Full-screen background videos
    • vimeo-demo.html - Original Vimeo integration demo
  • platform-comparison-demo.html - Side-by-side platform comparison

Running Examples

  1. Build the component: npm run build
  2. Open examples/index.html in your browser
  3. Explore different usage patterns and features

🧪 Testing

This component includes a comprehensive automated test suite to ensure reliability, performance, and correct functionality.

Running Tests

  1. Quick Access: Visit test/index.html in your browser
  2. From Examples: Navigate to the test suite from examples/index.html
  3. Direct URL: http://localhost:8000/test/ (when serving locally)

Test Categories

🔧 Unit Tests (20+ tests)

  • Component creation and initialization
  • Attribute parsing and validation
  • Video ID extraction from various URL formats
  • Method availability and error handling
  • DOM structure verification

Lazy Loading Tests (15+ tests)

  • Poster image display and interaction
  • Deferred iframe creation
  • Click-to-load functionality
  • Performance benefit verification
  • Resource optimization testing

▶️ Player State Tests (12+ tests)

  • Play/pause/stop operations
  • State transitions and reporting
  • Mute/unmute functionality
  • Custom vs native controls
  • Error recovery mechanisms

🔗 Integration Tests (10+ tests)

  • YouTube/Vimeo iframe API loading
  • Real player initialization and events
  • Cross-origin and security features
  • Browser compatibility verification
  • Multiple instance handling

Performance Tests (10+ tests)

  • Initialization speed benchmarks (< 200ms target)
  • Memory leak detection
  • Resource build:cache-bust| Update cache-busting params | Updates query strings in files | |npm run test:build | Build + ready message | One-command test preparation | |npm run test:validate | Build + comprehensive check | Full environment validation | |npm run watch | Watch mode (TypeScript + SCSS) | Auto-rebuild during development | |npm run examples` | Build + examples ready | Prepare examples for viewing |

Build Scripts

The project includes helper scripts in the scripts/ directory:

  • verify-build.js - Validates that all required build outputs exist and are valid

    • Checks for compiled JS files (index.js, youtube-only.js, vimeo-only.js, video-only.js)
    • Verifies CSS compilation (main.css, components.css)
    • Ensures test files are accessible
    • Automatically run as part of npm run build
  • update-cache-busters.js - Updates cache-busting query parameters in built files

    • Appends version-based timestamps to asset references
    • Ensures browsers load fresh assets after updates
    • Automatically run as part of npm run build

Test Framework Features

  • Visual Progress Tracking - Real-time progress bars and statistics
  • Console Capture - All logs displayed in beautiful interface
  • Test Filtering - Run specific suites or filter by pass/fail
  • Results Export - Download detailed JSON reports
  • Performance Monitoring - Automatic timing and memory tracking

Example Test Usage

// Run all tests
runAllTests();

// Run specific test suites
runTestSuite("unit"); // Unit tests only
runTestSuite("lazy"); // Lazy loading tests
runTestSuite("integration"); // Integration tests
runTestSuite("performance"); // Performance tests

Test Results Interpretation

  • 90%+ Success Rate - Excellent, production ready
  • 70-89% Success Rate - Good, minor issues may exist
  • < 70% Success Rate - Issues need investigation

Common test failures in browser environments:

  • YouTube/Vimeo API loading delays
  • Network connectivity issues
  • Browser security restrictions
  • Timing-sensitive operations

⚡ Performance Benefits

Lazy Loading

  • Faster initial page loads - Videos load on-demand
  • Reduced bandwidth - Only loads when user wants to watch
  • Better Core Web Vitals - Improved LCP scores
  • Mobile optimization - Data usage conscious

Event-Driven Architecture

  • Efficient updates - React to state changes without polling
  • Better UX - Responsive to user interactions
  • Easy integration - Standard event listeners

🛠️ Development

Build Process

Available Build Scripts

| Script | Purpose | Usage | | ------------------------ | -------------------------------- | ------------------------------- | | npm run build | Full clean build with validation | Always run before testing | | npm run build:clean | Clean dist directory | Removes old build artifacts | | npm run build:ts | Compile TypeScript only | Quick TypeScript updates | | npm run build:css | Compile SCSS only | Quick style updates | | npm run build:validate | Verify build integrity | Checks all required files | | npm run test:build | Build + ready message | One-command test preparation | | npm run test:validate | Build + comprehensive check | Full environment validation | | npm run watch | Watch mode (TypeScript + SCSS) | Auto-rebuild during development | | npm run examples | Build + examples ready | Prepare examples for viewing |

Quick Start

# Initial setup (one time)
npm install

# Development workflow (recommended)
npm run watch              # Auto-rebuild on changes

# OR manual builds
npm run build             # Full clean build
npm run test:validate     # Build + validate everything

Build Validation

The build process includes automatic validation that checks:

  • TypeScript compilation - dist/index.js and component files
  • SCSS compilation - dist/css/main.css with proper styling
  • Test file integrity - All test files present and accessible
  • Documentation - README and guides are available

Testing Workflow

Quick Testing Guide

  1. Build the component (required):

    npm run build
  2. Start watch mode (recommended for development):

    npm run watch
  3. Open the test suite:

    • Navigate to test/index.html in your browser
    • Or serve via local server: python -m http.server 8000
    • Visit: http://localhost:8000/test/

Development Workflow with Tests

When using npm run watch, the system will:

  • ✅ Automatically compile TypeScript changes to JavaScript
  • ✅ Automatically compile SCSS changes to CSS
  • ✅ Keep compiled files up to date

Workflow:

  1. Start watch mode: npm run watch
  2. Make changes to TypeScript or SCSS files
  3. Refresh the test page to see updates
  4. Tests automatically use latest compiled code

Test Features

  • 67+ comprehensive tests across 5 categories (YouTube) + 5 categories (Vimeo)
  • Automatic component detection and loading
  • Real-time console output with colored messages
  • Progress tracking and detailed statistics
  • JSON export for results documentation
  • Individual test suite execution
  • Component demo integration

Troubleshooting Tests

Component Not Loading:

  • Error: "Component not registered"
  • Solution: Run npm run build or npm run watch first

CORS Issues:

  • Error: Module loading fails
  • Solution: Serve files via HTTP server instead of file:// protocol

Outdated Tests:

  • Error: Tests show old behavior
  • Solution: Ensure watch mode is running or rebuild manually

For detailed testing documentation, see test/README.md.

🌐 Browser Support

  • Modern browsers with Web Components support
  • ES6+ JavaScript features
  • YouTube IFrame API integration (API Reference)
  • Vimeo Player API integration (API Reference)

YouTube IFrame API Integration

The <youtube-embed> component wraps the YouTube IFrame Player API to provide a consistent, web-component-based interface.

API Loading:

  • Dynamically loads https://www.youtube.com/iframe_api
  • Implements retry mechanism (up to 3 attempts) for reliability
  • Handles API initialization via onYouTubeIframeAPIReady callback
  • 10-second timeout protection against blocked scripts

Player Methods Used:

Event Handlers:

Player Parameters: Supports all YouTube Player Parameters via the player-vars attribute, including autoplay, loop, controls, modestbranding, rel, showinfo, and more.

📝 Migration Guide

From Standard iframes to Web Components

Migrating from traditional iframe embeds is straightforward:

Before (Standard iframe):

<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/dQw4w9WgXcQ"
  frameborder="0"
  allow="autoplay; encrypted-media"
  allowfullscreen
></iframe>

After (Web Component):

<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>

Benefits of Migration:

  • ✅ Lazy loading support (faster page loads)
  • ✅ Programmatic control (play, pause, mute, etc.)
  • ✅ Event handling (ready, play, pause, ended, error)
  • ✅ Quality control (set preferred video quality)
  • ✅ Fullscreen API (enter/exit fullscreen programmatically)
  • ✅ Accessibility features (keyboard shortcuts, ARIA support)
  • ✅ Error handling with retry mechanism
  • ✅ Responsive by default

From Previous Versions

  • pause() now pauses instead of stopping (use stopVideo() to stop)
  • Boolean attributes work correctly (presence = true, absence = false)
  • Event-based architecture replaces callbacks
  • video-id attribute supported alongside url

Event Migration

// Old approach
embed.onReady = () => {
  /* ... */
};

// New approach
embed.addEventListener("ready", () => {
  /* ... */
});

🔧 Troubleshooting

Common Issues and Solutions

Video Not Loading

Problem: Video doesn't load or shows error message

Solutions:

  1. Check Video ID: Ensure the video ID is correct and publicly accessible
  2. Disable Ad Blockers: Some ad blockers prevent video APIs from loading
  3. Check Network: Verify internet connection and firewall settings
  4. CORS Issues: Ensure your domain is allowed (usually not an issue with official APIs)
// Listen for error events
embed.addEventListener("error", (e) => {
  console.log("Error details:", e.detail);
  // e.detail.message - Human-readable error
  // e.detail.code - Error code (e.g., "API_LOAD_ERROR")
  // e.detail.retryable - Whether automatic retry will occur
});

Autoplay Not Working

Problem: Video doesn't autoplay even with autoplay attribute

Solutions:

  1. Add muted attribute: Most browsers require videos to be muted for autoplay
  2. User Interaction: Some browsers require user interaction before autoplay
<!-- Correct autoplay setup -->
<youtube-embed video-id="VIDEO_ID" autoplay muted></youtube-embed>

Lazy Loading Not Triggering

Problem: Video doesn't load when scrolled into view

Solutions:

  1. Check lazy attribute: Ensure the lazy attribute is present
  2. Viewport Visibility: Element must be within viewport threshold (50px)
  3. Browser Support: Ensure browser supports Intersection Observer API
// Debug lazy loading
const embed = document.querySelector("youtube-embed");
console.log("Lazy enabled:", embed.hasAttribute("lazy"));
console.log(
  "In viewport:",
  embed.getBoundingClientRect().top < window.innerHeight,
);

Keyboard Shortcuts Not Working

Problem: Pressing 'k', 'm', or 'f' doesn't control the video

Solutions:

  1. Focus Element: Click on the video component to focus it first
  2. Check Conflicts: Ensure no other scripts are intercepting keyboard events
  3. Browser Compatibility: Some browsers may restrict keyboard events

Available Shortcuts:

  • k - Play/Pause toggle
  • m - Mute/Unmute toggle
  • f - Fullscreen toggle

Quality Selection Issues

Problem: Cannot change video quality or quality selector shows no options

Solutions:

  1. Wait for Ready Event: Quality info only available after player is ready
  2. Video Support: Not all videos have multiple quality levels
  3. API Limitations: Platform APIs may not expose all quality levels immediately
embed.addEventListener("ready", async () => {
  // For YouTube
  const qualities = embed.getAvailableQualities();
  console.log("Available:", qualities);

  // For Vimeo (async)
  const qualities = await embed.getAvailableQualities();
  console.log("Available:", qualities);
});

🌐 Browser Compatibility

Minimum Requirements

| Feature | Chrome | Firefox | Safari | Edge | | ------------------------- | ------ | ------- | ------ | ---- | | Web Components | 67+ | 63+ | 10.1+ | 79+ | | Custom Elements | 67+ | 63+ | 10.1+ | 79+ | | Shadow DOM | 53+ | 63+ | 10.1+ | 79+ | | ES6 Modules | 61+ | 60+ | 10.1+ | 79+ | | Intersection Observer | 58+ | 55+ | 12.1+ | 79+ | | Fullscreen API | 71+ | 64+ | 5.1+ | 79+ |

Feature Support

| Feature | Support | Polyfill Available | Notes | | ------------------ | ------------------ | ------------------ | ------------------------------------- | | Lazy Loading | Modern Browsers | ✅ | Falls back to immediate load | | Keyboard Shortcuts | All Browsers | N/A | Browser-dependent focus behavior | | Fullscreen API | Modern Browsers | ❌ | Vendor prefixes handled automatically | | Quality Control | Platform-dependent | N/A | Depends on YouTube/Vimeo API support | | ARIA Support | All Browsers | N/A | Screen reader compatibility |

Polyfills

For older browser support, include these polyfills:

<!-- Web Components Polyfills -->
<script src="https://unpkg.com/@webcomponents/[email protected]/webcomponents-loader.js"></script>

<!-- Intersection Observer Polyfill -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>

💡 Advanced Usage Examples

Playlist Implementation

<div id="playlist-container"></div>

<script type="module">
  const videos = ["dQw4w9WgXcQ", "ScMzIvxBSi4", "M7lc1UVf-VE"];

  let currentIndex = 0;
  const container = document.getElementById("playlist-container");

  function loadVideo(index) {
    container.innerHTML = "";
    const embed = document.createElement("youtube-embed");
    embed.setAttribute("video-id", videos[index]);
    embed.setAttribute("autoplay", "");

    embed.addEventListener("ended", () => {
      currentIndex = (currentIndex + 1) % videos.length;
      loadVideo(currentIndex);
    });

    container.appendChild(embed);
  }

  loadVideo(0);
</script>

Custom Styling

<style>
  youtube-embed {
    --embed-border-radius: 16px;
    --embed-max-width: 800px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  }

  youtube-embed::part(container) {
    border-radius: var(--embed-border-radius);
  }
</style>

<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>

Dynamic Quality Selection

<youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>

<select id="quality-selector">
  <option value="auto">Auto</option>
</select>

<script type="module">
  const player = document.getElementById("player");
  const selector = document.getElementById("quality-selector");

  player.addEventListener("ready", () => {
    const qualities = player.getAvailableQualities();

    // Populate selector
    qualities.forEach((quality) => {
      if (quality !== "auto") {
        const option = document.createElement("option");
        option.value = quality;
        option.textContent = quality.toUpperCase();
        selector.appendChild(option);
      }
    });
  });

  selector.addEventListener("change", (e) => {
    player.setQuality(e.target.value);
  });

  player.addEventListener("qualitychange", (e) => {
    console.log(
      `Quality changed: ${e.detail.oldQuality} → ${e.detail.newQuality}`,
    );
  });
</script>

Event Logger

<youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>
<div id="event-log"></div>

<script type="module">
  const player = document.getElementById("player");
  const log = document.getElementById("event-log");

  const events = [
    "ready",
    "play",
    "pause",
    "ended",
    "error",
    "timeupdate",
    "qualitychange",
  ];

  events.forEach((eventName) => {
    player.addEventListener(eventName, (e) => {
      const entry = document.createElement("div");
      entry.textContent = `${new Date().toLocaleTimeString()} - ${eventName}: ${JSON.stringify(
        e.detail || {},
      )}`;
      log.prepend(entry);
    });
  });
</script>

Responsive Aspect Ratio

<style>
  .video-wrapper {
    position: relative;
    width: 100%;
    padding-bottom: 56.25%; /* 16:9 aspect ratio */
  }

  .video-wrapper youtube-embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
</style>

<div class="video-wrapper">
  <youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
</div>

📦 Build & Distribution

Development Build

# Standard build (unminified for debugging)
npm run build

# Watch mode for development
npm run dev

# Validate build integrity
npm run build:validate

Production Build

# Production build with minification (~50% size reduction)
npm run build:prod

This creates optimized .min.js and .min.css files with:

  • Terser minification for JavaScript (removes whitespace, shortens variable names)
  • CSSO opt (all three components) import "@smartimpact-it/modern-video-embed";

// YouTube only import "@smartimpact-it/modern-video-embed/dist/youtube-only.js";

// Vimeo only import "@smartimpact-it/modern-video-embed/dist/vimeo-only.js";

// Native video only import "@smartimpact-it/modern-video-embed/dist/vidKB) ├── index.min.js # Full bundle minified (29KB) ⭐ Production ├── youtube-only.js # YouTube bundle (29KB) ├── youtube-only.min.js # YouTube minified (15KB) ⭐ Production ├── vimeo-only.js # Vimeo bundle (29KB) ├── vimeo-only.min.js # Vimeo minified (14KB) ⭐ Production ├── components/ │ ├── YouTubeEmbed.js # YouTube component (58KB) │ ├── YouTubeEmbed.min.js # YouTube minified (29KB) │ ├── VimeoEmbed.js # Vimeo component (49KB) │ └── VimeoEmbed.min.js # Vimeo minified (27KB) ├── css/ │ ├── components.css # Core styles (9KB) │ ├── components.min.css # Core minified (7KB) ⭐ Production │ ├── main.css # With Bootstrap (276KB) │ └── main.min.css # Bootstrap minified (224KB) └── types/ # TypeScript declarations


### Performance Optimization

**Bundle Size Comparison:**

| Configuration        | Size (Dev) | Size (Prod) | With gzip\* |
| -------------------- | ---------- | ----------- | ----------- |
We welcome contributions! Here's how to get started:

1. **Read the docs**: Check [`.ai-instructions.md`](.ai-instructions.md) for development guidelines
2. **Fork the repository**: Create your own fork on GitHub
3. **Create a feature branch**: `git checkout -b feature/your-feature-name`
4. **Make your changes**: Follow TypeScript strict mode and existing patterns
5. **Build and test**: Run `npm run build` and test with `test/index.html`
6. **Update documentation**: Update README and type definitions if needed
7. **Submit a pull request**: Include a clear description of changes

**Development Checklist:**
- ✅ Code follows existing patterns and conventions
- ✅ TypeScript compiles without errors (strict mode)
- ✅ All tests pass in `test/index.html`
- ✅ Type definitions updated if API changed
- ✅ README updated for user-facing changes
- ✅ Examples added/updated if needed
\*Estimated with gzip compression enabled on server

**Optimization Tips:**

1. ✅ **Use minified files** (`.min.js` / `.min.css`) in production - saves ~50%
2. ✅ **Choose platform-specific bundles** when possible - saves ~50%
3. ✅ **Use core CSS** instead of Bootstrap - saves ~97%
4. ✅ **Enable gzip/brotli** on your server - saves additional ~70%
5. ✅ **Lazy loading is built-in** - videos only load when visible

**Best Configuration for Production:**

```html
<!-- YouTube only: 22KB total (7KB gzipped) -->
<script type="module" src="dist/youtube-only.min.js"></script>
<link rel="stylesheet" href="dist/css/components.min.css" />

Bundle Sizes

| File | Uncompressed | Gzipped | Description | | ----------------- | ------------ | ------- | -------------------------------- | | index.js | 58KB | ~15KB | Full bundle with both components | | youtube-only.js | 29KB | ~8KB | YouTube component only | | vimeo-only.js | 29KB | ~8KB | Vimeo component only | | components.css | 15KB | ~3KB | Core component styles | | main.css | 276KB | ~35KB | Full styles with Bootstrap |

Publishing to NPM

# Update version in package.json
npm version patch|minor|major

# Build and publish
npm publish

Using in Your Project

ES Modules

// Full bundle
import "@smartimpact-it/modern-video-embed";

// YouTube only
import "@smartimpact-it/modern-video-embed/dist/youtube-only.js";

// Vimeo only
import "@smartimpact-it/modern-video-embed/dist/vimeo-only.js";

With Bundlers (Webpack, Vite, etc.)

// Install
npm install @smartimpact-it/modern-video-embed

// Import in your app
import '@smartimpact-it/modern-video-embed';
import '@smartimpact-it/modern-video-embed/dist/css/components.css';

// Use in your HTML/JSX
<youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>

TypeScript Support

import { YouTubeEmbed } from "@smartimpact-it/modern-video-embed";

const embed = document.querySelector("youtube-embed") as YouTubeEmbed;
embed.play();

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Test with the examples
  5. Submit a pull request

📄 License

This project is open source. See the LICENSE file for details.


Built with vanilla JavaScript • Web Components • TypeScript ready