@werk1/w1-system-videoblock
v2.0.1
Published
Universal video player supporting YouTube, Vimeo, HLS, DASH with coordination and GSAP integration for W1 System
Maintainers
Readme
@werk1/w1-system-videoblock
W1 System Video Components - Universal video player supporting YouTube, Vimeo, HLS, DASH, and progressive videos with coordination and GSAP integration.
Features
🌐 Universal Video Support
- YouTube: Auto-detects and loads YouTube IFrame API
- Vimeo: Auto-detects and loads Vimeo Player API
- HLS Streaming: Auto-loads HLS.js for adaptive streaming
- DASH Streaming: Auto-loads Dash.js for MPEG-DASH
- Progressive: Native support for MP4, WebM, OGV
🎯 Smart Loading
- On-Demand: Libraries loaded only when needed
- Zero Overhead: No unused libraries in bundle
- Native First: Uses browser native support when available
- Graceful Fallback: Falls back to simpler implementations
🎮 Professional Features
- Simplified Video Coordination: Smart coordination with platform-specific behavior
- Native Videos: Full auto stop/resume based on visibility and priority
- YouTube/Vimeo: Basic single-video mode + native platform behavior
- Picture-in-Picture: Native PiP support
- Keyboard Controls: Optional space/arrow key navigation
- Custom Themes: Dark, light, transparent UI themes
- Modern SVG Icons: Professional vector icons for all controls
- Progress Tracking: Real-time callbacks and analytics
- Smart Logging: Production-optimized logging system
- Performance Optimized: Advanced state comparison and debouncing
- Centralized Types: Single source of truth for all platform types
Architecture
- W1VideoPlayer: Pure video engine (no store coordination)
- W1VideoBlock: Complete coordinator with simplified platform-specific behavior
- VideoCoordinationSlice: Zustand store integration with smart decision engine
- Centralized Types: All YouTube, Vimeo, HLS, DASH types in single location
- Icon Components: Scalable SVG icons (PlayIcon, PauseIcon, VolumeIcon, FullscreenIcon, PictureInPictureIcon)
- Smart Logger: Environment-aware logging for clean production builds
Simplified Coordination Strategy
- Native Videos (MP4, HLS, DASH): Full W1 coordination with custom visibility thresholds
- External Platforms (YouTube, Vimeo): Basic single-video mode + native platform behavior
- Manual Actions: Play/pause coordinates across all video types
- Platform Behaviors: YouTube/Vimeo handle their own intersection observers and autoplay policies
Installation
npm install @werk1/w1-system-videoblockPeer Dependencies
npm install react react-dom zustandComponents & Icons
Video Components
W1VideoBlock- Main orchestrator component (handles all coordination and store management)W1VideoPlayer- Pure video engine (no store dependencies)W1VideoControls- UI controls layer
Modern SVG Icons
PlayIcon- Professional play buttonPauseIcon- Modern pause buttonVolumeIcon- Dynamic volume icon (mute/low/high states)FullscreenIcon- Enter/exit fullscreen togglePictureInPictureIcon- Picture-in-picture mode toggle
All icons support:
- Scalable:
sizeprop for any pixel size - Themeable:
color="currentColor"inherits theme colors - Accessible: Built-in ARIA labels and roles
- Lightweight: Optimized SVG paths
Basic Usage
Universal Video Support
import { W1VideoBlock } from '@werk1/w1-system-videoblock';
// Works with any video type
const VideoSection = () => {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
{/* YouTube Video */}
<W1VideoBlock
src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
isVisible={isVisible}
enableStreamingLibs={true}
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
/>
{/* Vimeo Video */}
<W1VideoBlock
src="https://vimeo.com/1084537"
isVisible={isVisible}
enableStreamingLibs={true}
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
/>
{/* HLS Stream */}
<W1VideoBlock
src="https://example.com/stream.m3u8"
isVisible={isVisible}
enableStreamingLibs={true}
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
/>
{/* Progressive Video */}
<W1VideoBlock
src="video.mp4"
isVisible={isVisible}
priority={5}
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
/>
</div>
);
};Using Individual Icons
import { PlayIcon, PauseIcon, VolumeIcon } from '@werk1/w1-system-videoblock';
const CustomControls = () => {
return (
<div>
<button>
<PlayIcon size={24} color="#ffffff" />
</button>
<button>
<PauseIcon size={24} color="currentColor" />
</button>
<button>
<VolumeIcon level={0.8} isMuted={false} size={20} />
</button>
</div>
);
};GSAP Integration
import { W1VideoBlock } from '@werk1/w1-system-videoblock';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
const AnimatedVideoSection = () => {
const triggerRef = useRef();
const [isVisible, setIsVisible] = useState(false);
useLayoutEffect(() => {
const trigger = ScrollTrigger.create({
trigger: triggerRef.current,
start: "top 80%",
end: "bottom 20%",
onEnter: () => setIsVisible(true),
onLeave: () => setIsVisible(false),
});
return () => trigger.kill();
}, []);
return (
<div ref={triggerRef}>
<W1VideoBlock
src="video.mp4"
isVisible={isVisible}
priority={10}
videoCoordinationStore={getVideoCoordinationStore}
/>
</div>
);
};BoundStore Integration
Add to your BoundStore
import { createVideoCoordinationSlice } from '@werk1/w1-system-videoblock';
import type { VideoCoordinationSlice } from '@werk1/w1-system-videoblock';
type BoundStore = DeviceInfoSlice &
UIStateSlice &
VideoCoordinationSlice; // Add video coordination
export const useBoundStore = create<BoundStore>()(
persist(
(...a) => ({
...createDeviceInfoSlice(...a),
...createUIStateSlice(...a),
...createVideoCoordinationSlice(...a), // Add video slice
}),
{
name: 'app-state',
partialize: (state) => ({
ui: state.ui,
// Don't persist video state
}),
},
),
);Use Video Coordination
const VideoComponent = () => {
const {
currentlyPlayingVideoBlock,
singleVideoMode,
setSingleVideoMode
} = useBoundStore();
return (
<div>
<button onClick={() => setSingleVideoMode(!singleVideoMode)}>
Single Video Mode: {singleVideoMode ? 'ON' : 'OFF'}
</button>
<p>Currently Playing: {currentlyPlayingVideoBlock?.id || 'None'}</p>
</div>
);
};API Reference
W1VideoBlock Props
interface W1VideoBlockProps {
src: string; // Video source URL
isVisible: boolean; // External visibility control
priority?: number; // Coordination priority (default: 0)
// Video props
autoplay?: boolean; // Auto-play when visible
muted?: boolean; // Start muted
loop?: boolean; // Loop video
objectFit?: 'cover' | 'contain'; // Video sizing
// Controls
showControls?: boolean; // Show video controls
controlsTheme?: 'dark' | 'light' | 'transparent';
showVolumeControl?: boolean;
showFullscreen?: boolean;
// Callbacks
onVisibilityChange?: (isVisible: boolean) => void;
onReady?: (methods: W1VideoPlayerMethods) => void;
}Video Coordination
The system provides smart coordination with platform-specific behavior:
Native Videos (MP4, HLS, DASH)
- Full Coordination: Auto stop/resume based on visibility and priority
- Custom Thresholds: GSAP ScrollTrigger integration with configurable visibility zones
- Priority System: Higher priority videos take precedence automatically
- Seamless Behavior: Complete control over video lifecycle
External Platforms (YouTube, Vimeo)
- Basic Single-Video Mode: Manual play pauses other videos
- Platform-Native Behavior: Respect YouTube/Vimeo's built-in intersection observers
- Simplified Integration: No fighting against platform autoplay policies
- Stable Performance: Reliable behavior across platform updates
Coordination Features
- Single Video Mode: Only one video plays at a time (default: enabled)
- Manual Actions: Play/pause coordinates across all video types
- Mobile Optimized: Respects mobile autoplay limitations
- Mixed Coordination: Native and external videos work together seamlessly
Performance
- Mobile-First: Optimized for mobile devices
- Single Video Coordination: Prevents multiple video streams
- GSAP Integration: Uses GSAP ScrollTrigger for smooth performance
- Lazy Loading: Videos only load when needed
- Smart State Comparison: 25x faster than JSON.stringify (50ms → 2ms)
- Debounced Updates: Intelligent currentTime updates to prevent frame drops
- Production Logging: Silent in production, debug-enabled in development
Debugging & Development
Smart Logging System
The package includes a production-optimized logging system:
// Enable debug logging in development
if (typeof window !== 'undefined') {
window.W1_VIDEO_DEBUG = true;
}
// Or via environment variable
process.env.W1_VIDEO_DEBUG = 'true'Logging Behavior:
- Production: Silent (no console output)
- Development: Full debug information
- Debug Mode: Detailed video coordination and streaming logs
TypeScript
Full TypeScript support with comprehensive type definitions.
Examples
Custom Visibility Hook
import { useRef, useState, useLayoutEffect } from 'react';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
const useVideoVisibility = (options = {}) => {
const triggerRef = useRef();
const [isVisible, setIsVisible] = useState(false);
useLayoutEffect(() => {
const trigger = ScrollTrigger.create({
trigger: triggerRef.current,
start: "top 80%",
end: "bottom 20%",
onEnter: () => setIsVisible(true),
onLeave: () => setIsVisible(false),
onEnterBack: () => setIsVisible(true),
onLeaveBack: () => setIsVisible(false),
...options,
});
return () => trigger.kill();
}, []);
return { triggerRef, isVisible };
};Multiple Video Coordination
const MultiVideoSection = () => {
const { triggerRef: triggerRef1, isVisible: isVisible1 } = useVideoVisibility();
const { triggerRef: triggerRef2, isVisible: isVisible2 } = useVideoVisibility();
return (
<>
<div ref={triggerRef1}>
<W1VideoBlock
src="hero-video.mp4"
isVisible={isVisible1}
priority={10} // Higher priority
/>
</div>
<div ref={triggerRef2}>
<W1VideoBlock
src="background-video.mp4"
isVisible={isVisible2}
priority={5} // Lower priority
/>
</div>
</>
);
};Browser Support
- Modern browsers with ES2020 support
- Chrome 80+
- Firefox 78+
- Safari 14+
- Edge 80+
Mobile Support
- iOS Safari 14+
- Chrome Mobile 80+
- Samsung Internet 13+
Troubleshooting
Video Won't Play on Mobile
Mobile browsers have strict autoplay policies. Ensure:
<W1VideoBlock
src="video.mp4"
muted={true} // Required for autoplay
playsInline={true} // Prevents fullscreen on iOS
/>Multiple Videos Playing
Check your BoundStore integration and single video mode:
const { singleVideoMode, setSingleVideoMode } = useBoundStore();
// Enable single video mode
useEffect(() => {
setSingleVideoMode(true);
}, []);YouTube/Vimeo Auto-Pause Behavior
YouTube and Vimeo have built-in intersection observers that pause videos when ~70% out of view. This is platform behavior and cannot be disabled:
- Expected: YouTube/Vimeo videos pause themselves based on visibility
- Solution: Use native MP4/HLS videos if you need custom visibility control
- Mixed Usage: Combine native videos (full control) with YouTube/Vimeo (platform behavior)
Platform Coordination Differences
Different video types have different coordination capabilities:
// Native videos: Full coordination with custom thresholds
<W1VideoBlock src="video.mp4" isVisible={isVisible} priority={10} />
// YouTube/Vimeo: Basic single-video mode + platform behavior
<W1VideoBlock src="https://youtube.com/watch?v=..." isVisible={isVisible} />GSAP ScrollTrigger Issues
Ensure GSAP ScrollTrigger is properly registered:
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature - Commit changes:
git commit -am 'Add new feature' - Push to branch:
git push origin feature/new-feature - Submit a pull request
Development Setup
# Clone the repository
git clone https://github.com/werk1/w1-system-videoblock.git
# Install dependencies
npm install
# Build package
npm run build
# Run tests
npm testChangelog
v1.3.0 (December 2024)
- ✨ Simplified Coordination Architecture: Platform-specific coordination behavior
- Native Videos: Full auto stop/resume with custom visibility thresholds
- YouTube/Vimeo: Basic single-video mode + native platform behavior
- ✨ Centralized Type System: Single source of truth for all platform types
- ✨ Stable Platform Integration: No longer fights YouTube/Vimeo built-in behaviors
- 🐛 Fixed: YouTube/Vimeo intersection observer conflicts
- 🐛 Fixed: Removed duplicate type definitions across platform files
- 📚 Documentation: Updated all docs to reflect simplified coordination approach
v1.2.0 (December 2024)
- ✨ Performance Optimizations: 25x faster state comparison (JSON.stringify → smart shallow comparison)
- ✨ Smart Logging System: Production-silent, development-verbose logging with debug flags
- ✨ Architecture Cleanup: Clean separation between W1VideoPlayer (engine) and W1VideoBlock (coordinator)
- ✨ UX Improvements: Fixed click behavior and controls visibility
- ✨ Single Video Mode: Improved coordination timing for proper video switching
- ✨ w1-system-core Integration: Ready for BoundStore pattern integration
- 🐛 Fixed: Eliminated dual store registration complexity
- 🐛 Fixed: External GSAP management (no more hardcoded animations)
- 📚 Documentation: Complete integration guides and corrected package references
v1.1.0
- Enhanced streaming support (YouTube, Vimeo, HLS, DASH)
- Modern SVG icons with theme support
- Professional UI controls with dark/light/transparent themes
- Picture-in-Picture enhancements with metadata validation
- Cross-browser compatibility improvements
- TypeScript safety improvements
v1.0.0
- Initial release
- W1VideoPlayer headless component
- W1VideoControls UI layer
- W1VideoBlock orchestrator
- VideoCoordinationSlice store integration
- Mobile-first architecture
- GSAP ScrollTrigger integration
License
MIT License
Copyright (c) 2024 WERK1
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
🔗 W1 System Core Integration
Adding VideoCoordination to Core BoundStore
When integrating with w1-system-core, add the video coordination slice to your main BoundStore:
// w1-system-core/src/stores/boundStore.ts
import { createVideoCoordinationSlice } from '@werk1/w1-system-videoblock';
import type { VideoCoordinationSlice } from '@werk1/w1-system-videoblock';
type BoundStore = DeviceInfoSlice &
UIStateSlice &
VideoCoordinationSlice; // Add video coordination
export const useBoundStore = create<BoundStore>()(
persist(
(...a) => ({
...createDeviceInfoSlice(...a),
...createUIStateSlice(...a),
...createVideoCoordinationSlice(...a), // Add video slice
}),
{
name: 'app-state',
partialize: (state) => ({
ui: state.ui,
deviceInfo: state.deviceInfo,
// Don't persist video state (always starts fresh)
}),
},
),
);Usage in Core Application
// In your w1-system-core pages/components
import { W1VideoBlock } from '@werk1/w1-system-videoblock';
import { useBoundStore } from '../stores/boundStore';
const VideoSection = () => {
const [isVisible, setIsVisible] = useState(false);
// Get video coordination from main bound store
const getVideoCoordinationStore = useCallback(() => {
return useBoundStore.getState();
}, []);
return (
<W1VideoBlock
src="video.mp4"
isVisible={isVisible}
videoCoordinationStore={getVideoCoordinationStore}
/>
);
};🎯 GSAP ScrollTrigger Integration (Complete Working Example)
The W1VideoBlock integrates perfectly with GSAP ScrollTrigger for scroll-based video visibility and autoplay. Here's the exact working pattern from our test environment:
'use client';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useCallback, useEffect, useRef, useState } from 'react';
import { create } from 'zustand';
import { W1VideoBlock, createVideoCoordinationSlice } from '@werk1/w1-system-videoblock';
import type { VideoCoordinationSlice } from '@werk1/w1-system-videoblock';
// Register GSAP plugins
if (typeof window !== 'undefined') {
gsap.registerPlugin(ScrollTrigger);
}
// Video coordination store
type BoundStore = VideoCoordinationSlice;
const useBoundStore = create<BoundStore>()(createVideoCoordinationSlice);
// Custom visibility hook for GSAP integration
const useVideoVisibility = (options = {}) => {
const triggerRef = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
useEffect(() => {
if (!isClient || !triggerRef.current) return;
const trigger = ScrollTrigger.create({
trigger: triggerRef.current,
start: "top bottom-=20%",
end: "bottom top+=20%",
onEnter: () => setIsVisible(true),
onLeave: () => setIsVisible(false),
onEnterBack: () => setIsVisible(true),
onLeaveBack: () => setIsVisible(false),
...options
});
return () => trigger.kill();
}, [isClient, options]);
return { triggerRef, isVisible };
};
export default function VideoPage() {
const { triggerRef: triggerRef1, isVisible: isVisible1 } = useVideoVisibility();
const { triggerRef: triggerRef2, isVisible: isVisible2 } = useVideoVisibility();
const getVideoCoordinationStore = useCallback(() => {
return useBoundStore.getState();
}, []);
return (
<div>
{/* Video 1 with GSAP ScrollTrigger */}
<div ref={triggerRef1} style={{ height: '100vh', background: '#000', margin: '20px 0' }}>
<h3 style={{ color: 'white', padding: '20px' }}>
Video 1 (Visible: {isVisible1 ? 'YES' : 'NO'})
</h3>
<div style={{ height: '80%', padding: '20px' }}>
<W1VideoBlock
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
priority={10}
isVisible={isVisible1} // CRITICAL: Pass GSAP-detected visibility
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
enableStreamingLibs={true}
enableKeyboardControls={true}
style={{
width: '100%',
height: '100%',
borderRadius: '12px',
overflow: 'hidden'
}}
/>
</div>
</div>
{/* Video 2 with GSAP ScrollTrigger */}
<div ref={triggerRef2} style={{ height: '100vh', background: '#333', margin: '20px 0' }}>
<h3 style={{ color: 'white', padding: '20px' }}>
YouTube Video (Visible: {isVisible2 ? 'YES' : 'NO'})
</h3>
<div style={{ height: '80%', padding: '20px' }}>
<W1VideoBlock
src="https://www.youtube.com/watch?v=YE7VzlLtp-4"
priority={5}
isVisible={isVisible2} // CRITICAL: Pass GSAP-detected visibility
showControls={true}
videoCoordinationStore={getVideoCoordinationStore}
enableStreamingLibs={true}
style={{
width: '100%',
height: '100%',
borderRadius: '12px',
overflow: 'hidden'
}}
/>
</div>
</div>
</div>
);
}🎯 Key Points
- Page-Level GSAP Integration: ScrollTrigger is implemented at the page level, not inside W1VideoBlock
- useVideoVisibility Hook: Creates individual
triggerRefandisVisiblestate for each video - Wrapper Pattern: Each video is wrapped in a
<div ref={triggerRef}>for scroll detection - isVisible Prop: W1VideoBlock receives the
isVisibleprop from the GSAP hook - Video Coordination: Uses the Zustand store for single-video playback coordination
This pattern provides automatic scroll-based play/pause functionality with perfect GSAP integration.
🚀 Quick Start
npm install @werk1/w1-system-videoblock