@smartimpact-it/modern-video-embed 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +1205 -0
  2. package/dist/components/VimeoEmbed.d.ts +143 -0
  3. package/dist/components/VimeoEmbed.d.ts.map +1 -0
  4. package/dist/components/VimeoEmbed.js +1176 -0
  5. package/dist/components/VimeoEmbed.js.map +1 -0
  6. package/dist/components/VimeoEmbed.min.js +1 -0
  7. package/dist/components/YouTubeEmbed.d.ts +225 -0
  8. package/dist/components/YouTubeEmbed.d.ts.map +1 -0
  9. package/dist/components/YouTubeEmbed.js +1354 -0
  10. package/dist/components/YouTubeEmbed.js.map +1 -0
  11. package/dist/components/YouTubeEmbed.min.js +1 -0
  12. package/dist/css/components.css +349 -0
  13. package/dist/css/components.css.map +1 -0
  14. package/dist/css/components.min.css +1 -0
  15. package/dist/css/main.css +12210 -0
  16. package/dist/css/main.css.map +1 -0
  17. package/dist/css/main.min.css +7 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +4 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/index.min.js +1 -0
  23. package/dist/types/index.d.ts +7 -0
  24. package/dist/types/index.d.ts.map +1 -0
  25. package/dist/types/index.js +5 -0
  26. package/dist/types/index.js.map +1 -0
  27. package/dist/vimeo-only.d.ts +7 -0
  28. package/dist/vimeo-only.d.ts.map +1 -0
  29. package/dist/vimeo-only.js +8 -0
  30. package/dist/vimeo-only.js.map +1 -0
  31. package/dist/vimeo-only.min.js +1 -0
  32. package/dist/youtube-only.d.ts +7 -0
  33. package/dist/youtube-only.d.ts.map +1 -0
  34. package/dist/youtube-only.js +8 -0
  35. package/dist/youtube-only.js.map +1 -0
  36. package/dist/youtube-only.min.js +1 -0
  37. package/package.json +75 -0
  38. package/src/components/VimeoEmbed.ts +1340 -0
  39. package/src/components/YouTubeEmbed.ts +1568 -0
  40. package/src/index.ts +3 -0
  41. package/src/styles/README.md +56 -0
  42. package/src/styles/components.scss +7 -0
  43. package/src/styles/main.scss +10 -0
  44. package/src/styles/vimeo-embed.scss +255 -0
  45. package/src/styles/youtube-embed.scss +261 -0
  46. package/src/types/common.d.ts +198 -0
  47. package/src/types/index.ts +7 -0
  48. package/src/types/vimeo-embed.d.ts +80 -0
  49. package/src/types/youtube-embed.d.ts +83 -0
  50. package/src/vimeo-only.ts +9 -0
  51. package/src/youtube-only.ts +9 -0
