Skip to content

docs(design): draft composable media features design#1447

Open
luwes wants to merge 4 commits intomainfrom
design/composable-media-features
Open

docs(design): draft composable media features design#1447
luwes wants to merge 4 commits intomainfrom
design/composable-media-features

Conversation

@luwes
Copy link
Copy Markdown
Collaborator

@luwes luwes commented Apr 23, 2026

Summary

Design doc for bringing store-style composable features to the media
layer. Each media class binds an engine to a flat list of features —
mirroring createPlayer({ features }). Multi-engine playback (hls.js
with native-HLS fallback) falls out of the same factory function
called with multiple variants, with no separate router primitive.

Changes

  • createVideo / createAudio — variadic factories. One variant
    yields a single-engine class; two or more yields a class that picks
    at runtime based on src / type / preferPlayback. createAudio
    extends HTMLAudioElementHost and resolves the
    mux/index.ts:20-21 audio TODO.
  • Engines as functions. hlsJs(...) and nativeHls(...) take an
    options object (config, debug, features) and return a variant
    descriptor. The engine mixin closes over its config; supports()
    is part of the descriptor.
  • One import path per engine. Engine plumbing
    (hlsJs), engine-specific features (live, streamType, errors),
    and engine-agnostic features (googleCast, muxData re-exported from
    google-cast/, mux-data/) all live under
    @videojs/core/dom/media/hls. Same shape under …/native-hls.
    Tree-shake by import path.
  • Mixins stay underneath. Each feature is still a class mixin —
    #private fields, precise types, zero runtime overhead. Only the
    composition surface changes.
  • Migration sketch + open questions. Table maps today's static
    mixin chains onto createVideo calls. Open questions cover variant
    type surface (intersection vs union), MediaSupportContext shape,
    mixin ordering, and whether to add trailing-options sugar for
    cross-cutting features.
