Skip to content

Discovery: component-library skins with Base UI and Radix - #2692

Draft
cjpillsbury wants to merge 12 commits into
mainfrom
feat/component-library-skins
Draft

Discovery: component-library skins with Base UI and Radix#2692
cjpillsbury wants to merge 12 commits into
mainfrom
feat/component-library-skins

Conversation

@cjpillsbury

@cjpillsbury cjpillsbury commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Explore the "mix and match" story of Video.js 10's React architecture by driving the player with third-party component libraries. Two sandbox templates (Base UI, Radix) each show the same controls bar built two ways behind an approach param: Video.js components with library elements handed in through render, and library components fed by usePlayer(selector), store actions, and the option hooks. Each template's README records the friction and assumptions it surfaced.

Preview

Sandbox preview on Vercel: https://v10-sandbox-git-feat-component-library-skins-mux.vercel.app/ (the shell's dropdowns do not list these one-off templates; use the direct links below).

Template Video.js components + library via render Library components + Video.js hooks
Base UI render hooks
Radix render hooks

Changes

  • /react-base-ui-skin/: Base UI (already a sandbox dependency) via render and via hooks
  • /react-radix-skin/: Radix (radix-ui umbrella, new sandbox dependency) via render and via hooks, including the function form of render driving Radix Toggle's controlled pressed from Video.js state
  • Sandbox Vite config lists the extra library entry points the optimizer needs under noDiscovery
Findings so far

render works for buttons; compounds are whole swaps.

  • Library buttons in render keep our translated aria-label, data-* state, tooltip wiring, and hotkey hints.
  • Sliders cannot be rendered into a library slider: our root owns pointer events and puts role="slider" on Thumb, while libraries own pointer on their root/control and put the role on a thumb or hidden <input type="range">. Keep ours and restyle, or go fully to the library slider via hooks.
  • Menus, popovers, tooltips are our implementations coordinated through the container popup group, positioner, and controls lock. A foreign menu must use the use*Options hooks; *RadioGroup.Options needs Menu.Root context.
  • Tooltip labels flow by context push from our buttons, so a bare library trigger loses i18n.
  • disabled is a core prop; only aria-disabled reaches the DOM.
  • Element-form render lets library props win on collisions and runs library handlers first; className concatenates.

Hooks approach costs.

  • Every library portal needs container={useContainer()} to survive fullscreen.
  • Library popups do not take our controls lock, so the bar can auto-hide under an open menu.
  • Labels are hand-written; only the shortcut comes from useHotkeyShortcut.

Radix specifics. No Button primitive (Slot-based button instead); asChild stacks collide on data-state; DropdownMenu opens on pointerdown; Popover has no hover-open.

Parity pass (second commit)

Both templates now match the default video skin's control set in both approaches and gate every control on its availability state (PiP, remote playback, captions, fullscreen, volume level, empty settings groups). A shell-style toolbar adds Media, Source, and Captions pickers (URL-backed, shared via @app/shared/react/library-skin-harness) so quality, audio tracks, chapters, thumbnails, and captions can be exercised. Edge fixes: the slider preview only shows while pointing, settings popups are height-capped, and the sandbox optimizer entries for the CommonJS engines were repointed after the adapter rename.

Where the hooks approach still falls short of the skin: no pointer preview, chapters, or thumbnails on a library slider, and no exported hook for the seek/volume/status indicators.

Known rough edges

  • Tooltip label ownership when a library tooltip wraps a Video.js button, and vice versa
  • No controls lock for library popups in the hooks approach
  • Hooks approach: no slider pointer preview/chapters/thumbnails; no input-action indicator hook; library popups do not take the controls lock
  • pnpm-lock.yaml shows re-resolution noise from pnpm normalizing peer variants alongside the two added dependencies

Testing

Manual, in the sandbox (pnpm dev:sandbox): open /react-base-ui-skin/ and /react-radix-skin/, switch Approach, then play/pause, seek by pointer and keyboard, mute, open the volume popover, open Settings and change speed, and enter fullscreen with a menu open. Verified in Playwright for both templates and approaches: play/pause round-trips, keyboard seek commits through the library slider, settings menus render rate/captions/quality groups inside the player container, and the console stays clean.

🤖 Generated with Claude Code