package/README.md ADDED
@@ -0,0 +1,1205 @@
1
+ # Video Embed Components
2
+
3
+ Modern, performant YouTube and Vimeo embed web components with lazy loading, event-driven architecture, and comprehensive API control.
4
+
5
+ ## ✨ Features
6
+
7
+ - **🎮 Full Control API** - Play, pause, stop, mute, and load videos programmatically
8
+ - **📡 Event-Driven** - Listen to player events (ready, play, pause, error, etc.)
9
+ - **⚡ Lazy Loading** - Improve performance with poster images and on-demand loading
10
+ - **🔗 Flexible URLs** - Support for full URLs, short links, and video IDs
11
+ - **📱 Responsive** - Mobile-first design with touch-friendly controls
12
+ - **♿ Accessible** - Keyboard navigation and screen reader support
13
+ - **🎨 Customizable** - Custom overlay controls or native player controls
14
+ - **🎬 Multi-Platform** - Both YouTube and Vimeo support
15
+
16
+ ## 🚀 Quick Start
17
+
18
+ ### Installation
19
+
20
+ #### Via NPM (recommended for projects)
21
+
22
+ ```bash
23
+ npm install @siit-dev/video-embed
24
+ ```
25
+
26
+ #### Via CDN (for quick prototypes)
27
+
28
+ ```html
29
+ <script
30
+ type="module"
31
+ src="https://unpkg.com/@siit-dev/video-embed@latest/dist/index.js"
32
+ ></script>
33
+ <link
34
+ rel="stylesheet"
35
+ href="https://unpkg.com/@siit-dev/video-embed@latest/dist/css/components.css"
36
+ />
37
+ ```
38
+
39
+ #### Manual Build
40
+
41
+ ```bash
42
+ git clone https://github.com/siit-dev/si_video_embed.git
43
+ cd si_video_embed
44
+ npm install
45
+ npm run build
46
+ ```
47
+
48
+ ### Bundle Options
49
+
50
+ Choose the right bundle for your needs:
51
+
52
+ | Bundle | File | Size (Dev) | Size (Prod) | Use Case |
53
+ | ----------------- | ------------------------- | ---------- | ----------- | ---------------------- |
54
+ | **Full** | `dist/index.js` | 58KB | **29KB** | Both YouTube and Vimeo |
55
+ | **YouTube Only** | `dist/youtube-only.js` | 29KB | **15KB** | Only YouTube embeds |
56
+ | **Vimeo Only** | `dist/vimeo-only.js` | 29KB | **14KB** | Only Vimeo embeds |
57
+ | **Styles (Core)** | `dist/css/components.css` | 9KB | **7KB** | Component styles only |
58
+ | **Styles (Full)** | `dist/css/main.css` | 276KB | **224KB** | With Bootstrap (demos) |
59
+
60
+ **Production Files** use minified versions (`.min.js` / `.min.css`) for ~50% size reduction.
61
+
62
+ **Recommended for Production:**
63
+
64
+ - Use **YouTube-only** or **Vimeo-only** bundles when possible (~14-15KB minified)
65
+ - Use **Core styles** (`components.css`) instead of full Bootstrap (~7KB minified)
66
+ - Enable gzip compression on your server (reduces size by additional 60-70%)
67
+
68
+ ### Basic Usage
69
+
70
+ #### Include Required Files
71
+
72
+ **For Production** (recommended - use minified files):
73
+
74
+ ```html
75
+ <!-- Full bundle: Both YouTube and Vimeo (29KB minified) -->
76
+ <script type="module" src="dist/index.min.js"></script>
77
+ <link rel="stylesheet" href="dist/css/components.min.css" />
78
+
79
+ <!-- OR YouTube only (15KB minified) -->
80
+ <script type="module" src="dist/youtube-only.min.js"></script>
81
+ <link rel="stylesheet" href="dist/css/components.min.css" />
82
+
83
+ <!-- OR Vimeo only (14KB minified) -->
84
+ <script type="module" src="dist/vimeo-only.min.js"></script>
85
+ <link rel="stylesheet" href="dist/css/components.min.css" />
86
+ ```
87
+
88
+ **For Development** (use unminified files for debugging):
89
+
90
+ ```html
91
+ <!-- Full bundle: Both YouTube and Vimeo -->
92
+ <script type="module" src="dist/index.js"></script>
93
+
94
+ <!-- OR YouTube only (smaller) -->
95
+ <script type="module" src="dist/youtube-only.js"></script>
96
+
97
+ <!-- OR Vimeo only (smaller) -->
98
+ <script type="module" src="dist/vimeo-only.js"></script>
99
+
100
+ <!-- Component CSS only -->
101
+ <link rel="stylesheet" href="dist/css/components.css" />
102
+ ```
103
+
104
+ **For Demos/Examples** (includes Bootstrap):
105
+
106
+ ```html
107
+ <!-- Component JavaScript -->
108
+ <script type="module" src="dist/index.js"></script>
109
+
110
+ <!-- Full CSS with Bootstrap (~276KB) -->
111
+ <link rel="stylesheet" href="dist/css/main.css" />
112
+ ```
113
+
114
+ #### YouTube
115
+
116
+ ```html
117
+ <!-- Use in HTML -->
118
+ <youtube-embed url="https://www.youtube.com/watch?v=dQw4w9WgXcQ" controls>
119
+ </youtube-embed>
120
+ ```
121
+
122
+ #### Vimeo
123
+
124
+ ```html
125
+ <!-- Use in HTML -->
126
+ <vimeo-embed url="https://vimeo.com/76979871" controls> </vimeo-embed>
127
+ ```
128
+
129
+ ### Advanced Usage
130
+
131
+ #### YouTube
132
+
133
+ ```html
134
+ <!-- Lazy loading with custom poster -->
135
+ <youtube-embed
136
+ video-id="dQw4w9WgXcQ"
137
+ lazy
138
+ autoplay
139
+ muted
140
+ poster="custom-poster.jpg"
141
+ >
142
+ </youtube-embed>
143
+ ```
144
+
145
+ #### Vimeo
146
+
147
+ ```html
148
+ <!-- Lazy loading with custom poster -->
149
+ <vimeo-embed
150
+ video-id="123456789"
151
+ lazy
152
+ autoplay
153
+ muted
154
+ poster="custom-poster.jpg"
155
+ >
156
+ </vimeo-embed>
157
+ ```
158
+
159
+ ## 📖 API Reference
160
+
161
+ ### Attributes (Both Components)
162
+
163
+ | Attribute | Type | Description |
164
+ | ------------- | ------- | -------------------------------------------- |
165
+ | `url` | String | Full video URL |
166
+ | `video-id` | String | Video ID (YouTube: 11 chars, Vimeo: numeric) |
167
+ | `autoplay` | Boolean | Auto-play video when loaded |
168
+ | `controls` | Boolean | Show native player controls |
169
+ | `lazy` | Boolean | Enable lazy loading with poster |
170
+ | `muted` | Boolean | Start video muted |
171
+ | `poster` | String | Custom poster image URL |
172
+ | `background` | Boolean | Enable background video mode |
173
+ | `player-vars` | JSON | JSON object of extra player parameters |
174
+
175
+ ### Methods
176
+
177
+ #### YouTube Embed
178
+
179
+ ```javascript
180
+ const embed = document.querySelector("youtube-embed");
181
+
182
+ // Playback control
183
+ embed.play(); // ▶️ Play video
184
+ embed.pause(); // ⏸️ Pause video
185
+ embed.stopVideo(); // ⏹️ Stop video
186
+ embed.togglePlay(); // ⏯️ Toggle play/pause
187
+
188
+ // Audio control
189
+ embed.mute(); // 🔇 Mute audio
190
+ embed.unmute(); // 🔊 Unmute audio
191
+ embed.toggleMute(); // 🔇 Toggle mute
192
+
193
+ // Video management
194
+ embed.loadVideo("dQw4w9WgXcQ"); // Load by video ID
195
+ embed.loadVideo("https://youtu.be/dQw4w9WgXcQ"); // Load by URL
196
+
197
+ // State inspection
198
+ const state = embed.getPlayerState();
199
+ // Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
200
+
201
+ // Quality control
202
+ const qualities = embed.getAvailableQualities(); // Get available quality levels
203
+ const current = embed.getCurrentQuality(); // Get current quality
204
+ embed.setQuality("hd1080"); // Set quality to 1080p
205
+
206
+ // Fullscreen control
207
+ embed.enterFullscreen(); // Enter fullscreen mode
208
+ embed.exitFullscreen(); // Exit fullscreen mode
209
+ embed.toggleFullscreen(); // Toggle fullscreen
210
+ const isFs = embed.isFullscreen(); // Check fullscreen state
211
+
212
+ // Set extra player parameters
213
+ embed.playerVars = { loop: 1, playlist: "dQw4w9WgXcQ" };
214
+ ```
215
+
216
+ **Passing Extra Parameters:**
217
+
218
+ You can pass additional parameters to the YouTube or Vimeo player using the `player-vars` attribute or `playerVars` property:
219
+
220
+ ```html
221
+ <!-- YouTube: Loop a video -->
222
+ <youtube-embed
223
+ video-id="dQw4w9WgXcQ"
224
+ player-vars='{"loop": 1, "playlist": "dQw4w9WgXcQ"}'
225
+ ></youtube-embed>
226
+
227
+ <!-- YouTube: Custom start time and quality -->
228
+ <youtube-embed
229
+ video-id="dQw4w9WgXcQ"
230
+ player-vars='{"start": 30, "end": 90, "quality": "hd720"}'
231
+ ></youtube-embed>
232
+ ```
233
+
234
+ ```javascript
235
+ // Set via JavaScript
236
+ const embed = document.querySelector("youtube-embed");
237
+ embed.playerVars = {
238
+ loop: 1,
239
+ playlist: "dQw4w9WgXcQ", // Required for loop to work
240
+ start: 10, // Start at 10 seconds
241
+ modestbranding: 1, // Minimal YouTube branding
242
+ rel: 0, // Don't show related videos
243
+ };
244
+ ```
245
+
246
+ See [YouTube Player Parameters](https://developers.google.com/youtube/player_parameters) for all available options.
247
+
248
+ #### Vimeo Embed
249
+
250
+ ```javascript
251
+ const embed = document.querySelector("vimeo-embed");
252
+
253
+ // Playback control
254
+ embed.play(); // ▶️ Play video
255
+ embed.pause(); // ⏸️ Pause video
256
+ embed.stopVideo(); // ⏹️ Stop video
257
+ embed.togglePlay(); // ⏯️ Toggle play/pause
258
+
259
+ // Audio control
260
+ embed.mute(); // 🔇 Mute audio
261
+ embed.unmute(); // 🔊 Unmute audio
262
+ embed.toggleMute(); // 🔇 Toggle mute
263
+
264
+ // Video management
265
+ embed.loadVideo("123456789"); // Load by video ID
266
+ embed.loadVideo("https://vimeo.com/123456789"); // Load by URL
267
+
268
+ // State inspection
269
+ const state = embed.getPlayerState();
270
+ // Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
271
+
272
+ // Async methods
273
+ const currentTime = await embed.getCurrentTime();
274
+ const isMuted = await embed.isMuted();
275
+
276
+ // Quality control (async)
277
+ const qualities = await embed.getAvailableQualities(); // Get available quality levels
278
+ const current = await embed.getCurrentQuality(); // Get current quality
279
+ await embed.setQuality("720p"); // Set quality to 720p
280
+
281
+ // Fullscreen control
282
+ await embed.enterFullscreen(); // Enter fullscreen mode
283
+ await embed.exitFullscreen(); // Exit fullscreen mode
284
+ await embed.toggleFullscreen(); // Toggle fullscreen
285
+ const isFs = embed.isFullscreen(); // Check fullscreen state
286
+ ```
287
+
288
+ **Passing Extra Parameters:**
289
+
290
+ You can pass additional parameters to the Vimeo player using the `player-vars` attribute or `playerVars` property:
291
+
292
+ ```html
293
+ <!-- Vimeo: Loop a video -->
294
+ <vimeo-embed video-id="76979871" player-vars='{"loop": true}'></vimeo-embed>
295
+
296
+ <!-- Vimeo: Custom color and quality -->
297
+ <vimeo-embed
298
+ video-id="76979871"
299
+ player-vars='{"color": "ff0000", "quality": "720p"}'
300
+ ></vimeo-embed>
301
+
302
+ <!-- Vimeo: Privacy and customization -->
303
+ <vimeo-embed
304
+ video-id="76979871"
305
+ player-vars='{"dnt": true, "title": false, "byline": false, "portrait": false}'
306
+ ></vimeo-embed>
307
+ ```
308
+
309
+ ```javascript
310
+ // Set via JavaScript
311
+ const embed = document.querySelector("vimeo-embed");
312
+ embed.playerVars = {
313
+ loop: true, // Enable looping
314
+ color: "00adef", // Custom player color (hex without #)
315
+ title: false, // Hide video title
316
+ byline: false, // Hide author
317
+ portrait: false, // Hide author portrait
318
+ dnt: true, // Do Not Track
319
+ };
320
+ ```
321
+
322
+ See [Vimeo Embed Options](https://developer.vimeo.com/player/sdk/embed) for all available options.
323
+
324
+ ### Events
325
+
326
+ Both components support the same event interface:
327
+
328
+ ```javascript
329
+ // Listen to player events
330
+ embed.addEventListener("ready", () => console.log("Player ready"));
331
+ embed.addEventListener("play", () => console.log("Playing"));
332
+ embed.addEventListener("pause", () => console.log("Paused"));
333
+ embed.addEventListener("ended", () => console.log("Video ended"));
334
+ embed.addEventListener("error", (e) => console.error("Error:", e.detail));
335
+ embed.addEventListener("qualitychange", (e) => {
336
+ console.log(
337
+ "Quality changed:",
338
+ e.detail.oldQuality,
339
+ "→",
340
+ e.detail.newQuality
341
+ );
342
+ console.log("Available qualities:", e.detail.availableQualities);
343
+ });
344
+ ```
345
+
346
+ ### Video Quality
347
+
348
+ Both components support programmatic quality control and emit events when quality changes.
349
+
350
+ #### YouTube Quality Levels
351
+
352
+ | Quality Level | Resolution | Description |
353
+ | ------------- | ---------- | ------------------- |
354
+ | `tiny` | 144p | Lowest quality |
355
+ | `small` | 240p | Low quality |
356
+ | `medium` | 360p | Medium quality |
357
+ | `large` | 480p | Standard quality |
358
+ | `hd720` | 720p | HD quality |
359
+ | `hd1080` | 1080p | Full HD |
360
+ | `hd1440` | 1440p | 2K quality |
361
+ | `hd2160` | 2160p | 4K quality |
362
+ | `highres` | >1080p | Highest available |
363
+ | `auto` | Auto | Automatic selection |
364
+
365
+ #### Vimeo Quality Levels
366
+
367
+ | Quality Level | Resolution | Description |
368
+ | ------------- | ---------- | ------------------- |
369
+ | `240p` | 240p | Low quality |
370
+ | `360p` | 360p | Medium quality |
371
+ | `540p` | 540p | Standard quality |
372
+ | `720p` | 720p | HD quality |
373
+ | `1080p` | 1080p | Full HD |
374
+ | `2k` | 1440p | 2K quality |
375
+ | `4k` | 2160p | 4K quality |
376
+ | `auto` | Auto | Automatic selection |
377
+
378
+ #### Usage Examples
379
+
380
+ ```html
381
+ <!-- Set initial quality via attribute -->
382
+ <youtube-embed video-id="dQw4w9WgXcQ" quality="hd1080"></youtube-embed>
383
+ <vimeo-embed video-id="76979871" quality="720p"></vimeo-embed>
384
+ ```
385
+
386
+ ```javascript
387
+ // YouTube quality control
388
+ const ytEmbed = document.querySelector("youtube-embed");
389
+ const qualities = ytEmbed.getAvailableQualities(); // ["auto", "hd1080", "hd720", "large", "medium"]
390
+ const current = ytEmbed.getCurrentQuality(); // "hd720"
391
+ ytEmbed.setQuality("hd1080"); // Switch to 1080p
392
+
393
+ // Vimeo quality control (async)
394
+ const vimeoEmbed = document.querySelector("vimeo-embed");
395
+ const qualities = await vimeoEmbed.getAvailableQualities(); // ["auto", "1080p", "720p", "540p"]
396
+ const current = await vimeoEmbed.getCurrentQuality(); // "720p"
397
+ await vimeoEmbed.setQuality("1080p"); // Switch to 1080p
398
+ ```
399
+
400
+ #### Quality Change Event
401
+
402
+ ```javascript
403
+ embed.addEventListener("qualitychange", (event) => {
404
+ const { oldQuality, newQuality, availableQualities } = event.detail;
405
+ console.log(`Quality changed from ${oldQuality} to ${newQuality}`);
406
+ console.log(`Available: ${availableQualities.join(", ")}`);
407
+ });
408
+ ```
409
+
410
+ ### Error Handling
411
+
412
+ Components include comprehensive error handling with automatic retry and user-friendly feedback:
413
+
414
+ #### Features
415
+
416
+ - **Automatic Retry**: API loads retry up to 3 times with 2-second delays
417
+ - **Timeout Protection**: 10-second timeout prevents indefinite waiting
418
+ - **Visual Feedback**: Error UI with descriptive messages and retry button
419
+ - **Event Notifications**: Detailed error events with codes and retry status
420
+
421
+ #### Error Event Details
422
+
423
+ ```javascript
424
+ embed.addEventListener("error", (e) => {
425
+ console.log(e.detail.message); // Human-readable error message
426
+ console.log(e.detail.code); // Error code (e.g., "API_LOAD_ERROR")
427
+ console.log(e.detail.retryable); // Whether error is retryable
428
+ });
429
+ ```
430
+
431
+ #### Common Error Scenarios
432
+
433
+ | Error Type | Cause | Solution |
434
+ | -------------------- | -------------------------- | ------------------------------------ |
435
+ | API Load Timeout | Ad blocker or slow network | Disable ad blocker, check connection |
436
+ | Script Load Failed | Network issue or CORS | Verify internet connection |
437
+ | Video Not Found | Invalid video ID | Check video ID/URL |
438
+ | Embedding Restricted | Video owner restrictions | Use different video |
439
+
440
+ ### Accessibility
441
+
442
+ Both components are fully accessible with keyboard navigation and screen reader support:
443
+
444
+ #### Keyboard Shortcuts
445
+
446
+ | Key | Action |
447
+ | ----------------- | -------------------------- |
448
+ | `Tab` | Focus player |
449
+ | `Enter` / `Space` | Activate lazy-loaded video |
450
+ | `k` / `Space` | Play/Pause video |
451
+ | `m` | Mute/Unmute |
452
+ | `f` | Toggle fullscreen |
453
+
454
+ #### ARIA Support
455
+
456
+ - **ARIA Roles**: Components use `role="region"` with `aria-label="Video player"`
457
+ - **Live Regions**: Screen reader announcements for state changes (playing, paused, muted, etc.)
458
+ - **Button Labels**: Play buttons have descriptive `aria-label` attributes
459
+ - **Focus Management**: Proper `tabindex` and focus indicators
460
+ - **Keyboard Accessible**: All interactive elements support keyboard navigation
461
+
462
+ ```html
463
+ <!-- Example: Accessible video embed -->
464
+ <youtube-embed
465
+ url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
466
+ controls
467
+ aria-label="Product demo video"
468
+ tabindex="0"
469
+ >
470
+ </youtube-embed>
471
+ ```
472
+
473
+ ### Properties
474
+
475
+ All attributes are accessible as JavaScript properties:
476
+
477
+ ```javascript
478
+ embed.autoplay = true; // Enable autoplay
479
+ embed.muted = false; // Unmute video
480
+ embed.url = "https://..."; // Change video URL
481
+ console.log(embed.playing); // Get current playing state
482
+ ```
483
+
484
+ ## 🎯 Examples
485
+
486
+ Comprehensive examples are available in the `examples/` directory:
487
+
488
+ - **`examples/index.html`** - Overview and navigation to all demos
489
+ - **YouTube Demos:**
490
+ - `youtube-basic-demo.html` - Basic usage patterns and modal integration
491
+ - `youtube-lazy-loading-demo.html` - Performance optimization techniques
492
+ - `youtube-background-demo.html` - Background video integration
493
+ - **Vimeo Demos:**
494
+ - `vimeo-basic-demo.html` - Comprehensive Vimeo features
495
+ - `vimeo-lazy-loading-demo.html` - Performance-focused lazy loading
496
+ - `vimeo-background-demo.html` - Full-screen background videos
497
+ - `vimeo-demo.html` - Original Vimeo integration demo
498
+ - **`platform-comparison-demo.html`** - Side-by-side platform comparison
499
+
500
+ ### Running Examples
501
+
502
+ 1. Build the component: `npm run build`
503
+ 2. Open `examples/index.html` in your browser
504
+ 3. Explore different usage patterns and features
505
+
506
+ ## 🧪 Testing
507
+
508
+ This component includes a comprehensive automated test suite to ensure reliability, performance, and correct functionality.
509
+
510
+ ### Running Tests
511
+
512
+ 1. **Quick Access**: Visit `test/index.html` in your browser
513
+ 2. **From Examples**: Navigate to the test suite from `examples/index.html`
514
+ 3. **Direct URL**: `http://localhost:8000/test/` (when serving locally)
515
+
516
+ ### Test Categories
517
+
518
+ #### 🔧 **Unit Tests** (20+ tests)
519
+
520
+ - Component creation and initialization
521
+ - Attribute parsing and validation
522
+ - Video ID extraction from various URL formats
523
+ - Method availability and error handling
524
+ - DOM structure verification
525
+
526
+ #### ⏳ **Lazy Loading Tests** (15+ tests)
527
+
528
+ - Poster image display and interaction
529
+ - Deferred iframe creation
530
+ - Click-to-load functionality
531
+ - Performance benefit verification
532
+ - Resource optimization testing
533
+
534
+ #### ▶️ **Player State Tests** (12+ tests)
535
+
536
+ - Play/pause/stop operations
537
+ - State transitions and reporting
538
+ - Mute/unmute functionality
539
+ - Custom vs native controls
540
+ - Error recovery mechanisms
541
+
542
+ #### 🔗 **Integration Tests** (10+ tests)
543
+
544
+ - YouTube/Vimeo iframe API loading
545
+ - Real player initialization and events
546
+ - Cross-origin and security features
547
+ - Browser compatibility verification
548
+ - Multiple instance handling
549
+
550
+ #### ⚡ **Performance Tests** (10+ tests)
551
+
552
+ - Initialization speed benchmarks (< 200ms target)
553
+ - Memory leak detection
554
+ - Resource optimization verification
555
+ - Scalability testing (20+ instances)
556
+ - Bundle size impact assessment
557
+
558
+ ### Test Framework Features
559
+
560
+ - **Visual Progress Tracking** - Real-time progress bars and statistics
561
+ - **Console Capture** - All logs displayed in beautiful interface
562
+ - **Test Filtering** - Run specific suites or filter by pass/fail
563
+ - **Results Export** - Download detailed JSON reports
564
+ - **Performance Monitoring** - Automatic timing and memory tracking
565
+
566
+ ### Example Test Usage
567
+
568
+ ```javascript
569
+ // Run all tests
570
+ runAllTests();
571
+
572
+ // Run specific test suites
573
+ runTestSuite("unit"); // Unit tests only
574
+ runTestSuite("lazy"); // Lazy loading tests
575
+ runTestSuite("integration"); // Integration tests
576
+ runTestSuite("performance"); // Performance tests
577
+ ```
578
+
579
+ ### Test Results Interpretation
580
+
581
+ - **90%+ Success Rate** - Excellent, production ready
582
+ - **70-89% Success Rate** - Good, minor issues may exist
583
+ - **< 70% Success Rate** - Issues need investigation
584
+
585
+ Common test failures in browser environments:
586
+
587
+ - YouTube/Vimeo API loading delays
588
+ - Network connectivity issues
589
+ - Browser security restrictions
590
+ - Timing-sensitive operations
591
+
592
+ ## ⚡ Performance Benefits
593
+
594
+ ### Lazy Loading
595
+
596
+ - **Faster initial page loads** - Videos load on-demand
597
+ - **Reduced bandwidth** - Only loads when user wants to watch
598
+ - **Better Core Web Vitals** - Improved LCP scores
599
+ - **Mobile optimization** - Data usage conscious
600
+
601
+ ### Event-Driven Architecture
602
+
603
+ - **Efficient updates** - React to state changes without polling
604
+ - **Better UX** - Responsive to user interactions
605
+ - **Easy integration** - Standard event listeners
606
+
607
+ ## 🛠️ Development
608
+
609
+ ### Build Process
610
+
611
+ #### Available Build Scripts
612
+
613
+ | Script | Purpose | Usage |
614
+ | ------------------------ | -------------------------------- | ------------------------------- |
615
+ | `npm run build` | Full clean build with validation | Always run before testing |
616
+ | `npm run build:clean` | Clean dist directory | Removes old build artifacts |
617
+ | `npm run build:ts` | Compile TypeScript only | Quick TypeScript updates |
618
+ | `npm run build:css` | Compile SCSS only | Quick style updates |
619
+ | `npm run build:validate` | Verify build integrity | Checks all required files |
620
+ | `npm run test:build` | Build + ready message | One-command test preparation |
621
+ | `npm run test:validate` | Build + comprehensive check | Full environment validation |
622
+ | `npm run watch` | Watch mode (TypeScript + SCSS) | Auto-rebuild during development |
623
+ | `npm run examples` | Build + examples ready | Prepare examples for viewing |
624
+
625
+ #### Quick Start
626
+
627
+ ```bash
628
+ # Initial setup (one time)
629
+ npm install
630
+
631
+ # Development workflow (recommended)
632
+ npm run watch # Auto-rebuild on changes
633
+
634
+ # OR manual builds
635
+ npm run build # Full clean build
636
+ npm run test:validate # Build + validate everything
637
+ ```
638
+
639
+ #### Build Validation
640
+
641
+ The build process includes automatic validation that checks:
642
+
643
+ - ✅ **TypeScript compilation** - `dist/index.js` and component files
644
+ - ✅ **SCSS compilation** - `dist/css/main.css` with proper styling
645
+ - ✅ **Test file integrity** - All test files present and accessible
646
+ - ✅ **Documentation** - README and guides are available
647
+
648
+ ### Testing Workflow
649
+
650
+ #### Quick Testing Guide
651
+
652
+ 1. **Build the component** (required):
653
+
654
+ ```bash
655
+ npm run build
656
+ ```
657
+
658
+ 2. **Start watch mode** (recommended for development):
659
+
660
+ ```bash
661
+ npm run watch
662
+ ```
663
+
664
+ 3. **Open the test suite**:
665
+ - Navigate to `test/index.html` in your browser
666
+ - Or serve via local server: `python -m http.server 8000`
667
+ - Visit: `http://localhost:8000/test/`
668
+
669
+ #### Development Workflow with Tests
670
+
671
+ When using `npm run watch`, the system will:
672
+
673
+ - ✅ Automatically compile TypeScript changes to JavaScript
674
+ - ✅ Automatically compile SCSS changes to CSS
675
+ - ✅ Keep compiled files up to date
676
+
677
+ **Workflow:**
678
+
679
+ 1. Start watch mode: `npm run watch`
680
+ 2. Make changes to TypeScript or SCSS files
681
+ 3. Refresh the test page to see updates
682
+ 4. Tests automatically use latest compiled code
683
+
684
+ #### Test Features
685
+
686
+ - ✅ **67+ comprehensive tests** across 5 categories (YouTube) + 5 categories (Vimeo)
687
+ - ✅ **Automatic component detection** and loading
688
+ - ✅ **Real-time console output** with colored messages
689
+ - ✅ **Progress tracking** and detailed statistics
690
+ - ✅ **JSON export** for results documentation
691
+ - ✅ **Individual test suite** execution
692
+ - ✅ **Component demo** integration
693
+
694
+ #### Troubleshooting Tests
695
+
696
+ **Component Not Loading:**
697
+
698
+ - Error: "Component not registered"
699
+ - Solution: Run `npm run build` or `npm run watch` first
700
+
701
+ **CORS Issues:**
702
+
703
+ - Error: Module loading fails
704
+ - Solution: Serve files via HTTP server instead of file:// protocol
705
+
706
+ **Outdated Tests:**
707
+
708
+ - Error: Tests show old behavior
709
+ - Solution: Ensure watch mode is running or rebuild manually
710
+
711
+ For detailed testing documentation, see `test/README.md`.
712
+
713
+ ## 🌐 Browser Support
714
+
715
+ - Modern browsers with Web Components support
716
+ - ES6+ JavaScript features
717
+ - YouTube iframe API integration
718
+ - Vimeo Player API integration
719
+
720
+ ## 📝 Migration Guide
721
+
722
+ ### From Standard iframes to Web Components
723
+
724
+ Migrating from traditional iframe embeds is straightforward:
725
+
726
+ #### Before (Standard iframe):
727
+
728
+ ```html
729
+ <iframe
730
+ width="560"
731
+ height="315"
732
+ src="https://www.youtube.com/embed/dQw4w9WgXcQ"
733
+ frameborder="0"
734
+ allow="autoplay; encrypted-media"
735
+ allowfullscreen
736
+ ></iframe>
737
+ ```
738
+
739
+ #### After (Web Component):
740
+
741
+ ```html
742
+ <youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
743
+ ```
744
+
745
+ **Benefits of Migration:**
746
+
747
+ - ✅ Lazy loading support (faster page loads)
748
+ - ✅ Programmatic control (play, pause, mute, etc.)
749
+ - ✅ Event handling (ready, play, pause, ended, error)
750
+ - ✅ Quality control (set preferred video quality)
751
+ - ✅ Fullscreen API (enter/exit fullscreen programmatically)
752
+ - ✅ Accessibility features (keyboard shortcuts, ARIA support)
753
+ - ✅ Error handling with retry mechanism
754
+ - ✅ Responsive by default
755
+
756
+ ### From Previous Versions
757
+
758
+ - `pause()` now pauses instead of stopping (use `stopVideo()` to stop)
759
+ - Boolean attributes work correctly (presence = true, absence = false)
760
+ - Event-based architecture replaces callbacks
761
+ - `video-id` attribute supported alongside `url`
762
+
763
+ ### Event Migration
764
+
765
+ ```javascript
766
+ // Old approach
767
+ embed.onReady = () => {
768
+ /* ... */
769
+ };
770
+
771
+ // New approach
772
+ embed.addEventListener("ready", () => {
773
+ /* ... */
774
+ });
775
+ ```
776
+
777
+ ## 🔧 Troubleshooting
778
+
779
+ ### Common Issues and Solutions
780
+
781
+ #### Video Not Loading
782
+
783
+ **Problem**: Video doesn't load or shows error message
784
+
785
+ **Solutions**:
786
+
787
+ 1. **Check Video ID**: Ensure the video ID is correct and publicly accessible
788
+ 2. **Disable Ad Blockers**: Some ad blockers prevent video APIs from loading
789
+ 3. **Check Network**: Verify internet connection and firewall settings
790
+ 4. **CORS Issues**: Ensure your domain is allowed (usually not an issue with official APIs)
791
+
792
+ ```javascript
793
+ // Listen for error events
794
+ embed.addEventListener("error", (e) => {
795
+ console.log("Error details:", e.detail);
796
+ // e.detail.message - Human-readable error
797
+ // e.detail.code - Error code (e.g., "API_LOAD_ERROR")
798
+ // e.detail.retryable - Whether automatic retry will occur
799
+ });
800
+ ```
801
+
802
+ #### Autoplay Not Working
803
+
804
+ **Problem**: Video doesn't autoplay even with `autoplay` attribute
805
+
806
+ **Solutions**:
807
+
808
+ 1. **Add `muted` attribute**: Most browsers require videos to be muted for autoplay
809
+ 2. **User Interaction**: Some browsers require user interaction before autoplay
810
+
811
+ ```html
812
+ <!-- Correct autoplay setup -->
813
+ <youtube-embed video-id="VIDEO_ID" autoplay muted></youtube-embed>
814
+ ```
815
+
816
+ #### Lazy Loading Not Triggering
817
+
818
+ **Problem**: Video doesn't load when scrolled into view
819
+
820
+ **Solutions**:
821
+
822
+ 1. **Check `lazy` attribute**: Ensure the `lazy` attribute is present
823
+ 2. **Viewport Visibility**: Element must be within viewport threshold (50px)
824
+ 3. **Browser Support**: Ensure browser supports Intersection Observer API
825
+
826
+ ```javascript
827
+ // Debug lazy loading
828
+ const embed = document.querySelector("youtube-embed");
829
+ console.log("Lazy enabled:", embed.hasAttribute("lazy"));
830
+ console.log(
831
+ "In viewport:",
832
+ embed.getBoundingClientRect().top < window.innerHeight
833
+ );
834
+ ```
835
+
836
+ #### Keyboard Shortcuts Not Working
837
+
838
+ **Problem**: Pressing 'k', 'm', or 'f' doesn't control the video
839
+
840
+ **Solutions**:
841
+
842
+ 1. **Focus Element**: Click on the video component to focus it first
843
+ 2. **Check Conflicts**: Ensure no other scripts are intercepting keyboard events
844
+ 3. **Browser Compatibility**: Some browsers may restrict keyboard events
845
+
846
+ **Available Shortcuts**:
847
+
848
+ - `k` - Play/Pause toggle
849
+ - `m` - Mute/Unmute toggle
850
+ - `f` - Fullscreen toggle
851
+
852
+ #### Quality Selection Issues
853
+
854
+ **Problem**: Cannot change video quality or quality selector shows no options
855
+
856
+ **Solutions**:
857
+
858
+ 1. **Wait for Ready Event**: Quality info only available after player is ready
859
+ 2. **Video Support**: Not all videos have multiple quality levels
860
+ 3. **API Limitations**: Platform APIs may not expose all quality levels immediately
861
+
862
+ ```javascript
863
+ embed.addEventListener("ready", async () => {
864
+ // For YouTube
865
+ const qualities = embed.getAvailableQualities();
866
+ console.log("Available:", qualities);
867
+
868
+ // For Vimeo (async)
869
+ const qualities = await embed.getAvailableQualities();
870
+ console.log("Available:", qualities);
871
+ });
872
+ ```
873
+
874
+ ## 🌐 Browser Compatibility
875
+
876
+ ### Minimum Requirements
877
+
878
+ | Feature | Chrome | Firefox | Safari | Edge |
879
+ | ------------------------- | ------ | ------- | ------ | ---- |
880
+ | **Web Components** | 67+ | 63+ | 10.1+ | 79+ |
881
+ | **Custom Elements** | 67+ | 63+ | 10.1+ | 79+ |
882
+ | **Shadow DOM** | 53+ | 63+ | 10.1+ | 79+ |
883
+ | **ES6 Modules** | 61+ | 60+ | 10.1+ | 79+ |
884
+ | **Intersection Observer** | 58+ | 55+ | 12.1+ | 79+ |
885
+ | **Fullscreen API** | 71+ | 64+ | 5.1+ | 79+ |
886
+
887
+ ### Feature Support
888
+
889
+ | Feature | Support | Polyfill Available | Notes |
890
+ | ------------------ | ------------------ | ------------------ | ------------------------------------- |
891
+ | Lazy Loading | Modern Browsers | ✅ | Falls back to immediate load |
892
+ | Keyboard Shortcuts | All Browsers | N/A | Browser-dependent focus behavior |
893
+ | Fullscreen API | Modern Browsers | ❌ | Vendor prefixes handled automatically |
894
+ | Quality Control | Platform-dependent | N/A | Depends on YouTube/Vimeo API support |
895
+ | ARIA Support | All Browsers | N/A | Screen reader compatibility |
896
+
897
+ ### Polyfills
898
+
899
+ For older browser support, include these polyfills:
900
+
901
+ ```html
902
+ <!-- Web Components Polyfills -->
903
+ <script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.8.0/webcomponents-loader.js"></script>
904
+
905
+ <!-- Intersection Observer Polyfill -->
906
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>
907
+ ```
908
+
909
+ ## 💡 Advanced Usage Examples
910
+
911
+ ### Playlist Implementation
912
+
913
+ ```html
914
+ <div id="playlist-container"></div>
915
+
916
+ <script type="module">
917
+ const videos = ["dQw4w9WgXcQ", "ScMzIvxBSi4", "M7lc1UVf-VE"];
918
+
919
+ let currentIndex = 0;
920
+ const container = document.getElementById("playlist-container");
921
+
922
+ function loadVideo(index) {
923
+ container.innerHTML = "";
924
+ const embed = document.createElement("youtube-embed");
925
+ embed.setAttribute("video-id", videos[index]);
926
+ embed.setAttribute("autoplay", "");
927
+
928
+ embed.addEventListener("ended", () => {
929
+ currentIndex = (currentIndex + 1) % videos.length;
930
+ loadVideo(currentIndex);
931
+ });
932
+
933
+ container.appendChild(embed);
934
+ }
935
+
936
+ loadVideo(0);
937
+ </script>
938
+ ```
939
+
940
+ ### Custom Styling
941
+
942
+ ```html
943
+ <style>
944
+ youtube-embed {
945
+ --embed-border-radius: 16px;
946
+ --embed-max-width: 800px;
947
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
948
+ }
949
+
950
+ youtube-embed::part(container) {
951
+ border-radius: var(--embed-border-radius);
952
+ }
953
+ </style>
954
+
955
+ <youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
956
+ ```
957
+
958
+ ### Dynamic Quality Selection
959
+
960
+ ```html
961
+ <youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>
962
+
963
+ <select id="quality-selector">
964
+ <option value="auto">Auto</option>
965
+ </select>
966
+
967
+ <script type="module">
968
+ const player = document.getElementById("player");
969
+ const selector = document.getElementById("quality-selector");
970
+
971
+ player.addEventListener("ready", () => {
972
+ const qualities = player.getAvailableQualities();
973
+
974
+ // Populate selector
975
+ qualities.forEach((quality) => {
976
+ if (quality !== "auto") {
977
+ const option = document.createElement("option");
978
+ option.value = quality;
979
+ option.textContent = quality.toUpperCase();
980
+ selector.appendChild(option);
981
+ }
982
+ });
983
+ });
984
+
985
+ selector.addEventListener("change", (e) => {
986
+ player.setQuality(e.target.value);
987
+ });
988
+
989
+ player.addEventListener("qualitychange", (e) => {
990
+ console.log(
991
+ `Quality changed: ${e.detail.oldQuality} → ${e.detail.newQuality}`
992
+ );
993
+ });
994
+ </script>
995
+ ```
996
+
997
+ ### Event Logger
998
+
999
+ ```html
1000
+ <youtube-embed id="player" video-id="dQw4w9WgXcQ"></youtube-embed>
1001
+ <div id="event-log"></div>
1002
+
1003
+ <script type="module">
1004
+ const player = document.getElementById("player");
1005
+ const log = document.getElementById("event-log");
1006
+
1007
+ const events = [
1008
+ "ready",
1009
+ "play",
1010
+ "pause",
1011
+ "ended",
1012
+ "error",
1013
+ "timeupdate",
1014
+ "qualitychange",
1015
+ ];
1016
+
1017
+ events.forEach((eventName) => {
1018
+ player.addEventListener(eventName, (e) => {
1019
+ const entry = document.createElement("div");
1020
+ entry.textContent = `${new Date().toLocaleTimeString()} - ${eventName}: ${JSON.stringify(
1021
+ e.detail || {}
1022
+ )}`;
1023
+ log.prepend(entry);
1024
+ });
1025
+ });
1026
+ </script>
1027
+ ```
1028
+
1029
+ ### Responsive Aspect Ratio
1030
+
1031
+ ```html
1032
+ <style>
1033
+ .video-wrapper {
1034
+ position: relative;
1035
+ width: 100%;
1036
+ padding-bottom: 56.25%; /* 16:9 aspect ratio */
1037
+ }
1038
+
1039
+ .video-wrapper youtube-embed {
1040
+ position: absolute;
1041
+ top: 0;
1042
+ left: 0;
1043
+ width: 100%;
1044
+ height: 100%;
1045
+ }
1046
+ </style>
1047
+
1048
+ <div class="video-wrapper">
1049
+ <youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
1050
+ </div>
1051
+ ```
1052
+
1053
+ ## 📦 Build & Distribution
1054
+
1055
+ ### Development Build
1056
+
1057
+ ```bash
1058
+ # Standard build (unminified for debugging)
1059
+ npm run build
1060
+
1061
+ # Watch mode for development
1062
+ npm run dev
1063
+
1064
+ # Validate build integrity
1065
+ npm run build:validate
1066
+ ```
1067
+
1068
+ ### Production Build
1069
+
1070
+ ```bash
1071
+ # Production build with minification (~50% size reduction)
1072
+ npm run build:prod
1073
+ ```
1074
+
1075
+ This creates optimized `.min.js` and `.min.css` files with:
1076
+
1077
+ - **Terser** minification for JavaScript (removes whitespace, shortens variable names)
1078
+ - **CSSO** optimization for CSS (removes redundant rules, merges selectors)
1079
+ - **~50% smaller** file sizes compared to development builds
1080
+
1081
+ ### Build Output Structure
1082
+
1083
+ ```
1084
+ dist/
1085
+ ├── index.js # Full bundle (58KB)
1086
+ ├── index.min.js # Full bundle minified (29KB) ⭐ Production
1087
+ ├── youtube-only.js # YouTube bundle (29KB)
1088
+ ├── youtube-only.min.js # YouTube minified (15KB) ⭐ Production
1089
+ ├── vimeo-only.js # Vimeo bundle (29KB)
1090
+ ├── vimeo-only.min.js # Vimeo minified (14KB) ⭐ Production
1091
+ ├── components/
1092
+ │ ├── YouTubeEmbed.js # YouTube component (58KB)
1093
+ │ ├── YouTubeEmbed.min.js # YouTube minified (29KB)
1094
+ │ ├── VimeoEmbed.js # Vimeo component (49KB)
1095
+ │ └── VimeoEmbed.min.js # Vimeo minified (27KB)
1096
+ ├── css/
1097
+ │ ├── components.css # Core styles (9KB)
1098
+ │ ├── components.min.css # Core minified (7KB) ⭐ Production
1099
+ │ ├── main.css # With Bootstrap (276KB)
1100
+ │ └── main.min.css # Bootstrap minified (224KB)
1101
+ └── types/ # TypeScript declarations
1102
+ ```
1103
+
1104
+ ### Performance Optimization
1105
+
1106
+ **Bundle Size Comparison:**
1107
+
1108
+ | Configuration | Size (Dev) | Size (Prod) | With gzip\* |
1109
+ | -------------------- | ---------- | ----------- | ----------- |
1110
+ | YouTube + Core CSS | 38KB | **22KB** | ~7KB |
1111
+ | Vimeo + Core CSS | 38KB | **21KB** | ~7KB |
1112
+ | Both + Core CSS | 67KB | **36KB** | ~12KB |
1113
+ | Both + Bootstrap CSS | 343KB | **253KB** | ~45KB |
1114
+
1115
+ \*Estimated with gzip compression enabled on server
1116
+
1117
+ **Optimization Tips:**
1118
+
1119
+ 1. ✅ **Use minified files** (`.min.js` / `.min.css`) in production - saves ~50%
1120
+ 2. ✅ **Choose platform-specific bundles** when possible - saves ~50%
1121
+ 3. ✅ **Use core CSS** instead of Bootstrap - saves ~97%
1122
+ 4. ✅ **Enable gzip/brotli** on your server - saves additional ~70%
1123
+ 5. ✅ **Lazy loading is built-in** - videos only load when visible
1124
+
1125
+ **Best Configuration for Production:**
1126
+
1127
+ ```html
1128
+ <!-- YouTube only: 22KB total (7KB gzipped) -->
1129
+ <script type="module" src="dist/youtube-only.min.js"></script>
1130
+ <link rel="stylesheet" href="dist/css/components.min.css" />
1131
+ ```
1132
+
1133
+ ### Bundle Sizes
1134
+
1135
+ | File | Uncompressed | Gzipped | Description |
1136
+ | ----------------- | ------------ | ------- | -------------------------------- |
1137
+ | `index.js` | 58KB | ~15KB | Full bundle with both components |
1138
+ | `youtube-only.js` | 29KB | ~8KB | YouTube component only |
1139
+ | `vimeo-only.js` | 29KB | ~8KB | Vimeo component only |
1140
+ | `components.css` | 15KB | ~3KB | Core component styles |
1141
+ | `main.css` | 276KB | ~35KB | Full styles with Bootstrap |
1142
+
1143
+ ### Publishing to NPM
1144
+
1145
+ ```bash
1146
+ # Update version in package.json
1147
+ npm version patch|minor|major
1148
+
1149
+ # Build and publish
1150
+ npm publish
1151
+ ```
1152
+
1153
+ ### Using in Your Project
1154
+
1155
+ #### ES Modules
1156
+
1157
+ ```javascript
1158
+ // Full bundle
1159
+ import "@siit-dev/video-embed";
1160
+
1161
+ // YouTube only
1162
+ import "@siit-dev/video-embed/dist/youtube-only.js";
1163
+
1164
+ // Vimeo only
1165
+ import "@siit-dev/video-embed/dist/vimeo-only.js";
1166
+ ```
1167
+
1168
+ #### With Bundlers (Webpack, Vite, etc.)
1169
+
1170
+ ```javascript
1171
+ // Install
1172
+ npm install @siit-dev/video-embed
1173
+
1174
+ // Import in your app
1175
+ import '@siit-dev/video-embed';
1176
+ import '@siit-dev/video-embed/dist/css/components.css';
1177
+
1178
+ // Use in your HTML/JSX
1179
+ <youtube-embed video-id="dQw4w9WgXcQ" controls></youtube-embed>
1180
+ ```
1181
+
1182
+ #### TypeScript Support
1183
+
1184
+ ```typescript
1185
+ import { YouTubeEmbed } from "@siit-dev/video-embed";
1186
+
1187
+ const embed = document.querySelector("youtube-embed") as YouTubeEmbed;
1188
+ embed.play();
1189
+ ```
1190
+
1191
+ ## 🤝 Contributing
1192
+
1193
+ 1. Fork the repository
1194
+ 2. Create a feature branch
1195
+ 3. Make your changes
1196
+ 4. Test with the examples
1197
+ 5. Submit a pull request
1198
+
1199
+ ## 📄 License
1200
+
1201
+ This project is open source. See the LICENSE file for details.
1202
+
1203
+ ---
1204
+
1205
+ **Built with vanilla JavaScript • Web Components • TypeScript ready**