Discovery: component-library skins with Base UI and Radix - #2692
Draft
cjpillsbury wants to merge 12 commits into
Draft
Discovery: component-library skins with Base UI and Radix#2692cjpillsbury wants to merge 12 commits into
cjpillsbury wants to merge 12 commits into
Conversation
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).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for vjs-registry canceled.
|
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
@videojs/cdn
@videojs/core
@videojs/element
@videojs/html
@videojs/media
@videojs/react
@videojs/spf
@videojs/store
@videojs/utils
@videojs/cloudflare-video
@videojs/dash-video
@videojs/hlsjs-video
@videojs/mux-audio
@videojs/mux-video
@videojs/native-hls-video
@videojs/shaka-video
@videojs/spotify-audio
@videojs/tiktok-video
@videojs/twitch-video
@videojs/vimeo-video
@videojs/wistia-video
@videojs/youtube-video
@videojs/google-cast
@videojs/mux-data
commit: |
Contributor
📦 Bundle Size Report🎨 @videojs/html
Small changes (25, ≤ 300 B)
Presets (7)
Media (18)
Players (5)
Skins (29)
UI Components (62)
⚛️ @videojs/react — 14 small size changes
Presets (7)
Media (22)
Extensions (2)
Players (5)
Skins (18)
UI Components (39)
🧩 @videojs/core — no changesEntries (76)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (13)
📦 @videojs/cdn — 1 small size change
📦 @videojs/cloudflare-video — no changes📦 @videojs/dash-video — no changes📦 @videojs/google-cast — no changes📦 @videojs/hlsjs-video — no changes📦 @videojs/media — no changesEntries (3)
📦 @videojs/mux — no changes📦 @videojs/mux-audio — no changesEntries (2)
📦 @videojs/mux-data — 1 small size change
📦 @videojs/mux-video — no changesEntries (2)
📦 @videojs/native-hls-video — no changes📦 @videojs/shaka-video — no changes📦 @videojs/spf — no changesEntries (7)
📦 @videojs/spotify-audio — no changes📦 @videojs/tiktok-video — no changes📦 @videojs/twitch-video — no changes📦 @videojs/vimeo-video — no changes📦 @videojs/wistia-video — no changesEntries (2)
📦 @videojs/youtube-video — no changesℹ️ How to interpretEach 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 |
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
approachparam: Video.js components with library elements handed in throughrender, and library components fed byusePlayer(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).
renderChanges
/react-base-ui-skin/: Base UI (already a sandbox dependency) viarenderand via hooks/react-radix-skin/: Radix (radix-uiumbrella, new sandbox dependency) viarenderand via hooks, including the function form ofrenderdriving Radix Toggle's controlledpressedfrom Video.js statenoDiscoveryFindings so far
renderworks for buttons; compounds are whole swaps.renderkeep our translatedaria-label,data-*state, tooltip wiring, and hotkey hints.role="slider"onThumb, 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.use*Optionshooks;*RadioGroup.OptionsneedsMenu.Rootcontext.disabledis a core prop; onlyaria-disabledreaches the DOM.renderlets library props win on collisions and runs library handlers first;classNameconcatenates.Hooks approach costs.
container={useContainer()}to survive fullscreen.useHotkeyShortcut.Radix specifics. No Button primitive (Slot-based button instead);
asChildstacks collide ondata-state; DropdownMenu opens onpointerdown; 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
pnpm-lock.yamlshows re-resolution noise from pnpm normalizing peer variants alongside the two added dependenciesTesting
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