Explore using Video.js 10 with a third-party component library, Base UI
first since the sandbox shell already depends on it. One template, two
approaches behind an `approach` param:

- render: Video.js components keep behaviour and accessibility; Base UI
  buttons go in through `render`; Video.js sliders, menus, popovers, and
  tooltips stay and take Base UI-style classes.
- hooks: Base UI Button, Toggle, Slider, Menu, Popover, and Tooltip are
  fed by `usePlayer(selector)`, store actions, and the option hooks, with
  portals pointed at the player container.

The README records the friction each approach surfaced. The sandbox's
`noDiscovery` optimizer needs the Base UI menu and popover entries listed.
Second library in the component-library exploration. Same two-approach
template as the Base UI spike, using the `radix-ui` umbrella package:

- render: plain buttons render into a Slot-based button; toggle-shaped
  buttons use the function form of `render` to drive Radix Toggle's
  controlled `pressed` from Video.js state.
- hooks: Radix Slider, DropdownMenu, Tooltip, Popover, and Toggle fed by
  `usePlayer(selector)`, store actions, and the option hooks; portals
  point at the player container.

The README records Radix-specific findings (no Button primitive,
`asChild` data-state collisions, pointerdown-opened menus, no hover
popover).
@vercel

vercel Bot commented Sep 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
v10-sandbox Ready Ready Preview Sep 10, 2026 2:40pm UTC

Request Review

@netlify

netlify Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploy Preview for vjs-registry canceled.

Name Link
🔨 Latest commit 7ef1797
🔍 Latest deploy log https://app.netlify.com/projects/vjs-registry/deploys/6aa2c0ebac4d610008225be4

@netlify

netlify Bot commented Sep 9, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 7ef1797
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6aa2c0ebba4ef80008ce9a67
😎 Deploy Preview https://deploy-preview-2692--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@pkg-pr-new

pkg-pr-new Bot commented Sep 9, 2026

Copy link
Copy Markdown
@videojs/cdn

pnpm add https://pkg.pr.new/@videojs/cdn@2692

@videojs/core

pnpm add https://pkg.pr.new/@videojs/core@2692

@videojs/element

pnpm add https://pkg.pr.new/@videojs/element@2692

@videojs/html

pnpm add https://pkg.pr.new/@videojs/html@2692

@videojs/media

pnpm add https://pkg.pr.new/@videojs/media@2692

@videojs/react

pnpm add https://pkg.pr.new/@videojs/react@2692

@videojs/spf

pnpm add https://pkg.pr.new/@videojs/spf@2692

@videojs/store

pnpm add https://pkg.pr.new/@videojs/store@2692

@videojs/utils

pnpm add https://pkg.pr.new/@videojs/utils@2692

@videojs/cloudflare-video

pnpm add https://pkg.pr.new/@videojs/cloudflare-video@2692

@videojs/dash-video

pnpm add https://pkg.pr.new/@videojs/dash-video@2692

@videojs/hlsjs-video

pnpm add https://pkg.pr.new/@videojs/hlsjs-video@2692

@videojs/mux-audio

pnpm add https://pkg.pr.new/@videojs/mux-audio@2692

@videojs/mux-video

pnpm add https://pkg.pr.new/@videojs/mux-video@2692

@videojs/native-hls-video

pnpm add https://pkg.pr.new/@videojs/native-hls-video@2692

@videojs/shaka-video

pnpm add https://pkg.pr.new/@videojs/shaka-video@2692

@videojs/spotify-audio

pnpm add https://pkg.pr.new/@videojs/spotify-audio@2692

@videojs/tiktok-video

pnpm add https://pkg.pr.new/@videojs/tiktok-video@2692

@videojs/twitch-video

pnpm add https://pkg.pr.new/@videojs/twitch-video@2692

@videojs/vimeo-video

pnpm add https://pkg.pr.new/@videojs/vimeo-video@2692

@videojs/wistia-video

pnpm add https://pkg.pr.new/@videojs/wistia-video@2692

@videojs/youtube-video

pnpm add https://pkg.pr.new/@videojs/youtube-video@2692

@videojs/google-cast

pnpm add https://pkg.pr.new/@videojs/google-cast@2692

@videojs/mux-data

pnpm add https://pkg.pr.new/@videojs/mux-data@2692

