feat(site): replace home page tech preview player with real player (#580)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Darius Cepulis
2026-03-04 09:34:10 -06:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 410fc01350
commit f623e676f2
8 changed files with 43 additions and 2066 deletions
-6
View File
@@ -499,15 +499,9 @@ importers:
'@videojs/html': '@videojs/html':
specifier: workspace:* specifier: workspace:*
version: link:../packages/html version: link:../packages/html
'@videojs/html-preview':
specifier: workspace:*
version: link:../packages/__tech-preview__/html
'@videojs/react': '@videojs/react':
specifier: workspace:* specifier: workspace:*
version: link:../packages/react version: link:../packages/react
'@videojs/react-preview':
specifier: workspace:*
version: link:../packages/__tech-preview__/react
astro: astro:
specifier: ^5.14.4 specifier: ^5.14.4
version: 5.16.5(@netlify/blobs@10.5.0)(@types/node@22.19.3)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) version: 5.16.5(@netlify/blobs@10.5.0)(@types/node@22.19.3)(@vercel/functions@2.2.13)(jiti@2.6.1)(lightningcss@1.31.1)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
+1 -1
View File
@@ -103,7 +103,7 @@ export default defineConfig({
vite: { vite: {
plugins: [tailwindcss()], plugins: [tailwindcss()],
optimizeDeps: { optimizeDeps: {
exclude: ['@videojs/react-preview', '@videojs/react', '@videojs/html'], exclude: ['@videojs/react', '@videojs/html'],
}, },
resolve: { resolve: {
dedupe: ['react', 'react-dom'], dedupe: ['react', 'react-dom'],
-2
View File
@@ -28,9 +28,7 @@
"@sentry/astro": "^10.32.1", "@sentry/astro": "^10.32.1",
"@tailwindcss/vite": "^4.2.0", "@tailwindcss/vite": "^4.2.0",
"@videojs/html": "workspace:*", "@videojs/html": "workspace:*",
"@videojs/html-preview": "workspace:*",
"@videojs/react": "workspace:*", "@videojs/react": "workspace:*",
"@videojs/react-preview": "workspace:*",
"astro": "^5.14.4", "astro": "^5.14.4",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"es-toolkit": "^1.32.0", "es-toolkit": "^1.32.0",
+24 -23
View File
@@ -5,37 +5,42 @@ import { framework, skin } from '@/stores/homePageDemos';
import ClientCode from '../Code/ClientCode'; import ClientCode from '../Code/ClientCode';
function generateHTMLCode(skin: Skin): string { function generateHTMLCode(skin: Skin): string {
const skinTag = `${skin}-skin`; const skinTag = skin === 'frosted' ? 'video-skin' : 'video-minimal-skin';
const skinFile = skin === 'frosted' ? 'skin' : 'minimal-skin';
return `<video-provider> return `<script type="module">
import 'https://cdn.jsdelivr.net/npm/videojs/html/video/player.js';
import 'https://cdn.jsdelivr.net/npm/videojs/html/video/${skinFile}.js';
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/videojs/html/video/${skinFile}.css" />
<video-player>
<${skinTag}> <${skinTag}>
<video src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4"></video> <video src="..."></video>
</${skinTag}> </${skinTag}>
</video-provider>`; </video-player>`;
} }
function generateReactCode(skin: Skin): string { function generateReactCode(skin: Skin): string {
const skinComponent = skin === 'frosted' ? 'FrostedSkin' : 'MinimalSkin'; const skinComponent = skin === 'frosted' ? 'VideoSkin' : 'MinimalVideoSkin';
const skinImport = skin === 'frosted' ? 'frosted' : 'minimal'; const skinCss = skin === 'frosted' ? 'skin' : 'minimal-skin';
return `// npm install @videojs/react-preview return `import { createPlayer, Poster } from '@videojs/react';
import { VideoProvider, ${skinComponent}, Video } from '@videojs/react-preview'; import { ${skinComponent}, Video, videoFeatures } from '@videojs/react/video';
import '@videojs/react-preview/skins/${skinImport}.css'; import '@videojs/react/video/${skinCss}.css';
export const VideoPlayer = () => { const Player = createPlayer({ features: [...videoFeatures] });
export function VideoPlayer() {
return ( return (
<VideoProvider> <Player.Provider>
<${skinComponent}> <${skinComponent}>
<Video src="https://stream.mux.com/lhnU49l1VGi3zrTAZhDm9LUUxSjpaPW9BL4jY25Kwo4/highest.mp4" /> <Video src="..." playsInline />
<Poster src="..." />
</${skinComponent}> </${skinComponent}>
</VideoProvider> </Player.Provider>
); );
};`; }`;
}
function generateJS(skin: Skin): string {
return `// npm install @videojs/html-preview
import '@videojs/html-preview/skins/${skin}';`;
} }
export default function BaseDemo({ className }: { className?: string }) { export default function BaseDemo({ className }: { className?: string }) {
@@ -49,14 +54,10 @@ export default function BaseDemo({ className }: { className?: string }) {
<Tab value="html" initial> <Tab value="html" initial>
HTML HTML
</Tab> </Tab>
<Tab value="javascript">JavaScript</Tab>
</TabsList> </TabsList>
<TabsPanel value="html" initial> <TabsPanel value="html" initial>
<ClientCode code={generateHTMLCode($skin)} lang="html" /> <ClientCode code={generateHTMLCode($skin)} lang="html" />
</TabsPanel> </TabsPanel>
<TabsPanel value="javascript">
<ClientCode code={generateJS($skin)} lang="javascript" />
</TabsPanel>
</TabsRoot> </TabsRoot>
); );
} }
+5 -12
View File
@@ -2,33 +2,26 @@ import { useStore } from '@nanostores/react';
import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs'; import { Tab, TabsList, TabsPanel, TabsRoot } from '@/components/Tabs';
import type { Skin } from '@/stores/homePageDemos'; import type { Skin } from '@/stores/homePageDemos';
import { framework, skin } from '@/stores/homePageDemos'; import { framework, skin } from '@/stores/homePageDemos';
import {
generateHTMLCSS,
generateHTMLJS,
generateHTMLMarkup,
generateReactComponent,
generateReactCSS as genReactCSS,
} from '@/utils/ejectCodeGenerator';
import ClientCode from '../Code/ClientCode'; import ClientCode from '../Code/ClientCode';
function generateReactCode(skin: Skin): string { function generateReactCode(skin: Skin): string {
return generateReactComponent(skin); return `react`;
} }
function generateReactCSS(skin: Skin): string { function generateReactCSS(skin: Skin): string {
return genReactCSS(skin); return `css`;
} }
function generateHTMLCode(skin: Skin): string { function generateHTMLCode(skin: Skin): string {
return generateHTMLMarkup(skin); return `html`;
} }
function generateCSS(skin: Skin): string { function generateCSS(skin: Skin): string {
return generateHTMLCSS(skin); return `css`;
} }
function generateJS(skin: Skin): string { function generateJS(skin: Skin): string {
return generateHTMLJS(skin); return `js`;
} }
export default function EjectDemo({ className }: { className?: string }) { export default function EjectDemo({ className }: { className?: string }) {
+11 -12
View File
@@ -1,24 +1,23 @@
import { useStore } from '@nanostores/react'; import { useStore } from '@nanostores/react';
import { FrostedSkin, HlsVideo, MinimalSkin, VideoProvider } from '@videojs/react-preview'; import { createPlayer, Poster } from '@videojs/react';
import { MinimalVideoSkin, Video, VideoSkin, videoFeatures } from '@videojs/react/video';
import { VJS8_DEMO_VIDEO } from '@/consts'; import { VJS8_DEMO_VIDEO } from '@/consts';
import { skin } from '@/stores/homePageDemos'; import { skin } from '@/stores/homePageDemos';
import '@videojs/react-preview/skins/frosted.css'; import '@videojs/react/video/skin.css';
import '@videojs/react-preview/skins/minimal.css'; import '@videojs/react/video/minimal-skin.css';
const Player = createPlayer({ features: [...videoFeatures] });
export default function HeroVideo({ className, poster }: { className?: string; poster: string }) { export default function HeroVideo({ className, poster }: { className?: string; poster: string }) {
const $skin = useStore(skin); const $skin = useStore(skin);
const SkinComponent = $skin === 'frosted' ? FrostedSkin : MinimalSkin; const SkinComponent = $skin === 'frosted' ? VideoSkin : MinimalVideoSkin;
return ( return (
<VideoProvider> <Player.Provider>
<SkinComponent className={className}> <SkinComponent className={className}>
<HlsVideo <Video src={VJS8_DEMO_VIDEO.mp4} playsInline />
// @ts-expect-error -- types are incorrect <Poster src={poster} />
src={VJS8_DEMO_VIDEO.hls}
poster={poster}
playsInline
/>
</SkinComponent> </SkinComponent>
</VideoProvider> </Player.Provider>
); );
} }
@@ -1,100 +1,8 @@
--- ---
title: Customize skins title: Customize skins
description: Learn how to customize Video.js v10 skins by copying and modifying them description: Learn how to customize Video.js skins
--- ---
import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import ServerCode from '@/components/Code/ServerCode.astro';
import { TabsRoot, TabsList, TabsPanel, Tab } from '@/components/Tabs.tsx';
import {
generateHTMLCSS,
generateHTMLJS,
generateHTMLMarkup,
generateReactComponent,
generateReactCSS,
} from '@/utils/ejectCodeGenerator';
Video.js v10 comes with pre-built skins like Frosted and Minimal, but you can fully customize them by "ejecting" the code and making it your own. Video.js v10 comes with pre-built skins like Frosted and Minimal, but you can fully customize them by "ejecting" the code and making it your own.
While eventually we'll have a CLI that'll eject skins in your preferred framework and style, for now we invite you to try it out with these copy-paste-ready implementations. 🚧 Under construction 🚧
## Frosted skin
<FrameworkCase frameworks={["react"]}>
<TabsRoot client:idle>
<TabsList client:idle label="Frosted React implementation">
<Tab client:idle value="component" initial>FrostedSkin.tsx</Tab>
<Tab client:idle value="css">frosted.css</Tab>
</TabsList>
<TabsPanel client:idle value="component" initial>
<ServerCode code={generateReactComponent('frosted')} lang="tsx" />
</TabsPanel>
<TabsPanel client:idle value="css">
<ServerCode code={generateReactCSS('frosted')} lang="css" />
</TabsPanel>
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot client:idle>
<TabsList client:idle label="Frosted HTML implementation">
<Tab client:idle value="markup" initial>HTML</Tab>
<Tab client:idle value="js">JS</Tab>
<Tab client:idle value="css">CSS</Tab>
</TabsList>
<TabsPanel client:idle value="markup" initial>
<ServerCode code={generateHTMLMarkup('frosted')} lang="html" />
</TabsPanel>
<TabsPanel client:idle value="js">
<ServerCode code={generateHTMLJS('frosted')} lang="typescript" />
</TabsPanel>
<TabsPanel client:idle value="css">
<ServerCode code={generateHTMLCSS('frosted')} lang="css" />
</TabsPanel>
</TabsRoot>
</FrameworkCase>
## Minimal skin
<FrameworkCase frameworks={["react"]}>
<TabsRoot client:idle>
<TabsList client:idle label="Minimal React implementation">
<Tab client:idle value="react" initial>MinimalSkin.tsx</Tab>
<Tab client:idle value="css">minimal.css</Tab>
</TabsList>
<TabsPanel client:idle value="react" initial>
<ServerCode code={generateReactComponent('minimal')} lang="tsx" />
</TabsPanel>
<TabsPanel client:idle value="css">
<ServerCode code={generateReactCSS('minimal')} lang="css" />
</TabsPanel>
</TabsRoot>
</FrameworkCase>
<FrameworkCase frameworks={["html"]}>
<TabsRoot client:idle>
<TabsList client:idle label="Minimal HTML implementation">
<Tab client:idle value="html" initial>HTML</Tab>
<Tab client:idle value="js">JS</Tab>
<Tab client:idle value="css">CSS</Tab>
</TabsList>
<TabsPanel client:idle value="html" initial>
<ServerCode code={generateHTMLMarkup('minimal')} lang="html" />
</TabsPanel>
<TabsPanel client:idle value="js">
<ServerCode code={generateHTMLJS('minimal')} lang="typescript" />
</TabsPanel>
<TabsPanel client:idle value="css">
<ServerCode code={generateHTMLCSS('minimal')} lang="css" />
</TabsPanel>
</TabsRoot>
</FrameworkCase>
File diff suppressed because it is too large Load Diff