Skip to content

feat(react): implement default and minimal video skins#550

Merged
sampotts merged 1 commit intomainfrom
feat/skins
Feb 18, 2026
Merged

feat(react): implement default and minimal video skins#550
sampotts merged 1 commit intomainfrom
feat/skins

Conversation

@sampotts
Copy link
Copy Markdown
Collaborator

@sampotts sampotts commented Feb 18, 2026

Summary

Implement the default and minimal video skins for the React player, wiring up all UI controls (play, pause, seek, mute, PiP, fullscreen, buffering indicator) with proper icon sets and CSS styling.

Closes #539

Changes

  • Implement VideoSkin and MinimalVideoSkin components with full control layouts including play/pause, seek forward/back, mute/volume, PiP, fullscreen, time display, and buffering indicator
  • Enhance cn() utility to support object syntax for conditional class names (like clsx)
  • Add pip, restart, and seek icons to both default and minimal icon sets
  • Update existing SVG icons with refined designs and proper SVGO optimization pipeline
  • Add watch mode to icons build script for faster development
  • Add barrel exports for React UI components (play-button, mute-button, fullscreen-button, pip-button, seek-button, buffering-indicator, poster)
  • Fix RenderFunction type to allow returning null for conditional rendering
  • Fix composeRefs to only return cleanup when React 19+ cleanup callbacks are present
  • Improve PiP button accessibility labels ("Enter/Exit picture-in-picture")
  • Update site demos and docs to use new skin components instead of legacy react-preview imports
Implementation details
  • Both skins use a render-prop pattern for all controls, keeping the skin in full control of markup and styling
  • Icon visibility is toggled via CSS classes (media-icon--hidden) driven by component state
  • The cn() enhancement accepts Record<string, unknown> values where truthy values include the key as a class name
  • SVGO config now strips fill/stroke attributes and adds fill="currentColor" for theme-able icons
  • BaseSkinProps extended with className and style for skin customization

Testing

  • cn() changes covered by new tests in packages/utils/src/style/tests/cn.test.ts
  • Manual: run pnpm dev and verify both default and minimal skins render controls correctly

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 18, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
vjs-10-demo-react Ignored Ignored Preview Feb 18, 2026 8:39pm

Request Review

@netlify
Copy link
Copy Markdown

netlify bot commented Feb 18, 2026

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit cf2bff6
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/69962391140ce000087a8ba3
😎 Deploy Preview https://deploy-preview-550--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 Feb 18, 2026

📦 Bundle Size Report

Package Size Diff %
@videojs/core 5.85 kB +13 B ░░░░░░░░ +0.2% 🔺
@videojs/element 1.60 kB 0 B ░░░░░░░░ 0%
@videojs/html 8.93 kB +2 B ░░░░░░░░ +0.0% 🔺
@videojs/icons 3.46 kB -2.94 kB ██████░░ -45.9% 🔽
@videojs/react 13.46 kB +4.91 kB ████████ +57.5% 🔴
@videojs/store 1.94 kB 0 B ░░░░░░░░ 0%
@videojs/utils 2.47 kB +122 B █░░░░░░░ +5.1% 🔺

Total: 37.72 kB · +2.11 kB · +5.9%


Entry Breakdown

Subpath sizes are the additional bytes on top of the root entry point, measured by bundling root + subpath together and subtracting the root-only size.

@videojs/core
Entry Base PR Diff %
. 3.09 kB 3.09 kB +4 B +0.1% 🔺
./dom 2.75 kB 2.75 kB +9 B +0.3% 🔺
total 5.84 kB 5.85 kB +13 B +0.2%
@videojs/element
Entry Base PR Diff %
. 817 B 817 B 0 B 0%
./context 823 B 823 B 0 B 0%
total 1.60 kB 1.60 kB 0 B 0%
@videojs/icons
Entry Base PR Diff %
./react 4.77 kB 2.10 kB -2.67 kB -56.0% 🔽
./html 1.64 kB 1.37 kB -277 B -16.5% 🔽
total 6.40 kB 3.46 kB -2.94 kB -45.9%
@videojs/react
Entry Base PR Diff %
. 7.99 kB 8.00 kB +13 B +0.2% 🔺
./audio 266 B 255 B -11 B -4.1% 🔽
./background 40 B 34 B -6 B -15.0% 🔽
./video 264 B 5.17 kB +4.92 kB +1907.2% 🔴
total 8.55 kB 13.46 kB +4.91 kB +57.5%
@videojs/store
Entry Base PR Diff %
. 1.29 kB 1.29 kB 0 B 0%
./html 468 B 468 B 0 B 0%
./react 199 B 199 B 0 B 0%
total 1.94 kB 1.94 kB 0 B 0%
@videojs/utils
Entry Base PR Diff %
./array 104 B 104 B 0 B 0%
./dom 684 B 684 B 0 B 0%
./events 227 B 227 B 0 B 0%
./function 197 B 197 B 0 B 0%
./object 119 B 119 B 0 B 0%
./predicate 265 B 265 B 0 B 0%
./string 110 B 110 B 0 B 0%
./style 63 B 185 B +122 B +193.7% 🔴
./time 478 B 478 B 0 B 0%
./number 158 B 158 B 0 B 0%
total 2.35 kB 2.47 kB +122 B +5.1%

