@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 scriptsKey Files:
.ai-instructions.md- Development guide for AI agents and contributorspackage.json- NPM scripts and dependenciestsconfig.json- TypeScript configuration (strict mode)
�🚀 Quick Start
Installation
Via NPM (recommended for projects)
npm install @smartimpact-it/modern-video-embedVia 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 buildBundle 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 statePassing 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'sonReady)play- Video playback started (wraps YouTube'sonStateChangewith stateYT.PlayerState.PLAYING)pause- Video playback paused (wrapsonStateChangewith stateYT.PlayerState.PAUSED)stop- Video playback stoppedended- Video playback finished (wrapsonStateChangewith stateYT.PlayerState.ENDED)error- An error occurred (wraps YouTube'sonError, checkevent.detailfor details)timeupdate- Current time position updatedqualitychange- Video quality level changed (wraps YouTube'sonPlaybackQualityChange)loadstart- Video loading started (native video)connected- Component connected to DOMdisconnected- 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 1080pQuality 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:
getPlaybackQuality()- No longer returns accurate quality informationsetPlaybackQuality()- No-op function (does nothing)getAvailableQualityLevels()- No longer returns quality options
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:
onPlaybackQualityChangeevent - Still fires when YouTube automatically changes quality- The
qualitychangeevent 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 usagevideo-lazy-loading-demo.html- Native video lazy loadingvideo-background-demo.html- Background video patternsplatform-comparison-demo.html- Side-by-side platform comparisonquality-control-demo.html- Quality selection and switchingfullscreen-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"witharia-label="Video player" - Live Regions: Screen reader announcements for state changes (playing, paused, muted, etc.)
- Button Labels: Play buttons have descriptive
aria-labelattributes - Focus Management: Proper
tabindexand 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 integrationyoutube-lazy-loading-demo.html- Performance optimization techniquesyoutube-background-demo.html- Background video integration
- Vimeo Demos:
vimeo-basic-demo.html- Comprehensive Vimeo featuresvimeo-lazy-loading-demo.html- Performance-focused lazy loadingvimeo-background-demo.html- Full-screen background videosvimeo-demo.html- Original Vimeo integration demo
platform-comparison-demo.html- Side-by-side platform comparison
Running Examples
- Build the component:
npm run build - Open
examples/index.htmlin your browser - Explore different usage patterns and features
🧪 Testing
This component includes a comprehensive automated test suite to ensure reliability, performance, and correct functionality.
Running Tests
- Quick Access: Visit
test/index.htmlin your browser - From Examples: Navigate to the test suite from
examples/index.html - 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 testsTest 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 everythingBuild Validation
The build process includes automatic validation that checks:
- ✅ TypeScript compilation -
dist/index.jsand component files - ✅ SCSS compilation -
dist/css/main.csswith proper styling - ✅ Test file integrity - All test files present and accessible
- ✅ Documentation - README and guides are available
Testing Workflow
Quick Testing Guide
Build the component (required):
npm run buildStart watch mode (recommended for development):
npm run watchOpen the test suite:
- Navigate to
test/index.htmlin your browser - Or serve via local server:
python -m http.server 8000 - Visit:
http://localhost:8000/test/
- Navigate to
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:
- Start watch mode:
npm run watch - Make changes to TypeScript or SCSS files
- Refresh the test page to see updates
- 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 buildornpm run watchfirst
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
onYouTubeIframeAPIReadycallback - 10-second timeout protection against blocked scripts
Player Methods Used:
playVideo()- Start video playback ✅pauseVideo()- Pause video playback ✅stopVideo()- Stop video and reset ✅seekTo(seconds, allowSeekAhead)- Seek to specific time ✅mute()- Mute the player ✅unMute()- Unmute the player ✅isMuted()- Check mute state ✅getCurrentTime()- Get current playback time ✅- ~~
getPlaybackQuality()~~ - ⚠️ DEPRECATED Oct 2019 - No longer functional - ~~
setPlaybackQuality(suggestedQuality)~~ - ⚠️ DEPRECATED Oct 2019 - No-op function - ~~
getAvailableQualityLevels()~~ - ⚠️ DEPRECATED Oct 2019 - No longer returns data
Event Handlers:
onReady- Player is readyonStateChange- Player state changed (playing, paused, ended, etc.)onPlaybackQualityChange- Quality level changedonError- Error occurred
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 (usestopVideo()to stop)- Boolean attributes work correctly (presence = true, absence = false)
- Event-based architecture replaces callbacks
video-idattribute supported alongsideurl
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:
- Check Video ID: Ensure the video ID is correct and publicly accessible
- Disable Ad Blockers: Some ad blockers prevent video APIs from loading
- Check Network: Verify internet connection and firewall settings
- 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:
- Add
mutedattribute: Most browsers require videos to be muted for autoplay - 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:
- Check
lazyattribute: Ensure thelazyattribute is present - Viewport Visibility: Element must be within viewport threshold (50px)
- 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:
- Focus Element: Click on the video component to focus it first
- Check Conflicts: Ensure no other scripts are intercepting keyboard events
- Browser Compatibility: Some browsers may restrict keyboard events
Available Shortcuts:
k- Play/Pause togglem- Mute/Unmute togglef- Fullscreen toggle
Quality Selection Issues
Problem: Cannot change video quality or quality selector shows no options
Solutions:
- Wait for Ready Event: Quality info only available after player is ready
- Video Support: Not all videos have multiple quality levels
- 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:validateProduction Build
# Production build with minification (~50% size reduction)
npm run build:prodThis 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 publishUsing 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
- Fork the repository
- Create a feature branch
- Make your changes
- Test with the examples
- Submit a pull request
📄 License
This project is open source. See the LICENSE file for details.
Built with vanilla JavaScript • Web Components • TypeScript ready
