Skip to content

refactor(html): SkinMixin -> SkinElement#1159

Merged
mihar-22 merged 6 commits intomainfrom
refactor/skin-element-utils
Apr 1, 2026
Merged

refactor(html): SkinMixin -> SkinElement#1159
mihar-22 merged 6 commits intomainfrom
refactor/skin-element-utils

Conversation

@sampotts
Copy link
Copy Markdown
Collaborator

@sampotts sampotts commented Mar 30, 2026

Refs #1155

Summary

Address post-merge review feedback from @mihar-22 on #1155:

  • Extract generic shadow DOM style utilities from the skin module into @videojs/utils/domcreateShadowStyle, applyShadowStyles, ensureGlobalStyle, createTemplate (each in its own module)
  • Replace the SkinMixin(ReactiveElement) pattern with a SkinElement base class — simpler types, clearer inheritance, no mixin indirection
  • Templates are lazily created and cached per subclass on first construction via WeakMap, avoiding permanent null if the module is imported before document is available

Changes

  • @videojs/utils/dom: New shadow-styles module (createShadowStyle, applyShadowStyles, ensureGlobalStyle, ShadowStyle type) and template module (createTemplate) with full test coverage
  • @videojs/html: New SkinElement base class replaces SkinMixin; all 8 skin definitions updated to extend it directly
  • background/skin.ts: Uses shared ensureGlobalStyle instead of local helper

Testing

  • pnpm -F @videojs/utils test src/dom/tests/
  • pnpm -F @videojs/html test
  • pnpm -F @videojs/html build

Note

Medium Risk
Refactors how all audio/video skin custom elements render templates and apply shadow-root styles, which could subtly affect styling, slotting, or SSR behavior. Changes are mostly internal abstractions but touch core UI skins across multiple variants.

Overview
Refactors HTML skin definitions to drop the SkinMixin(ReactiveElement) pattern in favor of a new SkinElement base class that owns shadow-root creation, shared/root style injection, and template cloning.

Extracts shadow DOM styling and template helpers into @videojs/utils/dom (new shadow-styles and template modules), updates skins to use createShadowStyle/createTemplate, and replaces the background skin’s local style injection with shared ensureGlobalStyle. Adds unit tests covering the new DOM utilities and their SSR/fallback behavior.

Written by Cursor Bugbot for commit d3fe0ca. This will update automatically on new commits. Configure here.

…xin with base class

Move generic shadow DOM style utilities (`createShadowStyle`,
`applyShadowStyles`, `ShadowStyle` type) from `@videojs/html` into
`@videojs/utils/dom` so they are reusable across packages.

Replace the `SkinMixin` pattern with a `SkinElement` base class that
directly extends `ReactiveElement`, simplifying the type signatures
and inheritance chain for all 8 skin element definitions.

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 30, 2026

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

Project Deployment Actions Updated (UTC)
v10-sandbox Ready Ready Preview, Comment Apr 1, 2026 5:09am

Request Review

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 30, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit d3fe0ca
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69cca85a2b90e7000896375f
😎 Deploy Preview https://deploy-preview-1159--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

📦 Bundle Size Report

🎨 @videojs/html

Path Base PR Diff %
/skin-element 1.34 kB 🆕
Presets (7)
Entry Size
/video (default) 24.68 kB
/video (default + hls) 156.28 kB
/video (minimal) 24.61 kB
/video (minimal + hls) 156.28 kB
/audio (default) 22.69 kB
/audio (minimal) 22.79 kB
/background 6.86 kB
Media (7)
Entry Size
/media/background-video 1.04 kB
/media/container 1.59 kB
/media/dash-video 236.49 kB
/media/hls-video 133.17 kB
/media/mux-video 155.85 kB
/media/native-hls-video 3.03 kB
/media/simple-hls-video 14.98 kB
Players (3)
Entry Size
/video/player 6.57 kB
/audio/player 6.56 kB
/background/player 6.56 kB
Skins (17)
Entry Type Size
/video/minimal-skin.css css 3.43 kB
/video/skin.css css 3.45 kB
/video/minimal-skin js 23.77 kB
/video/minimal-skin.tailwind js 24.30 kB
/video/skin js 23.87 kB
/video/skin.tailwind js 24.32 kB
/audio/minimal-skin.css css 2.41 kB
/audio/skin.css css 2.37 kB
/audio/minimal-skin js 21.95 kB
/audio/minimal-skin.tailwind js 22.29 kB
/audio/skin js 21.90 kB
/audio/skin.tailwind js 22.35 kB
/background/skin.css css 117 B
/background/skin js 1.15 kB
/base.css css 157 B
/shared.css css 86 B
/skin-element js 1.34 kB
UI Components (22)
Entry Size
/ui/alert-dialog 1.91 kB
/ui/alert-dialog-close 1.63 kB
/ui/alert-dialog-description 1.59 kB
/ui/alert-dialog-title 1.58 kB
/ui/buffering-indicator 1.59 kB
/ui/captions-button 1.84 kB
/ui/controls 1.62 kB
/ui/error-dialog 2.00 kB
/ui/fullscreen-button 1.86 kB
/ui/mute-button 1.84 kB
/ui/pip-button 1.85 kB
/ui/play-button 1.83 kB
/ui/playback-rate-button 1.88 kB
/ui/popover 2.47 kB
/ui/poster 1.50 kB
/ui/seek-button 1.86 kB
/ui/slider 2.09 kB
/ui/thumbnail 1.91 kB
/ui/time 1.72 kB
/ui/time-slider 2.08 kB
/ui/tooltip 2.13 kB
/ui/volume-slider 2.26 kB

Sizes are marginal over the root entry point.

⚛️ @videojs/react

Path Base PR Diff %
/media/hls-video 133.06 kB 133.36 kB +309 B +0.2% 🔺
Presets (7)
Entry Size
/video (default) 19.33 kB
/video (default + hls) 151.06 kB
/video (minimal) 19.35 kB
/video (minimal + hls) 151.15 kB
/audio (default) 16.08 kB
/audio (minimal) 16.17 kB
/background 3.13 kB
Media (6)
Entry Size
/media/background-video 476 B
/media/dash-video 236.45 kB
/media/hls-video 133.36 kB
/media/mux-video 155.88 kB
/media/native-hls-video 2.96 kB
/media/simple-hls-video 14.98 kB
Skins (14)
Entry Type Size
/video/minimal-skin.css css 3.43 kB
/video/skin.css css 3.45 kB
/video/minimal-skin js 19.27 kB
/video/minimal-skin.tailwind js 22.74 kB
/video/skin js 19.23 kB
/video/skin.tailwind js 22.78 kB
/audio/minimal-skin.css css 2.41 kB
/audio/skin.css css 2.37 kB
/audio/minimal-skin js 16.09 kB
/audio/minimal-skin.tailwind js 18.58 kB
/audio/skin js 16.03 kB
/audio/skin.tailwind js 18.60 kB
/background/skin.css css 90 B
/background/skin js 272 B
UI Components (19)
Entry Size
/ui/alert-dialog 2.29 kB
/ui/buffering-indicator 1.92 kB
/ui/captions-button 1.70 kB
/ui/controls 1.22 kB
/ui/error-dialog 1.50 kB
/ui/fullscreen-button 2.29 kB
/ui/mute-button 2.32 kB
/ui/pip-button 2.34 kB
/ui/play-button 2.38 kB
/ui/playback-rate-button 2.35 kB
/ui/popover 2.96 kB
/ui/poster 1.76 kB
/ui/seek-button 2.35 kB
/ui/slider 3.17 kB
/ui/thumbnail 2.04 kB
/ui/time 1.95 kB
/ui/time-slider 2.82 kB
/ui/tooltip 2.99 kB
/ui/volume-slider 2.72 kB

Sizes are marginal over the root entry point.

🧩 @videojs/core — no changes
Entries (8)
Entry Size
. 5.12 kB
/dom 8.77 kB
/dom/media/custom-media-element 1.81 kB
/dom/media/dash 235.77 kB
/dom/media/hls 132.64 kB
/dom/media/mux 155.33 kB
/dom/media/native-hls 2.38 kB
/dom/media/simple-hls 14.39 kB
🏷️ @videojs/element — no changes
Entries (2)
Entry Size
. 999 B
/context 943 B
📦 @videojs/store — no changes
Entries (3)
Entry Size
. 1.39 kB
/html 696 B
/react 360 B
🔧 @videojs/utils — no changes
Entries (10)
Entry Size
/array 104 B
/dom 1.53 kB
/events 319 B
/function 261 B
/object 247 B
/predicate 265 B
/string 148 B
/style 190 B
/time 478 B
/number 158 B
📦 @videojs/spf — no changes
Entries (3)
Entry Size
. 40 B
/dom 12.45 kB
/playback-engine 12.41 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors shadow-DOM styling and skin infrastructure by extracting generic shadow style helpers into @videojs/utils/dom and replacing the SkinMixin(ReactiveElement) pattern with a concrete SkinElement base class used by all built-in audio/video skins.

Changes:

  • Added createShadowStyle, applyShadowStyles, and ShadowStyle to @videojs/utils/dom, with constructable stylesheet support and <style> fallback.
  • Introduced SkinElement extends ReactiveElement to encapsulate shadow root creation, template rendering, and shared/per-skin style application.
  • Updated all built-in audio/video default/minimal (CSS + tailwind) skins to extend SkinElement and use createShadowStyle for inline CSS.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/utils/src/dom/shadow-styles.ts New shared utilities for creating/applying shadow-root styles with constructable-sheet + <style> fallback.
packages/utils/src/dom/index.ts Re-exports the new shadow style utilities from the dom entrypoint.
packages/utils/src/dom/tests/shadow-styles.test.ts Adds unit tests covering constructable stylesheet creation and apply fallback behavior.
packages/html/src/define/skin-element.ts Adds SkinElement base class to replace the removed mixin and centralize skin shadow-root setup.
packages/html/src/define/skin-mixin.ts Removes the prior SkinMixin + createStyles implementation (logic moved/replaced).
packages/html/src/define/video/skin.ts Migrates video default CSS skin to SkinElement + createShadowStyle.
packages/html/src/define/video/skin.tailwind.ts Migrates video default tailwind skin to SkinElement.
packages/html/src/define/video/minimal-skin.ts Migrates video minimal CSS skin to SkinElement + createShadowStyle.
packages/html/src/define/video/minimal-skin.tailwind.ts Migrates video minimal tailwind skin to SkinElement.
packages/html/src/define/audio/skin.ts Migrates audio default CSS skin to SkinElement + createShadowStyle.
packages/html/src/define/audio/skin.tailwind.ts Migrates audio default tailwind skin to SkinElement.
packages/html/src/define/audio/minimal-skin.ts Migrates audio minimal CSS skin to SkinElement + createShadowStyle.
packages/html/src/define/audio/minimal-skin.tailwind.ts Migrates audio minimal tailwind skin to SkinElement.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

mihar-22
mihar-22 previously approved these changes Mar 31, 2026
Comment thread packages/html/src/define/audio/minimal-skin.tailwind.ts Outdated
Comment thread packages/html/src/define/skin-element.ts Outdated
@mihar-22 mihar-22 changed the title refactor(packages): extract shadow style utilities and replace SkinMixin with base class refactor(html): SkinMixin -> SkinElement Mar 31, 2026
…ities

Move ensureRootStyles into a generic ensureGlobalStyle(id, css) utility
in @videojs/utils/dom. Add createTemplate(html) for SSR-safe
HTMLTemplateElement creation.

Replace innerHTML-based template rendering with document.importNode
cloning for better performance (parse once, clone per instance).

Update SkinElement and all skin definitions to use static template
instead of static getTemplateHTML. Also update background/skin.ts to
use ensureGlobalStyle.

Made-with: Cursor
Copy link
Copy Markdown

@cursor cursor bot left a comment

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

Comment thread packages/html/src/define/skin-element.ts
createTemplate() was running at module evaluation via static class
fields. If first imported without document, template became null
permanently. Move to lazy creation on first construction via WeakMap.

Made-with: Cursor
Comment thread packages/html/src/define/skin-element.ts Outdated
Comment thread packages/html/src/define/skin-element.ts Outdated
Comment thread packages/utils/src/dom/shadow-styles.ts Outdated
Address review feedback: createTemplate is unrelated to shadow styles,
so move it to a dedicated template.ts module with its own test file.
Also clean up resolveTemplate to avoid unnecessary ?? undefined.

Made-with: Cursor
Replace lazy WeakMap + resolveTemplate() with a simple
`static template` field set eagerly at class evaluation via
createTemplate(). These are custom elements that only construct
in the browser, so lazy SSR guards are unnecessary.
@mihar-22 mihar-22 merged commit 4cda8fb into main Apr 1, 2026
21 checks passed
@mihar-22 mihar-22 deleted the refactor/skin-element-utils branch April 1, 2026 05:22
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