ℹ️ How to interpret

Sizes are minified + brotli, measured with esbuild.
Package totals are computed as root size + marginal subpath costs.
Subpath marginal cost = (root + subpath bundled together) − root alone.

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 implements the default and minimal video skins for the React player, introducing fully functional UI controls with proper theming and accessibility. The implementation includes comprehensive enhancements to utilities, icons, and React components to support the new skin system.

Changes:

  • Implemented VideoSkin and MinimalVideoSkin components with complete control layouts (play/pause, seek, mute, PiP, fullscreen, buffering)
  • Enhanced cn() utility to support clsx-like object syntax for conditional class names
  • Added new icons (pip, restart, seek) and updated existing SVG icons with refined designs
  • Added barrel exports for React UI components and fixed type/ref composition issues
  • Updated site demos and documentation to use new skin components

Reviewed changes

Copilot reviewed 25 out of 46 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/utils/src/style/cn.ts Enhanced to support object syntax for conditional classes
packages/utils/src/style/tests/cn.test.ts Comprehensive tests for new cn() functionality
packages/react/src/utils/types.ts Fixed RenderFunction to allow null returns
packages/react/src/utils/use-composed-refs.ts Fixed to only return cleanup when React 19+ callbacks present
packages/react/src/presets/video/skin.tsx Default video skin implementation with glassmorphic design
packages/react/src/presets/video/skin.css Comprehensive styles for default skin with accessibility features
packages/react/src/presets/video/minimal-skin.tsx Minimal video skin implementation
packages/react/src/presets/video/minimal-skin.css Styles for minimal skin with container queries
packages/react/src/presets/types.ts Enhanced BaseSkinProps with className and style support
packages/react/src/ui/*/index.ts Barrel exports for UI components (7 files)
packages/icons/scripts/build.ts Added SVGO optimization, watch mode, and jsxRuntime config
packages/icons/package.json Added build:watch and dev scripts
packages/icons/src/assets//.svg Updated 20 SVG icons with new designs
packages/core/src/core/ui/pip-button/pip-button-core.ts Improved accessibility labels
site/src/examples/react/*/Demo.tsx Updated demos to use new skin components (2 files)
site/src/content/docs/concepts/skins.mdx Updated documentation examples
site/src/components/frames/Minimal.astro Simplified container styling
site/src/components/FilmGrain/FilmGrain.tsx Adjusted z-index for proper layering
packages/react/package.json Added @videojs/icons dependency
pnpm-lock.yaml Updated lockfile with new dependency
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

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

Comment thread packages/react/src/presets/video/skin.css Outdated
Comment thread packages/icons/scripts/build.ts
Comment thread site/src/examples/react/FrostedSkin/FrostedSkinDemo.tsx Outdated
className={twMerge(
clsx(
'absolute w-full h-full top-0 left-0 pointer-events-none mix-blend-overlay z-10',
'absolute w-full h-full top-0 left-0 pointer-events-none mix-blend-overlay z-0',
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

The z-index is changed from z-10 to z-0. This change might affect the layering of the film grain effect. Without seeing the broader context of the page layout, it's unclear if this change is intentional or if it could cause the film grain to be covered by other elements. Please verify that the film grain effect is still visible as intended on the docs pages.

Copilot uses AI. Check for mistakes.
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.

Yeah check carefully for regressions, here! Iirc there was a reason I chose z-10 instead of z-0.... but... could be wrong.

My Demo component displays players at z-20 to combat this 😂

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, yeah I should have checked with you. The issue with z-10 was it caused the grain effect to bleed into the players (in Safari at least) and z-0 seemed to allow the effect on the background of the site. Maybe it's due to a classname change that's causing the z-20 to not work.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've used the ContentWidth.astro instead and setting relative z-20 on it 👍🏼

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.

Thanks. Awkward, but effective!

Comment thread packages/react/src/presets/video/minimal-skin.tsx Outdated
Comment thread packages/icons/scripts/build.ts Outdated
Comment thread site/src/content/docs/concepts/skins.mdx
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.

We have a different frame that provides unstyled functionality -- site/src/components/frames/ContentWidth.astro

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ah ok cool. I'll use that and revert this change. I just felt it looked a little odd as the container had its own radius which didn't match the players and I wasn't sure we needed a background and border for these demos.

@sampotts sampotts changed the title feat(packages): implement default and minimal video skins feat(react): implement default and minimal video skins Feb 18, 2026
@sampotts sampotts merged commit 7d3be36 into main Feb 18, 2026
12 checks passed
@sampotts sampotts deleted the feat/skins branch February 18, 2026 20:43
@github-actions github-actions bot mentioned this pull request Feb 18, 2026
@github-actions github-actions bot mentioned this pull request Feb 26, 2026
@github-actions github-actions bot mentioned this pull request Mar 10, 2026
This was referenced Apr 11, 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.

Skins: Recreate default and minimal skin in React

3 participants