commit: 7ef1797

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin 62.24 kB 62.66 kB +425 B (+0.7%) 0 B
/video/skin 62.03 kB 62.39 kB +373 B (+0.6%) 0 B
/video/skin2 8.00 kB 8.31 kB +319 B (+3.9%)
/video (default) 63.49 kB 63.89 kB +407 B (+0.6%) 0 B
/video (default + hls) 65.30 kB 65.69 kB +397 B (+0.6%) 0 B
/video (minimal) 63.87 kB 64.17 kB +311 B (+0.5%) 0 B
/video (minimal + hls) 65.56 kB 65.91 kB +358 B (+0.5%) 0 B
Small changes (25, ≤ 300 B)
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin.css 7.71 kB 7.88 kB +178 B (+2.3%)
/video/skin.css 7.51 kB 7.69 kB +183 B (+2.4%)
/video/minimal-skin2 8.16 kB 8.45 kB +296 B (+3.5%)
/audio/minimal-skin.css 5.60 kB 5.66 kB +70 B (+1.2%)
/audio/skin.css 5.43 kB 5.51 kB +76 B (+1.4%)
/audio/minimal-skin 46.19 kB 46.32 kB +127 B (+0.3%) 0 B
/audio/minimal-skin2 5.93 kB 6.04 kB +111 B (+1.8%)
/audio/skin 45.94 kB 46.11 kB +166 B (+0.4%) 0 B
/audio/skin2 5.75 kB 5.88 kB +127 B (+2.2%)
/live-video/minimal-skin.css 6.23 kB 6.32 kB +93 B (+1.5%)
/live-video/skin.css 6.23 kB 6.30 kB +79 B (+1.2%)
/live-video/minimal-skin 49.77 kB 49.94 kB +169 B (+0.3%) 0 B
/live-video/minimal-skin2 6.58 kB 6.72 kB +142 B (+2.1%)
/live-video/skin 49.79 kB 49.95 kB +166 B (+0.3%) 0 B
/live-video/skin2 6.63 kB 6.75 kB +126 B (+1.9%)
/live-audio/minimal-skin.css 4.45 kB 4.49 kB +47 B (+1.0%)
/live-audio/skin.css 4.45 kB 4.50 kB +47 B (+1.0%)
/live-audio/minimal-skin 35.64 kB 35.70 kB +61 B (+0.2%) 0 B
/live-audio/minimal-skin2 4.70 kB 4.78 kB +84 B (+1.7%)
/live-audio/skin 35.64 kB 35.78 kB +143 B (+0.4%) 0 B
/live-audio/skin2 4.70 kB 4.78 kB +84 B (+1.7%)
/ui/thumbnail 10.35 kB 10.41 kB +67 B (+0.6%) 0 B
/shared.css 118 B 134 B +16 B (+13.6%)
/audio (default) 47.47 kB 47.59 kB +122 B (+0.3%) 0 B
/audio (minimal) 47.70 kB 47.83 kB +136 B (+0.3%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 63.89 kB 58.19 kB
/video (default + hls) 65.69 kB 58.19 kB
/video (minimal) 64.17 kB 58.19 kB
/video (minimal + hls) 65.91 kB 58.19 kB
/audio (default) 47.59 kB 58.19 kB
/audio (minimal) 47.83 kB 58.19 kB
/background 11.65 kB 58.19 kB
Media (18)
Entry Initial
/media/mux-video 3.01 kB
/media/mux-audio 2.69 kB
/media/background-video 1.16 kB
/media/cloudflare-video 2.90 kB
/media/dash-video 2.63 kB
/media/hls-audio 22.05 kB
/media/hls-background-video 16.85 kB
/media/hls-video 28.01 kB
/media/hlsjs-video 2.63 kB
/media/mux-background-video 16.86 kB
/media/native-hls-video 7.59 kB
/media/shaka-video 2.63 kB
/media/spotify-audio 3.02 kB
/media/tiktok-video 3.13 kB
/media/twitch-video 2.93 kB
/media/vimeo-video 2.90 kB
/media/wistia-video 607 B
/media/youtube-video 2.90 kB
Players (5)
Entry Initial Lazy
/video/player 9.79 kB 58.19 kB
/audio/player 6.21 kB 58.19 kB
/background/player 4.46 kB 58.19 kB
/live-video/player 9.18 kB 58.19 kB
/live-audio/player 6.22 kB 58.19 kB
Skins (29)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.88 kB
/video/skin.css css 7.69 kB
/video/minimal-skin js 62.66 kB 58.19 kB
/video/minimal-skin2 js 8.45 kB
/video/skin js 62.39 kB 58.19 kB
/video/skin2 js 8.31 kB
/audio/minimal-skin.css css 5.66 kB
/audio/skin.css css 5.51 kB
/audio/minimal-skin js 46.32 kB 58.19 kB
/audio/minimal-skin2 js 6.04 kB
/audio/skin js 46.11 kB 58.19 kB
/audio/skin2 js 5.88 kB
/background/skin.css css 139 B
/background/skin js 9.91 kB 58.19 kB
/background/skin2 js 187 B
/live-video/minimal-skin.css css 6.32 kB
/live-video/skin.css css 6.30 kB
/live-video/minimal-skin js 49.94 kB 58.19 kB
/live-video/minimal-skin2 js 6.72 kB
/live-video/skin js 49.95 kB 58.19 kB
/live-video/skin2 js 6.75 kB
/live-audio/minimal-skin.css css 4.49 kB
/live-audio/skin.css css 4.50 kB
/live-audio/minimal-skin js 35.70 kB 58.19 kB
/live-audio/minimal-skin2 js 4.78 kB
/live-audio/skin js 35.78 kB 58.19 kB
/live-audio/skin2 js 4.78 kB
/global.css css 190 B
/shared.css css 134 B
UI Components (62)
Entry Initial Lazy
/ui/airplay-button 11.36 kB 58.19 kB
/ui/alert-dialog 8.79 kB 58.19 kB
/ui/audio-track-radio-group 11.46 kB 58.19 kB
/ui/buffering-indicator 8.68 kB 58.19 kB
/ui/captions-button 11.59 kB 58.19 kB
/ui/captions-radio-group 11.54 kB 58.19 kB
/ui/cast-button 11.34 kB 58.19 kB
/ui/container 9.58 kB 58.19 kB
/ui/controls 8.86 kB 58.19 kB
/ui/controls-backdrop 1.69 kB 58.19 kB
/ui/controls-content 1.67 kB 58.19 kB
/ui/dialog 8.73 kB 58.19 kB
/ui/dialog-backdrop 1.70 kB 58.19 kB
/ui/dialog-close 2.06 kB 58.19 kB
/ui/dialog-description 1.69 kB 58.19 kB
/ui/dialog-popup 1.90 kB 58.19 kB
/ui/dialog-title 1.70 kB 58.19 kB
/ui/error-dialog 12.47 kB 58.19 kB
/ui/fullscreen-button 11.33 kB 58.19 kB
/ui/gesture 9.66 kB 58.19 kB
/ui/hotkey 10.13 kB 58.19 kB
/ui/live-button 9.89 kB 58.19 kB
/ui/menu 18.54 kB 58.19 kB
/ui/menu-checkbox-item 1.97 kB 58.19 kB
/ui/menu-content 7.52 kB 58.19 kB
/ui/menu-group 2.09 kB 58.19 kB
/ui/menu-group-label 1.64 kB
/ui/menu-item 2.30 kB 58.19 kB
/ui/menu-item-indicator 1.38 kB 58.19 kB
/ui/menu-radio-group 3.01 kB 58.19 kB
/ui/menu-radio-item 2.13 kB 58.19 kB
/ui/menu-separator 1.30 kB 58.19 kB
/ui/mute-button 11.36 kB 58.19 kB
/ui/pip-button 11.31 kB 58.19 kB
/ui/play-button 11.31 kB 58.19 kB
/ui/playback-rate-button 11.41 kB 58.19 kB
/ui/playback-rate-radio-group 11.46 kB 58.19 kB
/ui/popover 8.57 kB 58.19 kB
/ui/poster 8.97 kB 58.19 kB
/ui/quality-radio-group 12.05 kB 58.19 kB
/ui/seek-button 11.34 kB 58.19 kB
/ui/seek-indicator 12.77 kB 58.19 kB
/ui/seek-indicator-value 1.10 kB
/ui/slider 11.75 kB 58.19 kB
/ui/slider-preview 2.01 kB 58.19 kB
/ui/status-announcer 10.68 kB 58.19 kB
/ui/status-indicator 13.10 kB 58.19 kB
/ui/status-indicator-value 1.10 kB
/ui/thumbnail 10.41 kB 58.19 kB
/ui/time 11.11 kB 58.19 kB
/ui/time-slider 13.22 kB 58.19 kB
/ui/time-slider-chapter-title 8.87 kB 58.19 kB
/ui/time-slider-chapters 9.71 kB 58.19 kB
/ui/title 8.25 kB 58.19 kB
/ui/tooltip 9.95 kB 58.19 kB
/ui/tooltip-label 1.21 kB
/ui/tooltip-shortcut 1.18 kB
/ui/volume-indicator 13.11 kB 58.19 kB
/ui/volume-indicator-fill 1.11 kB
/ui/volume-indicator-value 1.10 kB
/ui/volume-popover 14.82 kB 58.19 kB
/ui/volume-slider 12.43 kB 58.19 kB
⚛️ @videojs/react — 14 small size changes
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin.css 7.56 kB 7.70 kB +143 B (+1.8%)
/video/skin.css 7.37 kB 7.52 kB +150 B (+2.0%)
/video/minimal-skin 51.46 kB 51.47 kB +3 B (+0.0%) 0 B
/video/skin 51.32 kB 51.28 kB -41 B (-0.1%) 0 B
/audio/minimal-skin.css 5.42 kB 5.48 kB +68 B (+1.2%)
/audio/skin.css 5.25 kB 5.32 kB +67 B (+1.2%)
/live-video/minimal-skin.css 6.08 kB 6.16 kB +76 B (+1.2%)
/live-video/skin.css 6.08 kB 6.14 kB +69 B (+1.1%)
/live-audio/minimal-skin.css 4.26 kB 4.31 kB +49 B (+1.1%)
/live-audio/skin.css 4.26 kB 4.30 kB +36 B (+0.8%)
/video (default) 51.38 kB 51.41 kB +22 B (+0.0%) 0 B
/video (default + hls) 51.54 kB 51.67 kB +139 B (+0.3%) 0 B
/video (minimal) 51.51 kB 51.52 kB +7 B (+0.0%) 0 B
/video (minimal + hls) 51.80 kB 51.79 kB -12 B (-0.0%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 51.41 kB 58.19 kB
/video (default + hls) 51.67 kB 58.19 kB
/video (minimal) 51.52 kB 58.19 kB
/video (minimal + hls) 51.79 kB 58.19 kB
/audio (default) 39.82 kB 58.19 kB
/audio (minimal) 39.89 kB 58.19 kB
/background 581 B 58.19 kB
Media (22)
Entry Initial
/media/mux-video/hls-js 958 B
/media/mux-video/spf 962 B
/media/mux-audio/hls-js 699 B
/media/mux-audio/spf 703 B
/media/background-video 402 B
/media/cloudflare-video 911 B
/media/dash-video 693 B
/media/hls-audio 20.32 kB
/media/hls-background-video 16.47 kB
/media/hls-video 26.32 kB
/media/hlsjs-video 695 B
/media/mux-audio 693 B
/media/mux-background-video 16.41 kB
/media/mux-video 948 B
/media/native-hls-video 5.78 kB
/media/shaka-video 704 B
/media/spotify-audio 901 B
/media/tiktok-video 906 B
/media/twitch-video 929 B
/media/vimeo-video 888 B
/media/wistia-video 619 B
/media/youtube-video 897 B
Extensions (2)
Entry Initial
/extensions/google-cast 1.59 kB
/extensions/mux-data 1.64 kB
Players (5)
Entry Initial Lazy
/video/player 7.67 kB 58.19 kB
/audio/player 4.06 kB 58.19 kB
/background/player 2.27 kB 58.19 kB
/live-video/player 7.06 kB 58.19 kB
/live-audio/player 4.05 kB 58.19 kB
Skins (18)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.70 kB
/video/skin.css css 7.52 kB
/video/minimal-skin js 51.47 kB 58.19 kB
/video/skin js 51.28 kB 58.19 kB
/audio/minimal-skin.css css 5.48 kB
/audio/skin.css css 5.32 kB
/audio/minimal-skin js 39.78 kB 58.19 kB
/audio/skin js 39.74 kB 58.19 kB
/background/skin.css css 90 B
/background/skin js 277 B
/live-video/minimal-skin.css css 6.16 kB
/live-video/skin.css css 6.14 kB
/live-video/minimal-skin js 42.90 kB 58.19 kB
/live-video/skin js 42.92 kB 58.19 kB
/live-audio/minimal-skin.css css 4.31 kB
/live-audio/skin.css css 4.30 kB
/live-audio/minimal-skin js 30.55 kB 58.19 kB
/live-audio/skin js 30.58 kB 58.19 kB
UI Components (39)
Entry Initial Lazy
/ui/airplay-button 10.53 kB 58.19 kB
/ui/alert-dialog 4.73 kB 58.19 kB
/ui/audio-track 8.04 kB 58.19 kB
/ui/audio-track-radio-group 9.57 kB 58.19 kB
/ui/buffering-indicator 7.88 kB 58.19 kB
/ui/captions-button 10.51 kB 58.19 kB
/ui/captions-radio-group 9.67 kB 58.19 kB
/ui/cast-button 10.51 kB 58.19 kB
/ui/controls 7.80 kB 58.19 kB
/ui/dialog 4.75 kB 58.19 kB
/ui/error-dialog 11.88 kB 58.19 kB
/ui/fullscreen-button 10.49 kB 58.19 kB
/ui/gesture 8.07 kB 58.19 kB
/ui/hotkey 8.56 kB 58.19 kB
/ui/live-button 8.91 kB 58.19 kB
/ui/menu 19.54 kB 58.19 kB
/ui/mute-button 10.53 kB 58.19 kB
/ui/pip-button 10.48 kB 58.19 kB
/ui/play-button 10.45 kB 58.19 kB
/ui/playback-rate 8.07 kB 58.19 kB
/ui/playback-rate-button 10.45 kB 58.19 kB
/ui/playback-rate-radio-group 9.60 kB 58.19 kB
/ui/popover 8.16 kB 58.19 kB
/ui/poster 7.95 kB 58.19 kB
/ui/quality 8.50 kB 58.19 kB
/ui/quality-radio-group 10.04 kB 58.19 kB
/ui/seek-button 10.52 kB 58.19 kB
/ui/seek-indicator 12.06 kB 58.19 kB
/ui/slider 13.12 kB 58.19 kB
/ui/status-announcer 10.18 kB 58.19 kB
/ui/status-indicator 12.37 kB 58.19 kB
/ui/thumbnail 9.18 kB 58.19 kB
/ui/time 10.03 kB 58.19 kB
/ui/time-slider 14.40 kB 58.19 kB
/ui/title 7.35 kB 58.19 kB
/ui/tooltip 8.93 kB 58.19 kB
/ui/volume-indicator 12.42 kB 58.19 kB
/ui/volume-popover 14.29 kB 58.19 kB
/ui/volume-slider 12.25 kB 58.19 kB
🧩 @videojs/core — no changes
Entries (76)
Entry Initial Lazy
. 13.35 kB
/dom 22.82 kB 58.19 kB
/vjsc 1.87 kB
/i18n 3.23 kB 58.19 kB
/i18n/locales/all 35.98 kB
/i18n/locales/ar 1.24 kB
/i18n/locales/az 1.12 kB
/i18n/locales/bg 1.32 kB
/i18n/locales/bn 1.25 kB
/i18n/locales/bs 1.03 kB
/i18n/locales/ca 1.10 kB
/i18n/locales/cs 1.11 kB
/i18n/locales/cy 1.05 kB
/i18n/locales/da 1.01 kB
/i18n/locales/de 1.11 kB
/i18n/locales/el 1.53 kB
/i18n/locales/en 801 B
/i18n/locales/es 1.02 kB
/i18n/locales/et 1.07 kB
/i18n/locales/eu 1.04 kB
/i18n/locales/fa 1.25 kB
/i18n/locales/fi 1.07 kB
/i18n/locales/fr 1.09 kB
/i18n/locales/gd 1.13 kB
/i18n/locales/gl 1.02 kB
/i18n/locales/he 1.16 kB
/i18n/locales/hi 1.33 kB
/i18n/locales/hr 1.05 kB
/i18n/locales/hu 1.11 kB
/i18n/locales/id 941 B
/i18n/locales/it 1.04 kB
/i18n/locales/ja 1.22 kB
/i18n/locales/ko 1.18 kB
/i18n/locales/lt 1.04 kB
/i18n/locales/lv 1.11 kB
/i18n/locales/mr 1.32 kB
/i18n/locales/nb 1015 B
/i18n/locales/ne 1.32 kB
/i18n/locales/nl 1.02 kB
/i18n/locales/nn 1015 B
/i18n/locales/oc 1.10 kB
/i18n/locales/pl 1.17 kB
/i18n/locales/pt 1.05 kB
/i18n/locales/pt-BR 1.05 kB
/i18n/locales/pt-PT 1.04 kB
/i18n/locales/ro 1.10 kB
/i18n/locales/ru 1.42 kB
/i18n/locales/sk 1.13 kB
/i18n/locales/sl 1.07 kB
/i18n/locales/sr 1.13 kB
/i18n/locales/sv 1.03 kB
/i18n/locales/te 1.34 kB
/i18n/locales/th 1.31 kB
/i18n/locales/tr 1.09 kB
/i18n/locales/uk 1.44 kB
/i18n/locales/vi 1.13 kB
/i18n/locales/zh 1.04 kB
/i18n/locales/zh-CN 1.04 kB
/i18n/locales/zh-TW 1.04 kB
/i18n/text/airplay 101 B
/i18n/text/buttons 137 B
/i18n/text/captions 93 B
/i18n/text/cast 114 B
/i18n/text/common 90 B
/i18n/text/container 75 B
/i18n/text/errors 285 B
/i18n/text/fullscreen 98 B
/i18n/text/live 126 B
/i18n/text/menu 249 B
/i18n/text/pip 101 B
/i18n/text/playback 80 B
/i18n/text/seek 105 B
/i18n/text/slider 65 B
/i18n/text/status 222 B
/i18n/text/time 322 B
/i18n/text/volume 133 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Initial
. 1.05 kB
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Initial
. 1.72 kB
/html 703 B
/react 365 B
🔧 @videojs/utils — no changes
Entries (13)
Entry Initial
/array 271 B
/dom 5.11 kB
/events 320 B
/function 386 B
/i18n 249 B
/jwt 176 B
/object 535 B
/predicate 297 B
/percent 281 B
/string 239 B
/style 212 B
/time 976 B
/number 199 B
📦 @videojs/cdn — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
/package.json 810 B 811 B +1 B (+0.1%)
📦 @videojs/cloudflare-video — no changes
📦 @videojs/dash-video — no changes
📦 @videojs/google-cast — no changes
📦 @videojs/hlsjs-video — no changes
📦 @videojs/media — no changes
Entries (3)
Entry Initial
. 1.29 kB
/dom 4.12 kB
/media-tracks 2.05 kB
📦 @videojs/mux — no changes
📦 @videojs/mux-audio — no changes
Entries (2)
Entry Initial
. 135.53 kB
/spf 20.81 kB
📦 @videojs/mux-data — 1 small size change
Path Base initial PR initial Initial diff Lazy diff
. 24.31 kB 24.28 kB -24 B (-0.1%)
📦 @videojs/mux-video — no changes
Entries (2)
Entry Initial
. 135.67 kB
/spf 26.72 kB
📦 @videojs/native-hls-video — no changes
📦 @videojs/shaka-video — no changes
📦 @videojs/spf — no changes
Entries (7)
Entry Initial
. 4.55 kB
/dom 6.60 kB
/hls 22.15 kB
/media-tracks 504 B
/hls-background-video 15.84 kB
/hls-audio 19.84 kB
/hls-video 25.89 kB
📦 @videojs/spotify-audio — no changes
📦 @videojs/tiktok-video — no changes
📦 @videojs/twitch-video — no changes
📦 @videojs/vimeo-video — no changes
📦 @videojs/wistia-video — no changes
Entries (2)
Entry Initial
. 1.91 kB
/helpers 1.77 kB
📦 @videojs/youtube-video — no changes

ℹ️ How to interpret

Each entry is independently bundled, minified, and brotli-compressed. Initial size includes its static import graph; lazy dynamic chunks are reported separately.

Entries are not additive because their dependency graphs overlap. Preset rows represent realistic combined bundles.

Changes of 300 B or less across initial, lazy, and total size are collapsed, not discarded.

Run pnpm size locally to check current initial sizes.

Both component-library templates now match the default video skin's
control set in both approaches, gate every control on its availability
state, and share a shell-style toolbar with media, source, and captions
pickers so features like quality, audio tracks, chapters, thumbnails, and
captions can be exercised.

- New `@app/shared/react/library-skin-harness`: URL-backed media/source/
  captions selection, shell `SelectField`s, the lazily loaded React media
  component per media id with chapter/storyboard tracks and the sandbox's
  synthetic captions, and the default skin's hotkeys and gestures.
- render approach: poster, buffering indicator, error dialog, remaining-
  time toggle, captions/cast/AirPlay/PiP/fullscreen buttons, chapters and
  thumbnail preview in the slider (shown only while pointing), settings
  with quality/audio/speed/captions and hook-gated empty groups, status
  indicators. Radix toggles use function-form `render` for `pressed`.
- hooks approach: availability-gated PiP, remote playback, captions,
  fullscreen; audio group; buffered range; poster; spinner; library
  AlertDialog from the error feature; remaining-time toggle.
- Sandbox optimizer entries for the CommonJS engines now point at the
  packages that own them after the adapter rename, plus Base UI's
  alert-dialog entry.
Rework the Radix hooks approach to match the default video skin and to
use Radix end to end:

- Radix Icons throughout; nested DropdownMenu submenus per option hook
  with the current value in each trigger, gated by the hook's `hidden`.
- Seek slider shows hover time, chapter title, and storyboard thumbnail,
  derived from the text-track cues with the core's media-fragment parser
  and `ThumbnailCore`, and draws chapter boundaries as track gaps.
- Default-skin metrics: 44px pill bar inset 12px over a bottom gradient,
  36px round buttons with 18px icons, 13px tabular time, 4px track.
- No tooltip on mute since hover opens the volume popover; disabled
  buttons show `cursor: not-allowed`.
- Mark the bar and volume popover `data-interactive` so the container's
  tap gesture ignores clicks on the library slider; a React-level
  `stopPropagation` never reached the native container listener. Applied
  to the Base UI hooks flavor as well.
One icon per concept, shared between button and menu: speech bubble for
captions, overlapping frames for picture-in-picture, sliders for quality,
globe for audio language, stopwatch for speed. Labels, tooltips, and menu
headings now come from the default skin's i18n text tokens through
`useTranslator()` instead of hand-written strings.
Swap icons where Radix Icons has a pair (PiP exit uses ExitIcon) and
underline the active icon otherwise (captions showing, remote playback
connected), with the inactive icon at reduced opacity.
The popover content is portaled into the container, so leaving the
trigger's wrapper closed it before the pointer could reach the slider.
Hover state now covers both trigger and content, with a short close
delay that bridges the gap between them.
Radix AlertDialog is always page-modal, so the hooks flavor now uses a
non-modal Dialog with role="alertdialog" portaled into the container,
with its own backdrop and outside interactions ignored. Its copy comes
from the core error-dialog text helpers via translateText.

Both library-skin templates render inside SandboxI18nProvider, and the
shared harness toolbar gains a Language select that writes the locale
param and posts the shell's locale-change message.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he Radix seek slider

Draw the track as one segment per chapter with transparent gaps from the
core's normalizeChapterCues partition instead of painting black bars on
a single track, and grow the hovered segment. Stack the truncated chapter
title and centered time over the bottom of the thumbnail at the default
skin's offsets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A React how-to that drives Radix primitives with Video.js hooks: a
controlled Toggle from the volume feature, a Slider from the time and
buffer features, a DropdownMenu built from the option hooks, and a full
player at default-skin parity with tooltips, a hover volume popover,
chapters and storyboard previews, a player-scoped error dialog,
hotkeys, gestures, and translated labels. Availability and
constraints record the Radix behaviors that differ from Video.js's
own components.

Demos use scoped plain CSS. The site gains radix-ui, Radix Icons, and
a direct @videojs/core dependency for the text tokens and the
thumbnail and chapter helpers, which @videojs/react does not re-export.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Replace the hand-rolled time formatter with formatTime from
@videojs/utils/time, guided by the duration like the built-in Time
component, and fold the extra package installs into one step.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Use useTranslator with the core text tokens from the first step on,
including the seek and volume slider labels, and explain translation
alongside feature guards and availability in the first section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant