Skip to content

refactor(skins): consolidate design system tokens and utilities - #2581

Merged
mihar-22 merged 50 commits into
fix/generated-skin-parityfrom
refactor/skins-design-system
Sep 3, 2026
Merged

refactor(skins): consolidate design system tokens and utilities#2581
mihar-22 merged 50 commits into
fix/generated-skin-parityfrom
refactor/skins-design-system

Conversation

@mihar-22

@mihar-22 mihar-22 commented Sep 2, 2026

Copy link
Copy Markdown
Member

Stacked on #2580.

Summary

A review pass over the skins design system after the generated-skin parity work. Every value that varies per theme or preference now flows through a --media-* token with a matching Tailwind theme key, repeated selector patterns became shared utilities and variants, and the parts of the pipeline that kept those from reaching consumers were fixed along the way.

Changes

  • Tokens. Motion, shadow, backdrop filter, and geometry values are declared once in the theme files, aliased as Tailwind keys such as duration-media-fast, scale-media-hidden, and rounded-media-controls, and named after those keys. Reduced motion, reduced transparency, and forced colors override tokens in a base.preferences sublayer instead of per-rule variants. That also fixes scrims, dialogs, menu blur, slider previews, and the seek indicator, which still animated or blurred under those preferences.
  • Utilities and variants. surface-media, focus-ring-media, mask-media-volume, clip-media-*, transition-media-popup, and friends replace repeated recipes. media-compact, media-wide, media-highlighted, media-transitioning, and media-anchored replace raw container, hover, starting-style, and @supports prefixes. Menu items gate hover highlighting on hover-capable pointers.
  • Registry. The shared Tailwind source exports as theme keys, functional utilities, and custom variants. The html registry shares one theme stylesheet across skins, the theme item ships generated docs for public tokens, keys, utilities, and variants, and style assets emit in dependency and composition order.
  • Dev tooling. The vjsc plugin writes a Tailwind candidate manifest into the Vite cache, aliases it as vjsc:candidates, keeps it watched so the dev CSS recompiles as modules record, and carries candidates across restarts. The skins playground fully reloads when workspace dist rebuilds reach its entry instead of hot swapping into a broken player context. The sandbox scans its generated Tailwind skins.
  • Fixes surfaced by the review. The live skins' captions menu trigger has its tooltip back, the popup entry distance scales with the player, the Minimal caption offset applies from the video element, and the dead Tailwind entry, color alias, and dependencies are gone.
  • Docs. The skins README walks the package structure, style layers, Tailwind entries, build outputs, and commands. The component skill records the shared utility and token rules.

Testing

  • pnpm -F @videojs/skins test: unit, build, and registry contracts, including a test that compiles every shipped Tailwind class against the exported registry theme and one that every aliased internal token is declared.
  • pnpm -F vjsc test, pnpm typecheck, pnpm check:workspace, and vp run validate:shadcn for the three registries.
  • Manual in the playground and sandbox: Tailwind and CSS modes across skins, the candidate manifest recompile on a cold start, the captions tooltip in the live skins, the sandbox Tailwind pages, and a single full reload when dist files rebuild.

Note

Medium Risk
Broad visual and build-output changes across all skins and Shadcn registry consumers; mitigated by contract tests and token-driven preferences, but regressions in theming or registry install order remain possible.

Overview
This PR recenters skins styling on a shared token and Tailwind design system, then rewires builds, registries, and skin modules to use it instead of duplicated literals and ad hoc breakpoints.

Tokens and CSS layering move motion, shadows, radii, and hidden-state values onto --media-* variables with @theme inline aliases (duration-media-*, scale-media-hidden, etc.). Preference handling shifts into a base.preferences layer so reduced motion, transparency, and forced colors win without sprinkling motion-reduce / opaque on every rule. Preset-specific bases split into base.video.css and base.audio.css, and bundling picks the right entry via skinBaseStylesheet.

Shared Tailwind recipes land in tailwind.css as flat @utility and media-* variants (surface-media, media-compact / media-wide, media-transitioning, clip-media-*, …). Style modules and per-skin controls are trimmed to deltas; common chrome moves into layout modules (controls.styles, audio-controls.styles, time.styles, indicator.styles).

Registry and packages ship one _style-theme item (documented from vars.ts / utilities.ts); HTML skins import theme CSS before scoped skin.css and no longer embed full base bundles. A new registry test asserts exported theme utilities/variants match the source and compile every Tailwind class in shipped TSX.

