Skip to content

feat(skin): improved responsive design - #1832

Merged
sampotts merged 1 commit into
mainfrom
fix/responsive-skins
Jul 30, 2026
Merged

feat(skin): improved responsive design#1832
sampotts merged 1 commit into
mainfrom
fix/responsive-skins

Conversation

@sampotts

@sampotts sampotts commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Closes #1382

  • On small screens, Default adds a secondary controls section in the top right for fullscreen, pip and casting controls to create more space for the other controls.
  • Added a scaling system for larger displays in fullscreen.

Other changes

  • Lighting/shadow tweaks to Default controls.
  • Menu radius and padding tweaks in Minimal.
  • Moved the volume control to the left in both skins.
  • Fixed missing tooltip define entries in HTML live skins.
  • Seek buttons removed from both skins, to provide space for the captions toggle button.
  • Updated css-to-tailwind AI skill re: how spacing works now and to prevent it using custom classnames in the Tailwind skins.

Note

Medium Risk
Broad, user-visible control layout changes across HTML and React presets; regression risk is mainly visual/UX and tooltip registration, not security or data.

Overview
Reworks Default and Minimal player chrome (HTML skins, React presets, and live variants) for tighter small screens and clearer fullscreen scaling.

Default video splits the bar into primaryControls (play, volume, scrubber, captions, settings) and secondaryControls (cast, AirPlay, PiP, fullscreen). Legacy CSS skins use matching media-controls--primary / --secondary wrappers. Seek ±10s controls are removed from default and minimal video so a standalone captions toggle can sit on the bar; captions stay in the settings menu as well.

Volume moves beside play on minimal video/live (popover side="right", horizontal slider in minimal). Minimal live volume sits in the start button group; audio minimal puts volume before playback rate.

Tailwind templates drop ad‑hoc grow and BEM hooks (media-menu__item--submenu, media-sr-only, etc.) in favor of skin tokens like spacer and shared menu classes. Tooltip custom elements register via new defineTooltip() on all relevant UI bundles (fixing missing defines on live HTML skins). The css-to-tailwind skill doc adds --spacing / --spacing() guidance and a rule against legacy media-* marker classes. Visual e2e reads thumbnail sizing from --max-height instead of the old custom property name.

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

@netlify

netlify Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit 0baabae
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a6a707a463c660009d3518d
😎 Deploy Preview https://deploy-preview-1832--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.

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview Jul 29, 2026 9:28pm

Request Review

@sampotts

Copy link
Copy Markdown
Collaborator Author

cursor review

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

🎨 @videojs/html

Path Base initial PR initial Diff % Lazy
/video/minimal-skin 52.27 kB 52.58 kB +321 B +0.6% 0 B 🔺
/video/skin 52.37 kB 52.73 kB +360 B +0.7% 0 B 🔺
/live-video/minimal-skin 48.65 kB 48.95 kB +303 B +0.6% 0 B 🔺
/live-video/minimal-skin.tailwind 49.18 kB 49.52 kB +350 B +0.7% 0 B 🔺
/live-video/skin 50.86 kB 51.19 kB +340 B +0.7% 0 B 🔺
/live-video/skin.tailwind 51.47 kB 51.83 kB +373 B +0.7% 0 B 🔺
/live-audio/skin 37.68 kB 37.99 kB +321 B +0.8% 0 B 🔺
/live-audio/skin.tailwind 37.26 kB 37.59 kB +329 B +0.9% 0 B 🔺
/ui/compounds 32.22 kB 33.54 kB +1.32 kB +4.1% 0 B 🔺
/video (default) 52.41 kB 52.70 kB +302 B +0.6% 0 B 🔺
/video (default + hls) 191.64 kB 192.18 kB +556 B +0.3% 0 B 🔺
/video (minimal + hls) 191.66 kB 191.97 kB +312 B +0.2% 0 B 🔺
/audio (default) 45.42 kB 45.72 kB +307 B +0.7% 0 B 🔺
Presets (7)
Entry Initial Lazy
/video (default) 52.70 kB 50.16 kB
/video (default + hls) 192.18 kB 50.16 kB
/video (minimal) 52.59 kB 50.16 kB
/video (minimal + hls) 191.97 kB 50.16 kB
/audio (default) 45.72 kB 50.16 kB
/audio (minimal) 42.20 kB 50.16 kB
/background 4.20 kB
Media (10)
Entry Initial
/media/background-video 1.14 kB
/media/container 1.71 kB
/media/dash-video 214.86 kB
/media/hlsjs-video 141.54 kB
/media/mux-audio 164.86 kB
/media/mux-video 164.86 kB
/media/native-hls-video 9.07 kB
/media/simple-hls-audio-only 19.08 kB
/media/simple-hls-video 21.13 kB
/media/vimeo-video 12.31 kB
Players (5)
Entry Initial
/video/player 8.22 kB
/audio/player 5.37 kB
/background/player 3.92 kB
/live-video/player 7.63 kB
/live-audio/player 5.38 kB
Skins (30)
Entry Type Initial Lazy
/video/minimal-skin.css css 5.93 kB
/video/skin.css css 5.94 kB
/video/minimal-skin js 52.58 kB 50.16 kB
/video/minimal-skin.tailwind js 53.23 kB 50.16 kB
/video/skin js 52.73 kB 50.16 kB
/video/skin.tailwind js 53.44 kB 50.16 kB
/audio/minimal-skin.css css 4.12 kB
/audio/skin.css css 4.06 kB
/audio/minimal-skin js 42.26 kB 50.16 kB
/audio/minimal-skin.tailwind js 42.77 kB 50.16 kB
/audio/skin js 45.71 kB 50.16 kB
/audio/skin.tailwind js 46.22 kB 50.16 kB
/background/skin.css css 133 B
/background/skin js 1.14 kB
/live-video/minimal-skin.css css 5.93 kB
/live-video/skin.css css 5.94 kB
/live-video/minimal-skin js 48.95 kB 50.16 kB
/live-video/minimal-skin.tailwind js 49.52 kB 50.16 kB
/live-video/skin js 51.19 kB 50.16 kB
/live-video/skin.tailwind js 51.83 kB 50.16 kB
/live-audio/minimal-skin.css css 4.12 kB
/live-audio/skin.css css 4.06 kB
/live-audio/minimal-skin js 34.41 kB 50.16 kB
/live-audio/minimal-skin.tailwind js 33.90 kB 50.16 kB
/live-audio/skin js 37.99 kB 50.16 kB
/live-audio/skin.tailwind js 37.59 kB 50.16 kB
/global.css css 183 B
/shared.css css 153 B
/tailwind.css css 161 B
/skin-element js 1.51 kB
UI Components (39)
Entry Initial
/ui/airplay-button 2.63 kB
/ui/alert-dialog 2.88 kB
/ui/alert-dialog-close 2.42 kB
/ui/alert-dialog-description 2.46 kB
/ui/alert-dialog-title 2.47 kB
/ui/audio-track-radio-group 3.06 kB
/ui/buffering-indicator 2.66 kB
/ui/captions-button 2.67 kB
/ui/captions-radio-group 3.05 kB
/ui/cast-button 2.57 kB
/ui/compounds 3.39 kB
/ui/controls 3.01 kB
/ui/error-dialog 3.02 kB
/ui/fullscreen-button 2.62 kB
/ui/hotkey 2.58 kB
/ui/menu 3.05 kB
/ui/mute-button 2.58 kB
/ui/pip-button 2.64 kB
/ui/play-button 2.66 kB
/ui/playback-rate-button 2.66 kB
/ui/playback-rate-radio-group 2.90 kB
/ui/popover 3.21 kB
/ui/poster 2.46 kB
/ui/quality-radio-group 2.99 kB
/ui/seek-button 2.54 kB
/ui/seek-indicator 2.84 kB
/ui/seek-indicator-value 441 B
/ui/slider 3.04 kB
/ui/status-announcer 2.50 kB
/ui/status-indicator 2.59 kB
/ui/status-indicator-value 448 B
/ui/thumbnail 2.67 kB
/ui/time 3.03 kB
/ui/time-slider 3.05 kB
/ui/tooltip 2.94 kB
/ui/volume-indicator 2.62 kB
/ui/volume-indicator-fill 432 B
/ui/volume-indicator-value 485 B
/ui/volume-slider 3.04 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

Path Base initial PR initial Diff % Lazy
/live-video/minimal-skin.tailwind 41.99 kB 42.34 kB +355 B +0.8% 0 B 🔺
/live-video/skin.tailwind 42.07 kB 42.43 kB +368 B +0.9% 0 B 🔺
/live-audio/skin.tailwind 27.86 kB 28.17 kB +311 B +1.1% 0 B 🔺
Presets (7)
Entry Initial Lazy
/video (default) 41.49 kB 50.16 kB
/video (default + hls) 179.48 kB 50.16 kB
/video (minimal) 41.76 kB 50.16 kB
/video (minimal + hls) 179.76 kB 50.16 kB
/audio (default) 34.73 kB 50.16 kB
/audio (minimal) 34.78 kB 50.16 kB
/background 581 B
Media (9)
Entry Initial
/media/background-video 394 B
/media/dash-video 213.12 kB
/media/hlsjs-video 139.90 kB
/media/mux-audio 163.12 kB
/media/mux-video 163.36 kB
/media/native-hls-video 7.29 kB
/media/simple-hls-audio-only 17.39 kB
/media/simple-hls-video 19.42 kB
/media/vimeo-video 10.46 kB
Skins (27)
Entry Type Initial Lazy
/tailwind.css css 161 B
/video/minimal-skin.css css 5.79 kB
/video/skin.css css 5.78 kB
/video/minimal-skin js 41.73 kB 50.16 kB
/video/minimal-skin.tailwind js 47.92 kB 50.16 kB
/video/skin js 41.34 kB 50.16 kB
/video/skin.tailwind js 47.61 kB 50.16 kB
/audio/minimal-skin.css css 3.95 kB
/audio/skin.css css 3.87 kB
/audio/minimal-skin js 34.69 kB 50.16 kB
/audio/minimal-skin.tailwind js 36.86 kB 50.16 kB
/audio/skin js 34.63 kB 50.16 kB
/audio/skin.tailwind js 39.04 kB 50.16 kB
/background/skin.css css 90 B
/background/skin js 272 B
/live-video/minimal-skin.css css 5.79 kB
/live-video/skin.css css 5.78 kB
/live-video/minimal-skin js 36.19 kB 50.16 kB
/live-video/minimal-skin.tailwind js 42.34 kB 50.16 kB
/live-video/skin js 36.23 kB 50.16 kB
/live-video/skin.tailwind js 42.43 kB 50.16 kB
/live-audio/minimal-skin.css css 3.95 kB
/live-audio/skin.css css 3.87 kB
/live-audio/minimal-skin js 24.76 kB 50.16 kB
/live-audio/minimal-skin.tailwind js 28.00 kB 50.16 kB
/live-audio/skin js 24.81 kB 50.16 kB
/live-audio/skin.tailwind js 28.17 kB 50.16 kB
UI Components (33)
Entry Initial
/ui/airplay-button 2.47 kB
/ui/alert-dialog 2.69 kB
/ui/audio-track 2.43 kB
/ui/buffering-indicator 2.62 kB
/ui/captions-button 2.54 kB
/ui/captions-radio-group 2.27 kB
/ui/cast-button 2.54 kB
/ui/controls 2.49 kB
/ui/error-dialog 2.52 kB
/ui/fullscreen-button 2.53 kB
/ui/gesture 2.46 kB
/ui/hotkey 2.38 kB
/ui/live-button 2.39 kB
/ui/menu 2.64 kB
/ui/mute-button 2.53 kB
/ui/pip-button 2.52 kB
/ui/play-button 2.46 kB
/ui/playback-rate 2.51 kB
/ui/playback-rate-button 2.52 kB
/ui/popover 3.16 kB
/ui/poster 2.61 kB
/ui/quality 2.36 kB
/ui/seek-button 2.49 kB
/ui/seek-indicator 2.63 kB
/ui/slider 2.53 kB
/ui/status-announcer 2.34 kB
/ui/status-indicator 2.37 kB
/ui/thumbnail 2.54 kB
/ui/time 2.20 kB
/ui/time-slider 2.57 kB
/ui/tooltip 3.09 kB
/ui/volume-indicator 2.51 kB
/ui/volume-slider 2.59 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (72)
Entry Initial Lazy
. 10.79 kB
/dom 17.87 kB
/i18n 2.99 kB 50.16 kB
/i18n/locales/all 30.51 kB
/i18n/locales/ar 1.11 kB
/i18n/locales/az 1.01 kB
/i18n/locales/bg 1.17 kB
/i18n/locales/bn 1.20 kB
/i18n/locales/bs 926 B
/i18n/locales/ca 988 B
/i18n/locales/cs 978 B
/i18n/locales/cy 948 B
/i18n/locales/da 918 B
/i18n/locales/de 1023 B
/i18n/locales/el 1.37 kB
/i18n/locales/en 707 B
/i18n/locales/es 925 B
/i18n/locales/et 995 B
/i18n/locales/eu 939 B
/i18n/locales/fa 1.11 kB
/i18n/locales/fi 970 B
/i18n/locales/fr 1001 B
/i18n/locales/gd 1.00 kB
/i18n/locales/gl 929 B
/i18n/locales/he 1.03 kB
/i18n/locales/hi 1.21 kB
/i18n/locales/hr 948 B
/i18n/locales/hu 1019 B
/i18n/locales/it 958 B
/i18n/locales/ja 1.10 kB
/i18n/locales/ko 1.04 kB
/i18n/locales/lv 1012 B
/i18n/locales/mr 1.21 kB
/i18n/locales/nb 908 B
/i18n/locales/ne 1.20 kB
/i18n/locales/nl 934 B
/i18n/locales/nn 913 B
/i18n/locales/oc 999 B
/i18n/locales/pl 1.03 kB
/i18n/locales/pt 955 B
/i18n/locales/pt-BR 955 B
/i18n/locales/pt-PT 914 B
/i18n/locales/ro 996 B
/i18n/locales/ru 1.24 kB
/i18n/locales/sk 1018 B
/i18n/locales/sl 960 B
/i18n/locales/sr 989 B
/i18n/locales/sv 935 B
/i18n/locales/te 1.22 kB
/i18n/locales/th 1.19 kB
/i18n/locales/tr 1003 B
/i18n/locales/uk 1.27 kB
/i18n/locales/vi 1006 B
/i18n/locales/zh 947 B
/i18n/locales/zh-CN 947 B
/i18n/locales/zh-TW 949 B
/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/errors 277 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 179 B
/i18n/text/time 214 B
/i18n/text/volume 133 B
🏷️ @videojs/element — no changes
Entries (2)
Entry Initial
. 996 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Initial
. 1.39 kB
/html 696 B
/react 361 B
🔧 @videojs/utils — no changes
Entries (12)
Entry Initial
/array 104 B
/dom 3.02 kB
/events 319 B
/function 327 B
/jwt 176 B
/object 505 B
/predicate 265 B
/percent 281 B
/string 239 B
/style 190 B
/time 813 B
/number 158 B
📦 @videojs/media — no changes
Entries (14)
Entry Initial
. 959 B
/dom/audio-host 1.16 kB
/dom/custom-media-element 2.09 kB
/dom/dash 209.06 kB
/dom/google-cast 4.04 kB
/dom/hls-js 135.95 kB
/dom/media-host 1.27 kB
/dom/media-played-ranges 576 B
/dom/mux 159.27 kB
/dom/native-hls 3.06 kB
/dom/simple-hls 18.90 kB
/dom/simple-hls-audio-only 16.88 kB
/dom/video-host 1.44 kB
/dom/vimeo 9.88 kB
📦 @videojs/spf — no changes
Entries (4)
Entry Initial
. 4.46 kB
/dom 6.49 kB
/hls 17.85 kB
/background-video 13.30 kB

ℹ️ How to interpret

JS sizes are initial static graph totals (minified + brotli). Lazy dynamic chunks are shown separately when present.

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

Run pnpm size locally to check current initial sizes.

Comment thread packages/html/src/define/video/skin.tailwind.ts Outdated

@decepulis decepulis 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.

Generally these interactions feel good!

  • Are variable names like --scale or --size too generic? Should they be --media-* scoped? Or are you using those in a limited way I missed?
  • I don't think there's anything inherently wrong with duplicating items in the DOM, but it does make me wonder a bit about focus management. Like, if we hit fullscreen on mobile and the frame expands, will focus properly be transferred to a non-hidden control?
  • I threw Fable and Sol at this and they found a few things that I think are worth addressing:
  • The Default Vanilla CSS selector for hiding the trailing time also hides the slider-preview value below 10rem.
  • Invalid values passed through resolveCSSLength() can now resolve to the containing width instead of safely falling back to zero.
  • The Minimal CSS selector for the dragging fill of a vertical slider cannot match. This is latent in the built-in presets because Minimal now uses horizontal volume sliders, but it affects ejected/custom layouts.
  • The Vanilla controls still translate under prefers-reduced-motion

@sampotts

Copy link
Copy Markdown
Collaborator Author

Are variable names like --scale or --size too generic? Should they be --media-* scoped? Or are you using those in a limited way I missed?

I'm going to address it in an upcoming PR but I think we leave --media-* for custom properties we actually want users to change. These scaling ones are really internal. Also, given they're scoped, there's no worry of clashes with host styles. Maybe I'm getting fatigued seeing media-* everywhere 😅

I don't think there's anything inherently wrong with duplicating items in the DOM, but it does make me wonder a bit about focus management. Like, if we hit fullscreen on mobile and the frame expands, will focus properly be transferred to a non-hidden control?

Ideally I wouldn't duplicate the elements but how complex do we want the CSS is the issue. If we have user specified functions in CSS, it'd be fairly easy but otherwise I'll find myself duplicating a lot of CSS. Focus management is a good point though.

I threw Fable and Sol at this and they found a few things that I think are worth addressing...

Sure, I'll take a look. I thought I'd fixed at least one of those but maybe it was fixed in Tailwind only.

Comment thread packages/html/src/define/video/skin.tailwind.ts Outdated
Comment thread packages/html/src/define/live-video/skin.ts Outdated
Comment thread packages/skins/src/default/css/components/button.css
Comment thread packages/skins/src/default/tailwind/video.tailwind.ts
Comment thread packages/html/src/define/video/skin.ts
Comment thread packages/skins/src/default/css/video.css
Comment thread packages/html/src/define/video/skin.ts

@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 0a20113. Configure here.

Comment thread packages/skins/src/default/tailwind/components/button.ts

@mihar-22 mihar-22 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Love that this PR is trying to clean up and better generalize some of the design system tokens/vars. Helps with the future eject story and tailwind config. I like that the design is opening up some more space and breathing room in the lower controls on small screens. I think there's more work we can do on the overall mobile experience over time but this is great.

@sampotts
sampotts merged commit c8a9eec into main Jul 30, 2026
27 checks passed
@sampotts
sampotts deleted the fix/responsive-skins branch July 30, 2026 20:49
@luwes luwes mentioned this pull request Jul 30, 2026
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.

Feature: Skins: Improve Responsive Design

3 participants