Scope & status
  • Filed as status: draft under internal/design/ — this lands the
    recommended shape after iterating through earlier options
    (HTML/React child components, defineMediaFeature primitive,
    delegateMixins map, separate createMediaRouter). Once the
    shape is stable, the public-API pieces graduate to an RFC.
  • Cross-references:
    packages/core/src/dom/store/features/presets.ts (the shape we're
    mirroring), packages/core/src/dom/media/hls/hlsjs.ts (today's
    static mixin chain), packages/core/src/dom/media/hls/index.ts
    (today's hand-coded delegate selection), and
    packages/core/src/dom/media/google-cast/index.ts (the heaviest
    engine-agnostic mixin).

Testing

Docs-only — no code paths touched.

Lay out options for making media features composable the same way store
features are (defineSlice / feature arrays). Captures nine approaches
from a `defineMediaFeature` primitive to global side-effect imports,
with worked examples for the live feature on HlsMedia, the `<hls-video>`
custom element, and the `<HlsVideo />` React component, plus a
comparison table and a recommended rollout.

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment May 5, 2026 7:33pm

Request Review

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 23, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 30fb671
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69fa4605750b9a0008e44d8f
😎 Deploy Preview https://deploy-preview-1447--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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

📦 Bundle Size Report

🎨 @videojs/html — no changes
Presets (7)
Entry Size
/video (default) 32.84 kB
/video (default + hls) 166.49 kB
/video (minimal) 32.42 kB
/video (minimal + hls) 166.03 kB
/audio (default) 27.34 kB
/audio (minimal) 24.45 kB
/background 4.15 kB
Media (8)
Entry Size
/media/background-video 1.04 kB
/media/container 1.72 kB
/media/dash-video 236.58 kB
/media/hls-video 134.87 kB
/media/mux-audio 160.89 kB
/media/mux-video 161.01 kB
/media/native-hls-video 4.62 kB
/media/simple-hls-video 15.99 kB
Players (5)
Entry Size
/video/player 7.00 kB
/audio/player 5.12 kB
/background/player 3.85 kB
/live-video/player 7.01 kB
/live-audio/player 5.13 kB
Skins (30)
Entry Type Size
/video/minimal-skin.css css 4.46 kB
/video/skin.css css 4.46 kB
/video/minimal-skin js 32.36 kB
/video/minimal-skin.tailwind js 32.83 kB
/video/skin js 32.79 kB
/video/skin.tailwind js 33.24 kB
/audio/minimal-skin.css css 2.69 kB
/audio/skin.css css 2.65 kB
/audio/minimal-skin js 24.44 kB
/audio/minimal-skin.tailwind js 24.61 kB
/audio/skin js 27.36 kB
/audio/skin.tailwind js 27.50 kB
/background/skin.css css 115 B
/background/skin js 1.15 kB
/live-video/minimal-skin.css css 4.46 kB
/live-video/skin.css css 4.46 kB
/live-video/minimal-skin js 32.17 kB
/live-video/minimal-skin.tailwind js 32.49 kB
/live-video/skin js 32.22 kB
/live-video/skin.tailwind js 32.48 kB
/live-audio/minimal-skin.css css 2.69 kB
/live-audio/skin.css css 2.65 kB
/live-audio/minimal-skin js 24.32 kB
/live-audio/minimal-skin.tailwind js 24.25 kB
/live-audio/skin js 26.84 kB
/live-audio/skin.tailwind js 26.87 kB
/global.css css 176 B
/shared.css css 88 B
/tailwind.css css 228 B
/skin-element js 1.37 kB
UI Components (33)
Entry Size
/ui/alert-dialog 632 B
/ui/alert-dialog-close 272 B
/ui/alert-dialog-description 237 B
/ui/alert-dialog-title 230 B
/ui/buffering-indicator 2.00 kB
/ui/captions-button 2.03 kB
/ui/cast-button 2.00 kB
/ui/compounds 3.63 kB
/ui/controls 1.91 kB
/ui/error-dialog 2.44 kB
/ui/fullscreen-button 1.97 kB
/ui/hotkey 2.66 kB
/ui/mute-button 2.00 kB
/ui/pip-button 2.01 kB
/ui/play-button 1.98 kB
/ui/playback-rate-button 2.10 kB
/ui/popover 1.49 kB
/ui/poster 1.79 kB
/ui/seek-button 1.98 kB
/ui/seek-indicator 2.62 kB
/ui/seek-indicator-value 259 B
/ui/slider 1.17 kB
/ui/status-announcer 2.42 kB
/ui/status-indicator 2.37 kB
/ui/status-indicator-value 147 B
/ui/thumbnail 2.54 kB
/ui/time 1.93 kB
/ui/time-slider 2.94 kB
/ui/tooltip 1.59 kB
/ui/volume-indicator 2.65 kB
/ui/volume-indicator-fill 247 B
/ui/volume-indicator-value 250 B
/ui/volume-slider 3.53 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react — no changes
Presets (7)
Entry Size
/video (default) 26.36 kB
/video (default + hls) 158.72 kB
/video (minimal) 26.35 kB
/video (minimal + hls) 158.74 kB
/audio (default) 19.19 kB
/audio (minimal) 17.65 kB
/background 756 B
Media (7)
Entry Size
/media/background-video 575 B
/media/dash-video 235.21 kB
/media/hls-video 133.39 kB
/media/mux-audio 159.70 kB
/media/mux-video 159.68 kB
/media/native-hls-video 3.13 kB
/media/simple-hls-video 14.56 kB
Skins (27)
Entry Type Size
/tailwind.css css 228 B
/video/minimal-skin.css css 4.38 kB
/video/skin.css css 4.37 kB
/video/minimal-skin js 26.24 kB
/video/minimal-skin.tailwind js 30.88 kB
/video/skin js 26.28 kB
/video/skin.tailwind js 30.80 kB
/audio/minimal-skin.css css 2.56 kB
/audio/skin.css css 2.51 kB
/audio/minimal-skin js 17.58 kB
/audio/minimal-skin.tailwind js 20.20 kB
/audio/skin js 19.11 kB
/audio/skin.tailwind js 20.24 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 4.38 kB
/live-video/skin.css css 4.37 kB
/live-video/minimal-skin js 22.91 kB
/live-video/minimal-skin.tailwind js 27.39 kB
/live-video/skin js 22.98 kB
/live-video/skin.tailwind js 27.43 kB
/live-audio/minimal-skin.css css 2.56 kB
/live-audio/skin.css css 2.51 kB
/live-audio/minimal-skin js 16.23 kB
/live-audio/minimal-skin.tailwind js 18.71 kB
/live-audio/skin js 17.83 kB
/live-audio/skin.tailwind js 18.81 kB
UI Components (26)
Entry Size
/ui/alert-dialog 1.16 kB
/ui/buffering-indicator 1.83 kB
/ui/captions-button 2.08 kB
/ui/cast-button 2.02 kB
/ui/controls 1.87 kB
/ui/error-dialog 2.38 kB
/ui/fullscreen-button 2.00 kB
/ui/gesture 1.26 kB
/ui/hotkey 1.93 kB
/ui/live-button 2.06 kB
/ui/mute-button 2.06 kB
/ui/pip-button 1.97 kB
/ui/play-button 2.02 kB
/ui/playback-rate-button 2.02 kB
/ui/popover 1.88 kB
/ui/poster 1.69 kB
/ui/seek-button 2.04 kB
/ui/seek-indicator 1.98 kB
/ui/slider 3.34 kB
/ui/status-indicator 1.98 kB
/ui/thumbnail 2.06 kB
/ui/time 2.56 kB
/ui/time-slider 3.06 kB
/ui/tooltip 2.38 kB
/ui/volume-indicator 1.99 kB
/ui/volume-slider 2.39 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (9)
Entry Size
. 7.02 kB
/dom 12.31 kB
/dom/media/custom-media-element 1.90 kB
/dom/media/dash 234.36 kB
/dom/media/google-cast 4.07 kB
/dom/media/hls 132.98 kB
/dom/media/mux 159.10 kB
/dom/media/native-hls 2.52 kB
/dom/media/simple-hls 13.93 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 695 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.92 kB
/events 319 B
/function 327 B
/object 275 B
/predicate 265 B
/string 192 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 4.29 kB
/dom 7.48 kB
/hls 13.32 kB

ℹ️ How to interpret

All sizes are standalone totals (minified + brotli).

Icon Meaning
No change
🔺 Increased ≤ 10%
🔴 Increased > 10%
🔽 Decreased
🆕 New (no baseline)

Run pnpm size locally to check current sizes.

…nctions

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant