fix(skin): style poster and slider thumbnail shadow hosts in webkit - #2693
Conversation
WebKit does not match an `@scope` rule whose subject hosts a shadow root or is slotted into one. `<media-poster>` gained a shadow root in #2563 and `<media-slider-thumbnail>` became the styled root in #2572, so in Safari the poster no longer faded out on play and the slider thumbnail rendered unpositioned and unstyled inside the preview. Chromium and Firefox were unaffected. `vjsc` already has a `shadowHost` rule flag for this, but #2572 removed its only use from the image rule without flagging the new host, and the poster was never flagged. The flag now goes on the poster root and image, the thumbnail root, image, and spinner, and the shared preview-content and popup-surface rules the thumbnail host carries. The renderer now detects a shadow-host rule by its selector's subject rather than its first component, so relationship selectors such as `:where(.owner)[data-x] .subject` are covered, and it repeats those rules outside the scope block instead of moving them. A scoped rule always outranks an equal-specificity unscoped one on proximity, so the copy can never beat the original and the cascade in engines that match `@scope` correctly is unchanged.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for vjs-registry ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ 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: |
📦 Bundle Size Report🎨 @videojs/html — 30 small size changes
Presets (7)
Media (18)
Players (5)
Skins (29)
UI Components (62)
⚛️ @videojs/react — 8 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 — no changes📦 @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 — no changes📦 @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 |
decepulis
left a comment
There was a problem hiding this comment.
based on the fact that this fixes the problem, is easily reversible, and doesn't affect the user-facing api, I'm inclined to approve!
Refs #2563
Refs #2572
Summary
In Safari, the poster no longer hides once playback starts and the time slider's storyboard thumbnail renders unstyled inside the preview. WebKit does not match an
@scoperule whose subject hosts a shadow root or is slotted into one, and both<media-poster>(#2563) and<media-slider-thumbnail>(#2572) recently became shadow hosts carrying skin classes. Chromium and Firefox are unaffected.Changes
previewContentandpopup.surfacerules asshadowHost, so they are also emitted outside the@scopeblock for WebKit.vjscrenderer detects shadow-host rules by the selector's subject rather than its first component, so relationship selectors such as:where(.media-slider-preview)[data-pointing] .media-slider-preview-contentare covered.Implementation details
Repeating rather than moving keeps the cascade identical in engines that match
@scopecorrectly: a scoped declaration always outranks an equal-specificity unscoped one on scope proximity regardless of source order (verified in Chromium, WebKit, and Firefox), so the:where(scope)-prefixed copy can never beat the original. Moving would have changed tie-breaking for shared classes likemedia-popup-surfaceon tooltips and menus.shadowHostis now documented as covering slotted light-DOM children too; the thumbnail image and spinner are not hosts themselves but are slotted through one, and WebKit skips scoped rules for them as well.Testing
pnpm -F vjsc test src/styles(updated shadow-host compile test) andpnpm -F @videojs/skins test./html-video/and/html-mux-video/pages in WebKit, across the package, registry, and authored skin sources and the minimal skin:media-posterfades toopacity: 0after play, andmedia-slider-thumbnailis absolutely positioned at 190×106 with rounded corners, matching Chromium. Chromium output unchanged.Note
Medium Risk
Touches global skin CSS compilation and cascade for all engines; duplication is designed not to outrank scoped rules, but styling regressions are possible if scope proximity behaves differently than expected.
Overview
Fixes Safari/WebKit styling for
<media-poster>and<media-slider-thumbnail>(and related preview/popup surfaces) after those elements became shadow hosts carrying skin classes. WebKit does not apply@scoperules when the subject hosts a shadow root or is slotted through one.Skin definitions now mark poster/thumbnail (root, image, spinner), slider
previewContent, and popupsurfacewithshadowHost: trueso the compiler emits the extra unscoped copy.vjscCSS emission changes how those rules are handled: shadow-host rules stay inside@scopeand are duplicated outside with a:where(.media-skin-video)ancestor (instead of being removed from the scoped block), preserving cascade behavior in Chromium/Firefox while giving WebKit a matching rule. Detection now keys off the selector subject (last compound), so relationship selectors like preview → preview-content are included. Slotted::slottedrules are still moved out only.Compile tests were updated to assert the repeat-outside-scope behavior and nested relationship scopes.
Reviewed by Cursor Bugbot for commit ed228ac. Bugbot is set up for automated code reviews on this repo. Configure here.