Dev and docs: playground gains CSS vs Tailwind compare, RTL, richer copy report, and tailwind.dev.css + vjsc candidate manifest scanning; sandbox adds a generated-components @source. README and the create-vjsc-component skill document the pipeline. Smaller fixes include captions menu ButtonTooltip, live-audio controls deduplication, and token-driven play-button hidden icons.

Reviewed by Cursor Bugbot for commit 159089a. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Sep 2, 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 3, 2026 6:57pm UTC

Request Review

@netlify

netlify Bot commented Sep 2, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 159089a
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a99c2e7af29e100083a9f07
😎 Deploy Preview https://deploy-preview-2581--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 2, 2026

Copy link
Copy Markdown
@videojs/core

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

@videojs/element

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

@videojs/html

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

@videojs/media

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

@videojs/react

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

@videojs/spf

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

@videojs/store

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

@videojs/utils

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

commit: 7f26a6f

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html — 30 small size changes
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin.css 7.45 kB 7.63 kB +187 B (+2.5%)
/video/skin.css 7.28 kB 7.41 kB +135 B (+1.8%)
/video/minimal-skin 11.15 kB 11.29 kB +150 B (+1.3%) 0 B
/video/minimal-skin2 7.95 kB 8.05 kB +101 B (+1.2%)
/video/skin 11.04 kB 11.07 kB +33 B (+0.3%) 0 B
/video/skin2 7.87 kB 7.90 kB +31 B (+0.4%)
/audio/minimal-skin.css 5.30 kB 5.51 kB +217 B (+4.0%)
/audio/skin.css 5.08 kB 5.32 kB +250 B (+4.8%)
/audio/minimal-skin 8.06 kB 8.24 kB +184 B (+2.2%) 0 B
/audio/minimal-skin2 5.66 kB 5.82 kB +167 B (+2.9%)
/audio/skin 7.83 kB 8.05 kB +225 B (+2.8%) 0 B
/audio/skin2 5.43 kB 5.64 kB +217 B (+3.9%)
/live-video/minimal-skin.css 6.00 kB 6.16 kB +173 B (+2.8%)
/live-video/skin.css 6.02 kB 6.16 kB +147 B (+2.4%)
/live-video/minimal-skin 8.95 kB 9.09 kB +142 B (+1.5%) 0 B
/live-video/minimal-skin2 6.41 kB 6.52 kB +112 B (+1.7%)
/live-video/skin 9.08 kB 9.12 kB +44 B (+0.5%) 0 B
/live-video/skin2 6.54 kB 6.55 kB +14 B (+0.2%)
/live-audio/minimal-skin.css 4.13 kB 4.38 kB +250 B (+5.9%)
/live-audio/skin.css 4.08 kB 4.35 kB +283 B (+6.8%)
/live-audio/minimal-skin 6.42 kB 6.62 kB +209 B (+3.2%) 0 B
/live-audio/minimal-skin2 4.40 kB 4.63 kB +231 B (+5.1%)
/live-audio/skin 6.38 kB 6.60 kB +225 B (+3.4%) 0 B
/live-audio/skin2 4.36 kB 4.61 kB +258 B (+5.8%)
/video (default) 19.94 kB 19.98 kB +39 B (+0.2%) 0 B
/video (default + hls) 159.71 kB 159.67 kB -45 B (-0.0%) 0 B
/video (minimal) 20.06 kB 20.23 kB +171 B (+0.8%) 0 B
/video (minimal + hls) 159.81 kB 159.95 kB +144 B (+0.1%) 0 B
/audio (default) 13.16 kB 13.39 kB +232 B (+1.7%) 0 B
/audio (minimal) 13.40 kB 13.60 kB +200 B (+1.5%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 19.98 kB 56.87 kB
/video (default + hls) 159.67 kB 56.87 kB
/video (minimal) 20.23 kB 56.87 kB
/video (minimal + hls) 159.95 kB 56.87 kB
/audio (default) 13.39 kB 56.87 kB
/audio (minimal) 13.60 kB 56.87 kB
/background 11.57 kB 56.87 kB
Media (18)
Entry Initial
/media/mux-video 142.40 kB
/media/mux-audio 142.23 kB
/media/background-video 1.16 kB
/media/cloudflare-video 6.28 kB
/media/dash-video 211.61 kB
/media/hls-audio 22.09 kB
/media/hls-background-video 16.80 kB
/media/hls-video 28.04 kB
/media/hlsjs-video 141.22 kB
/media/mux-background-video 16.83 kB
/media/native-hls-video 7.57 kB
/media/shaka-video 199.18 kB
/media/spotify-audio 6.27 kB
/media/tiktok-video 6.33 kB
/media/twitch-video 6.22 kB
/media/vimeo-video 13.05 kB
/media/wistia-video 60.23 kB
/media/youtube-video 6.81 kB
Extensions (2)
Entry Initial
/extensions/google-cast 6.52 kB
/extensions/mux-data 26.94 kB
Players (5)
Entry Initial Lazy
/video/player 9.70 kB 56.87 kB
/audio/player 6.14 kB 56.87 kB
/background/player 4.46 kB 56.87 kB
/live-video/player 9.06 kB 56.87 kB
/live-audio/player 6.15 kB 56.87 kB
Skins (29)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.63 kB
/video/skin.css css 7.41 kB
/video/minimal-skin js 11.29 kB 56.87 kB
/video/minimal-skin2 js 8.05 kB
/video/skin js 11.07 kB 56.87 kB
/video/skin2 js 7.90 kB
/audio/minimal-skin.css css 5.51 kB
/audio/skin.css css 5.32 kB
/audio/minimal-skin js 8.24 kB 56.87 kB
/audio/minimal-skin2 js 5.82 kB
/audio/skin js 8.05 kB 56.87 kB
/audio/skin2 js 5.64 kB
/background/skin.css css 139 B
/background/skin js 9.81 kB 56.87 kB
/background/skin2 js 187 B
/live-video/minimal-skin.css css 6.16 kB
/live-video/skin.css css 6.16 kB
/live-video/minimal-skin js 9.09 kB 56.87 kB
/live-video/minimal-skin2 js 6.52 kB
/live-video/skin js 9.12 kB 56.87 kB
/live-video/skin2 js 6.55 kB
/live-audio/minimal-skin.css css 4.38 kB
/live-audio/skin.css css 4.35 kB
/live-audio/minimal-skin js 6.62 kB 56.87 kB
/live-audio/minimal-skin2 js 4.63 kB
/live-audio/skin js 6.60 kB 56.87 kB
/live-audio/skin2 js 4.61 kB
/global.css css 190 B
/shared.css css 118 B
UI Components (62)
Entry Initial Lazy
/ui/airplay-button 11.26 kB 56.87 kB
/ui/alert-dialog 8.79 kB 56.87 kB
/ui/audio-track-radio-group 11.38 kB 56.87 kB
/ui/buffering-indicator 8.61 kB 56.87 kB
/ui/captions-button 11.51 kB 56.87 kB
/ui/captions-radio-group 11.46 kB 56.87 kB
/ui/cast-button 11.25 kB 56.87 kB
/ui/container 9.47 kB 56.87 kB
/ui/controls 8.77 kB 56.87 kB
/ui/controls-backdrop 1.69 kB 56.87 kB
/ui/controls-content 1.67 kB 56.87 kB
/ui/dialog 8.73 kB 56.87 kB
/ui/dialog-backdrop 1.70 kB 56.87 kB
/ui/dialog-close 2.06 kB 56.87 kB
/ui/dialog-description 1.69 kB 56.87 kB
/ui/dialog-popup 1.90 kB 56.87 kB
/ui/dialog-title 1.70 kB 56.87 kB
/ui/error-dialog 12.38 kB 56.87 kB
/ui/fullscreen-button 11.27 kB 56.87 kB
/ui/gesture 9.56 kB 56.87 kB
/ui/hotkey 9.97 kB 56.87 kB
/ui/live-button 9.74 kB 56.87 kB
/ui/menu 18.45 kB 56.87 kB
/ui/menu-checkbox-item 1.97 kB 56.87 kB
/ui/menu-content 7.52 kB 56.87 kB
/ui/menu-group 2.09 kB 56.87 kB
/ui/menu-group-label 1.64 kB
/ui/menu-item 2.29 kB 56.87 kB
/ui/menu-item-indicator 1.37 kB 56.87 kB
/ui/menu-radio-group 3.01 kB 56.87 kB
/ui/menu-radio-item 2.13 kB 56.87 kB
/ui/menu-separator 1.30 kB 56.87 kB
/ui/mute-button 11.28 kB 56.87 kB
/ui/pip-button 11.22 kB 56.87 kB
/ui/play-button 11.23 kB 56.87 kB
/ui/playback-rate-button 11.32 kB 56.87 kB
/ui/playback-rate-radio-group 11.37 kB 56.87 kB
/ui/popover 8.57 kB 56.87 kB
/ui/poster 8.67 kB 56.87 kB
/ui/quality-radio-group 11.97 kB 56.87 kB
/ui/seek-button 11.27 kB 56.87 kB
/ui/seek-indicator 12.69 kB 56.87 kB
/ui/seek-indicator-value 1.10 kB
/ui/slider 11.65 kB 56.87 kB
/ui/slider-preview 2.01 kB 56.87 kB
/ui/status-announcer 10.59 kB 56.87 kB
/ui/status-indicator 13.02 kB 56.87 kB
/ui/status-indicator-value 1.10 kB
/ui/thumbnail 9.84 kB 56.87 kB
/ui/time 10.80 kB 56.87 kB
/ui/time-slider 13.02 kB 56.87 kB
/ui/time-slider-chapter-title 8.79 kB 56.87 kB
/ui/time-slider-chapters 9.64 kB 56.87 kB
/ui/title 8.17 kB 56.87 kB
/ui/tooltip 9.95 kB 56.87 kB
/ui/tooltip-label 1.21 kB
/ui/tooltip-shortcut 1.18 kB
/ui/volume-indicator 13.01 kB 56.87 kB
/ui/volume-indicator-fill 1.11 kB
/ui/volume-indicator-value 1.10 kB
/ui/volume-popover 14.74 kB 56.87 kB
/ui/volume-slider 12.31 kB 56.87 kB
⚛️ @videojs/react — 21 small size changes
Path Base initial PR initial Initial diff Lazy diff
/video/minimal-skin.css 7.30 kB 7.48 kB +192 B (+2.6%)
/video/skin.css 7.14 kB 7.28 kB +137 B (+1.9%)
/video/minimal-skin 51.25 kB 51.31 kB +61 B (+0.1%) 0 B
/video/skin 50.53 kB 50.55 kB +18 B (+0.0%) 0 B
/audio/minimal-skin.css 5.20 kB 5.36 kB +165 B (+3.1%)
/audio/skin.css 4.98 kB 5.16 kB +185 B (+3.6%)
/audio/minimal-skin 39.59 kB 39.53 kB -56 B (-0.1%) 0 B
/audio/skin 39.49 kB 39.50 kB +14 B (+0.0%) 0 B
/live-video/minimal-skin.css 5.85 kB 6.04 kB +195 B (+3.3%)
/live-video/skin.css 5.88 kB 6.01 kB +136 B (+2.3%)
/live-video/minimal-skin 43.13 kB 43.06 kB -73 B (-0.2%) 0 B
/live-video/skin 42.50 kB 42.46 kB -42 B (-0.1%) 0 B
/live-audio/minimal-skin.css 4.03 kB 4.22 kB +199 B (+4.8%)
/live-audio/skin.css 3.96 kB 4.20 kB +246 B (+6.1%)
/live-audio/skin 30.38 kB 30.38 kB +7 B (+0.0%) 0 B
/video (default) 50.58 kB 50.55 kB -23 B (-0.0%) 0 B
/video (default + hls) 188.34 kB 188.43 kB +86 B (+0.0%) 0 B
/video (minimal) 51.36 kB 51.38 kB +24 B (+0.0%) 0 B
/video (minimal + hls) 189.19 kB 189.31 kB +120 B (+0.1%) 0 B
/audio (default) 39.59 kB 39.59 kB -1 B (-0.0%) 0 B
/audio (minimal) 39.62 kB 39.63 kB +18 B (+0.0%) 0 B
Presets (7)
Entry Initial Lazy
/video (default) 50.55 kB 56.87 kB
/video (default + hls) 188.43 kB 56.87 kB
/video (minimal) 51.38 kB 56.87 kB
/video (minimal + hls) 189.31 kB 56.87 kB
/audio (default) 39.59 kB 56.87 kB
/audio (minimal) 39.63 kB 56.87 kB
/background 581 B 56.87 kB
Media (22)
Entry Initial
/media/mux-video/hls-js 140.61 kB
/media/mux-video/spf 27.39 kB
/media/mux-audio/hls-js 140.55 kB
/media/mux-audio/spf 21.32 kB
/media/background-video 402 B
/media/cloudflare-video 4.34 kB
/media/dash-video 210.29 kB
/media/hls-audio 20.33 kB
/media/hls-background-video 16.46 kB
/media/hls-video 26.34 kB
/media/hlsjs-video 139.44 kB
/media/mux-audio 140.56 kB
/media/mux-background-video 16.45 kB
/media/mux-video 140.54 kB
/media/native-hls-video 5.76 kB
/media/shaka-video 197.52 kB
/media/spotify-audio 4.22 kB
/media/tiktok-video 4.18 kB
/media/twitch-video 4.30 kB
/media/vimeo-video 11.19 kB
/media/wistia-video 60.05 kB
/media/youtube-video 4.86 kB
Extensions (2)
Entry Initial
/extensions/google-cast 5.34 kB
/extensions/mux-data 25.70 kB
Players (5)
Entry Initial Lazy
/video/player 7.56 kB 56.87 kB
/audio/player 4.00 kB 56.87 kB
/background/player 2.27 kB 56.87 kB
/live-video/player 6.96 kB 56.87 kB
/live-audio/player 3.99 kB 56.87 kB
Skins (18)
Entry Type Initial Lazy
/video/minimal-skin.css css 7.48 kB
/video/skin.css css 7.28 kB
/video/minimal-skin js 51.31 kB 56.87 kB
/video/skin js 50.55 kB 56.87 kB
/audio/minimal-skin.css css 5.36 kB
/audio/skin.css css 5.16 kB
/audio/minimal-skin js 39.53 kB 56.87 kB
/audio/skin js 39.50 kB 56.87 kB
/background/skin.css css 90 B
/background/skin js 277 B
/live-video/minimal-skin.css css 6.04 kB
/live-video/skin.css css 6.01 kB
/live-video/minimal-skin js 43.06 kB 56.87 kB
/live-video/skin js 42.46 kB 56.87 kB
/live-audio/minimal-skin.css css 4.22 kB
/live-audio/skin.css css 4.20 kB
/live-audio/minimal-skin js 30.38 kB 56.87 kB
/live-audio/skin js 30.38 kB 56.87 kB
UI Components (39)
Entry Initial Lazy
/ui/airplay-button 10.45 kB 56.87 kB
/ui/alert-dialog 4.73 kB 56.87 kB
/ui/audio-track 7.93 kB 56.87 kB
/ui/audio-track-radio-group 9.48 kB 56.87 kB
/ui/buffering-indicator 7.80 kB 56.87 kB
/ui/captions-button 10.44 kB 56.87 kB
/ui/captions-radio-group 9.59 kB 56.87 kB
/ui/cast-button 10.44 kB 56.87 kB
/ui/controls 7.71 kB 56.87 kB
/ui/dialog 4.75 kB 56.87 kB
/ui/error-dialog 11.80 kB 56.87 kB
/ui/fullscreen-button 10.40 kB 56.87 kB
/ui/gesture 7.99 kB 56.87 kB
/ui/hotkey 8.38 kB 56.87 kB
/ui/live-button 8.80 kB 56.87 kB
/ui/menu 19.45 kB 56.87 kB
/ui/mute-button 10.44 kB 56.87 kB
/ui/pip-button 10.39 kB 56.87 kB
/ui/play-button 10.37 kB 56.87 kB
/ui/playback-rate 7.99 kB 56.87 kB
/ui/playback-rate-button 10.37 kB 56.87 kB
/ui/playback-rate-radio-group 9.53 kB 56.87 kB
/ui/popover 8.16 kB 56.87 kB
/ui/poster 7.57 kB 56.87 kB
/ui/quality 8.42 kB 56.87 kB
/ui/quality-radio-group 9.95 kB 56.87 kB
/ui/seek-button 10.41 kB 56.87 kB
/ui/seek-indicator 11.97 kB 56.87 kB
/ui/slider 12.76 kB 56.87 kB
/ui/status-announcer 10.10 kB 56.87 kB
/ui/status-indicator 12.29 kB 56.87 kB
/ui/thumbnail 8.73 kB 56.87 kB
/ui/time 9.77 kB 56.87 kB
/ui/time-slider 14.17 kB 56.87 kB
/ui/title 7.28 kB 56.87 kB
/ui/tooltip 8.93 kB 56.87 kB
/ui/volume-indicator 12.33 kB 56.87 kB
/ui/volume-popover 14.20 kB 56.87 kB
/ui/volume-slider 12.14 kB 56.87 kB
🧩 @videojs/core — no changes
Entries (76)
Entry Initial Lazy
. 13.15 kB
/dom 22.53 kB 56.87 kB
/vjsc 1.82 kB
/i18n 3.23 kB 56.87 kB
/i18n/locales/all 34.97 kB
/i18n/locales/ar 1.22 kB
/i18n/locales/az 1.09 kB
/i18n/locales/bg 1.29 kB
/i18n/locales/bn 1.23 kB
/i18n/locales/bs 1.00 kB
/i18n/locales/ca 1.07 kB
/i18n/locales/cs 1.07 kB
/i18n/locales/cy 1.02 kB
/i18n/locales/da 1006 B
/i18n/locales/de 1.09 kB
/i18n/locales/el 1.49 kB
/i18n/locales/en 785 B
/i18n/locales/es 1018 B
/i18n/locales/et 1.05 kB
/i18n/locales/eu 1.03 kB
/i18n/locales/fa 1.22 kB
/i18n/locales/fi 1.04 kB
/i18n/locales/fr 1.07 kB
/i18n/locales/gd 1.10 kB
/i18n/locales/gl 1022 B
/i18n/locales/he 1.14 kB
/i18n/locales/hi 1.30 kB
/i18n/locales/hr 1.02 kB
/i18n/locales/hu 1.10 kB
/i18n/locales/id 919 B
/i18n/locales/it 1.01 kB
/i18n/locales/ja 1.18 kB
/i18n/locales/ko 1.14 kB
/i18n/locales/lt 1.02 kB
/i18n/locales/lv 1.09 kB
/i18n/locales/mr 1.30 kB
/i18n/locales/nb 987 B
/i18n/locales/ne 1.29 kB
/i18n/locales/nl 1.00 kB
/i18n/locales/nn 987 B
/i18n/locales/oc 1.08 kB
/i18n/locales/pl 1.14 kB
/i18n/locales/pt 1.03 kB
/i18n/locales/pt-BR 1.03 kB
/i18n/locales/pt-PT 1.01 kB
/i18n/locales/ro 1.07 kB
/i18n/locales/ru 1.40 kB
/i18n/locales/sk 1.10 kB
/i18n/locales/sl 1.04 kB
/i18n/locales/sr 1.07 kB
/i18n/locales/sv 1.01 kB
/i18n/locales/te 1.32 kB
/i18n/locales/th 1.29 kB
/i18n/locales/tr 1.08 kB
/i18n/locales/uk 1.41 kB
/i18n/locales/vi 1.11 kB
/i18n/locales/zh 1.02 kB
/i18n/locales/zh-CN 1.02 kB
/i18n/locales/zh-TW 1.02 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 280 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.02 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/media

Path Base initial PR initial Initial diff Lazy diff
/dom/dash 209.83 kB 3.28 kB -206.54 kB (-98.4%)
/dom/wistia 60.09 kB 1.89 kB -58.20 kB (-96.9%)
Entries (23)
Entry Initial
. 1.07 kB
/dom 121 B
/dom/dash 3.28 kB
/dom/wistia 1.89 kB
/dom/audio-host 1.10 kB
/dom/cloudflare 3.69 kB
/dom/custom-media-element 2.13 kB
/dom/dash 209.83 kB
/dom/google-cast 4.06 kB
/dom/hls-js 139.16 kB
/dom/media-host 1.20 kB
/dom/media-played-ranges 576 B
/dom/mux 162.88 kB
/dom/native-hls 5.25 kB
/dom/shaka 197.02 kB
/dom/spotify 3.58 kB
/dom/tiktok 3.57 kB
/dom/twitch 3.62 kB
/dom/video-host 1.39 kB
/dom/vimeo 10.62 kB
/dom/wistia 60.09 kB
/dom/youtube 4.25 kB
/media-tracks 2.05 kB
📦 @videojs/spf — no changes
Entries (10)
Entry Initial
. 4.55 kB
/dom 6.60 kB
/hls 22.06 kB
/media-tracks 504 B
/hls-background-video 15.89 kB
/hls-audio 19.82 kB
/hls-video 25.85 kB
/mux-audio 20.76 kB
/mux-background-video 15.89 kB
/mux-video 26.67 kB

ℹ️ 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.

@mihar-22 mihar-22 changed the title refactor(skins): consolidate the design system tokens and shared utilities refactor(skins): consolidate design system tokens and utilities Sep 2, 2026

@sampotts sampotts left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick thought on this - I've not done a super thorough review...

I’m concerned we may be sharing too much styling between the skins. While this works well for the two existing skins, it could make future skins harder to build, particularly if they need to break the mould, as we’d have to untangle those shared assumptions.

My preference would be to keep each skin largely self-contained, sharing only clear base or reset styles. For example, something like shadow-media-thumb introduces a specific visual choice into the shared layer. If a future skin uses a different thumb shadow, that abstraction starts to break down.

I can see why sharing these styles makes sense given the similarities between the current skins, but I worry it could constrain us in unexpected ways later.

'before:absolute before:inset-0 before:bg-black/35 before:backdrop-blur-sm',
'not-data-visible:[--media-spinner-animation:none] data-visible:grid motion-reduce:[--media-spinner-animation:none]',
'pointer-events-none absolute inset-0 hidden place-content-center text-media-controls-foreground',
'before:absolute before:inset-0 before:bg-media-scrim/35 before:backdrop-filter-media-scrim',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should probably be consistent and stick with "backdrop" rather than "scrim"

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7f26a6f. Configure here.

Comment thread packages/skins/src/styles/buttons/live-button.styles.ts
@mihar-22
mihar-22 force-pushed the refactor/skins-design-system branch from 7f26a6f to e899b71 Compare September 3, 2026 18:52
@mihar-22
mihar-22 force-pushed the refactor/skins-design-system branch from e899b71 to ac5819f Compare September 3, 2026 18:53
@mihar-22
mihar-22 force-pushed the refactor/skins-design-system branch from ac5819f to 3ab2572 Compare September 3, 2026 18:53
@mihar-22
mihar-22 force-pushed the refactor/skins-design-system branch from 3ab2572 to 6383208 Compare September 3, 2026 18:54
Move reduced-transparency, high-contrast, and forced-colors handling
into theme tokens layered as base.theme and base.preferences so
preference overrides win without per-rule variants. Add scrim, frame
border, gradient, surface, popup radius, duration, breakpoint, icon,
and slider offset tokens, and route focus rings through --media-ring.

Replace copied surface, focus ring, time toggle, indicator, and
controls recipes with flat surface-media, surface-media-inset, and
focus-ring-media utilities plus shared time, indicator, and controls
style modules. Remove dead ring utilities and redundant overrides.

Live audio controls now share the audio controls stylesheet, so the
Vite workflow test declares the expected stylesheet per skin.
Replace the repeated mask and limit-nudge arbitrary properties with flat
mask-media-volume, mask-media-volume-open, and nudge-media utilities so
the recipes stay static for Tailwind source scanning while remaining
shareable across skins.
…gistry

Read Tailwind-only at-rules from the theme source text instead of
Lightning CSS, which cannot parse Tailwind's --value() syntax. The
registry theme item now carries @custom-variant blocks, which the
opaque variant in shipped Tailwind sources depended on, and accepts
functional @Utility names for the Shadcn css field.
Emit the theme item for the html registry too and make every html skin
depend on it, importing it before the skin's own scoped rules. Skin
stylesheets no longer repeat the tokens, resets, and preset layers, so
installing several skins ships them once.
Add a catalog describing every shared utility, variant, and computed
theme key, with a test that keeps it complete against the Tailwind
source, and generate the registry theme item's docs from that catalog
and the token descriptions. Group the theme tokens and comment the
shared Tailwind source for contributors, since compiled output drops
comments.
Expose durations, delays, hidden-state scale, blur, and offsets, thumb
and tooltip shadows, and the menu item and player radii as Tailwind
theme keys so rules use named utilities such as duration-media-fast and
scale-media-hidden instead of the variable shorthand. Named utilities
fail to compile when a token is renamed and appear in the registry theme
and its docs. The shorthand remains for runtime values such as slider
positions.
Duration, delay, hidden-state, shadow, text-shadow, backdrop-filter, and menu item radius tokens now
read the same as the theme keys that alias them, for example --media-duration-menu behind
duration-media-menu and --media-hidden-indicator-scale behind scale-media-hidden-indicator. Property
families lead with the property, hidden-state values lead with hidden, and geometry stays subject first.
The default popup translate distance read the scale unit directly, so it ignored --media-scale in
fullscreen while the Minimal override already used the scaled spacing unit. Both now derive from
--media-spacing and resolve to the same 8px at scale one.
…gh tokens

Dialog backdrops, the buffering and playback status scrims, and the audio error dialog blurred with
literal utilities that reduced transparency never reached. They now read --media-backdrop-filter-dialog
and --media-backdrop-filter-scrim through backdrop-filter-media-*, which preferences switch off with
the surface filter.

Menu submenu blur, the audio error dialog scale and blur, the Minimal indicator duration, the slider
preview hidden state, and the seek indicator entry offset used literals that reduced motion did not
collapse. They now use the hidden-state tokens plus --media-duration-indicator, --media-hidden-preview-*,
and --media-hidden-seek-offset. Slider tracking transitions read --media-duration-slider, which reduced
motion sets to zero, so the slider modules drop their motion-safe prefixes and follow the same token
policy as the rest of the system.
media-transitioning matches an element in its starting or ending style, replacing the repeated
data-starting-style and data-ending-style pairs and the arbitrary :is() selector that indicators,
dialogs, popups, menus, and the seek indicator carried. media-anchored wraps the CSS anchor positioning
support check the menu highlight repeated seven times. Both export through the registry theme like the
existing variants.
…ariant

The variant now also matches [data-highlighted], so menu items and their icons use it instead of
separate hover and data-highlighted utilities. Hover highlighting is therefore gated on a hover-capable
pointer, which removes the sticky highlight touch devices showed after a tap.
Dialog radius, width, and maximum width and the controls bar radius move from per-theme literals in
style modules into --media-dialog-* and --media-controls-radius tokens behind rounded-media-dialog,
w-media-dialog-width, max-w-media-dialog, and rounded-media-controls, so the Minimal differences live in
the theme files. The hairline the Minimal audio bar drew by hand is now shadow-media-hairline, and the
tooltip shadow gains a default so shadow-media-tooltip resolves in every theme. Values are unchanged at
scale one; the bar radius and dialog width now follow --media-scale.
Menu items use Tailwind's ease-in-out instead of an arbitrary timing-function property, and the
resizable settings menu popup reads transition-media-menu-resize instead of two arbitrary properties
listing six durations. The vars test now asserts that every internal token a theme key aliases is
declared in a theme file, which would have caught the tooltip shadow that only Minimal defined. The
registry theme test removes its temporary consumer directory after loading it.
media-anchored:data-highlighted:bg-transparent and media-highlighted:highlight-media share a specificity,
so Tailwind's variant declaration order decides which wins. Record that next to the variant.
src/styles/tailwind.css had no importer since the playground moved to tailwind.compiler.css and the
candidate manifest, and scanning raw style modules is what the manifest replaced. The
--color-media-accent-text theme key had no call site because highlight-media reads the token directly.
tailwind-merge and @playwright/test were declared but never used by this package.
Walk from one skin's component tree through shared components, tokens, the build, and the playground,
then map every folder, explain the style layers, the three Tailwind entry files and who consumes them,
the generated outputs, and the package commands.
With the unused scanning entry gone, the design system file takes the plain name. The compiler and
dev entries, the registry theme item, the catalog tests, the README, and the component skill point at
the new path.
--color-media-accent aliased --media-primary and --color-media-control-hover aliased --media-accent,
so the class names disagreed with the tokens behind them. bg-media-accent now means the accent token
and bg-media-primary the primary token; the slider fill, volume indicator fill, and menu badge move to
the matching class. Compiled output is unchanged.
The manifest lives in the Vite cache directory, which the Vite watcher ignores along with node_modules,
so Tailwind kept the CSS it compiled before style modules recorded their utilities. In the skins
playground that dropped classes such as gap-2.5 from the default video controls until a stylesheet was
touched. The style plugin now re-includes the manifest through server.watch.ignored, and it carries the
candidates of an earlier session forward so a restart never shrinks the manifest before modules record
again.
The live video skins open a captions menu from the captions button, and that trigger lost its tooltip
when the menu started rendering the real captions button. Wrap the trigger in the shared button
tooltip so both targets label it from the button, with the captions shortcut, like every other control.
The Vite contract test now expects the tooltip instead of asserting its absence.
Tailwind skips gitignored directories, and app/_generated is ignored, so the React skins the setup
task installs there were never scanned; the Tailwind sandbox pages rendered without any skin utilities
while the HTML tree already had an explicit source. List the components directory the same way.
React Fast Refresh treated dev/main.tsx as a boundary, so whenever workspace dist files were rebuilt
while the playground ran, hundreds of module updates re-executed the entry and invalidated the player
context module mid-render, throwing "usePlayerContext must be used within a Player" before Vite gave up
and reloaded. Excluding the entry from refresh lets those updates fall through to one full reload while
skin component edits keep hot refreshing.
…tails

The playground's copy button now produces a markdown report with the URL, branch and commit, every
option, browser, viewport, the live state of the preferences the theme reacts to, and the last runtime
errors, and shows it inline when the clipboard is unavailable. Preference badges above the player update
as DevTools emulation flips them, so testers know which mode they are looking at.
The package test script type checks src with unchecked index access, where the untyped array from map
made the alias values possibly undefined. CI failed there while the workspace typecheck, which does
not include the tests, passed.
compare=styles renders the selected skin twice, CSS above Tailwind, sharing the media, width, and
every other option, so parity regressions between the two styling outputs show up in one view instead
of two tabs. Works for both the React and HTML targets.
dir=rtl sets the document direction so right-to-left control order, menus, and slider previews can be
checked without editing the page.
@mihar-22
mihar-22 force-pushed the refactor/skins-design-system branch from 6383208 to 159089a Compare September 3, 2026 18:56
@mihar-22
mihar-22 merged commit 3188d1a into main Sep 3, 2026
15 of 18 checks passed
@mihar-22
mihar-22 deleted the refactor/skins-design-system branch September 3, 2026 18:59
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.

3 participants