@videojs/html 10.1.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. package/dist/default/define/audio/compat-skin.css +116 -65
  2. package/dist/default/define/audio/compat-skin2.js +1 -1
  3. package/dist/default/define/audio/neutral-skin.css +25 -16
  4. package/dist/default/define/audio/neutral-skin2.js +1 -1
  5. package/dist/default/define/audio/scaffold-skin.css +93 -51
  6. package/dist/default/define/audio/scaffold-skin2.js +1 -1
  7. package/dist/default/define/audio/skin.css +21 -6
  8. package/dist/default/define/audio/skin2.js +1 -1
  9. package/dist/default/define/extensions/google-analytics.js +6 -0
  10. package/dist/default/define/live-audio/compat-skin.css +98 -65
  11. package/dist/default/define/live-audio/compat-skin2.js +1 -1
  12. package/dist/default/define/live-audio/neutral-skin.css +8 -5
  13. package/dist/default/define/live-audio/neutral-skin2.js +1 -1
  14. package/dist/default/define/live-audio/scaffold-skin.css +75 -51
  15. package/dist/default/define/live-audio/scaffold-skin2.js +1 -1
  16. package/dist/default/define/live-audio/skin.css +6 -5
  17. package/dist/default/define/live-audio/skin2.js +1 -1
  18. package/dist/default/define/live-video/compat-skin.css +123 -21
  19. package/dist/default/define/live-video/compat-skin2.js +1 -1
  20. package/dist/default/define/live-video/neutral-skin.css +82 -14
  21. package/dist/default/define/live-video/neutral-skin2.js +1 -1
  22. package/dist/default/define/live-video/scaffold-skin.css +107 -26
  23. package/dist/default/define/live-video/scaffold-skin2.js +1 -1
  24. package/dist/default/define/live-video/skin.css +96 -16
  25. package/dist/default/define/live-video/skin2.js +1 -1
  26. package/dist/default/define/media/dailymotion-video.js +11 -0
  27. package/dist/default/define/media/mixcloud-audio.js +11 -0
  28. package/dist/default/define/media/soundcloud-audio.js +11 -0
  29. package/dist/default/define/video/compat-skin.css +123 -21
  30. package/dist/default/define/video/compat-skin2.js +1 -1
  31. package/dist/default/define/video/neutral-skin.css +85 -24
  32. package/dist/default/define/video/neutral-skin2.js +1 -1
  33. package/dist/default/define/video/scaffold-skin.css +107 -26
  34. package/dist/default/define/video/scaffold-skin2.js +1 -1
  35. package/dist/default/define/video/skin.css +94 -16
  36. package/dist/default/define/video/skin2.js +1 -1
  37. package/dist/default/extensions/google-analytics/extension.js +75 -0
  38. package/dist/default/extensions/mux-data/extension.js +7 -2
  39. package/dist/default/internal/skins/compat-audio/register.js +0 -10
  40. package/dist/default/internal/skins/compat-audio/skin.css +116 -65
  41. package/dist/default/internal/skins/compat-audio/template.js +15 -39
  42. package/dist/default/internal/skins/compat-live-audio/register.js +0 -10
  43. package/dist/default/internal/skins/compat-live-audio/skin.css +98 -65
  44. package/dist/default/internal/skins/compat-live-audio/template.js +2 -26
  45. package/dist/default/internal/skins/compat-live-video/skin.css +123 -21
  46. package/dist/default/internal/skins/compat-video/skin.css +123 -21
  47. package/dist/default/internal/skins/default-audio/skin.css +21 -6
  48. package/dist/default/internal/skins/default-live-audio/skin.css +6 -5
  49. package/dist/default/internal/skins/default-live-video/skin.css +96 -16
  50. package/dist/default/internal/skins/default-video/skin.css +94 -16
  51. package/dist/default/internal/skins/neutral-audio/skin.css +25 -16
  52. package/dist/default/internal/skins/neutral-audio/template.js +1 -1
  53. package/dist/default/internal/skins/neutral-live-audio/skin.css +8 -5
  54. package/dist/default/internal/skins/neutral-live-video/skin.css +82 -14
  55. package/dist/default/internal/skins/neutral-video/skin.css +85 -24
  56. package/dist/default/internal/skins/neutral-video/template.js +1 -1
  57. package/dist/default/internal/skins/scaffold-audio/register.js +0 -10
  58. package/dist/default/internal/skins/scaffold-audio/skin.css +93 -51
  59. package/dist/default/internal/skins/scaffold-audio/template.js +15 -39
  60. package/dist/default/internal/skins/scaffold-live-audio/register.js +0 -10
  61. package/dist/default/internal/skins/scaffold-live-audio/skin.css +75 -51
  62. package/dist/default/internal/skins/scaffold-live-audio/template.js +2 -26
  63. package/dist/default/internal/skins/scaffold-live-video/skin.css +107 -26
  64. package/dist/default/internal/skins/scaffold-video/skin.css +107 -26
  65. package/dist/default/media/dailymotion-video/adapter.js +31 -0
  66. package/dist/default/media/hls-audio/adapter.js +1 -1
  67. package/dist/default/media/hls-background-video/adapter.js +8 -1
  68. package/dist/default/media/hls-video/adapter.js +1 -1
  69. package/dist/default/media/mixcloud-audio/adapter.js +16 -0
  70. package/dist/default/media/soundcloud-audio/adapter.js +16 -0
  71. package/dist/default/player/element.js +1 -5
  72. package/dist/default/presets/background/skin.js +2 -2
  73. package/dist/default/presets/skin.js +4 -3
  74. package/dist/default/ui/dialog/element.js +13 -1
  75. package/dist/default/ui/gesture/element.js +3 -0
  76. package/dist/default/ui/media-button-element.js +10 -1
  77. package/dist/default/ui/playback-rate-button/element.js +7 -2
  78. package/dist/default/ui/playback-rate-radio-group/element.js +10 -3
  79. package/dist/default/ui/radio-options/controller.js +18 -5
  80. package/dist/default/ui/slider/element.js +3 -1
  81. package/dist/default/ui/time-slider/element.js +19 -7
  82. package/dist/default/ui/volume-slider/element.js +3 -1
  83. package/dist/default/version.js +1 -1
  84. package/dist/dev/define/audio/compat-skin.css +116 -65
  85. package/dist/dev/define/audio/compat-skin2.js +1 -1
  86. package/dist/dev/define/audio/compat-skin2.js.map +1 -1
  87. package/dist/dev/define/audio/neutral-skin.css +25 -16
  88. package/dist/dev/define/audio/neutral-skin2.js +1 -1
  89. package/dist/dev/define/audio/neutral-skin2.js.map +1 -1
  90. package/dist/dev/define/audio/scaffold-skin.css +93 -51
  91. package/dist/dev/define/audio/scaffold-skin2.js +1 -1
  92. package/dist/dev/define/audio/scaffold-skin2.js.map +1 -1
  93. package/dist/dev/define/audio/skin.css +21 -6
  94. package/dist/dev/define/audio/skin2.js +1 -1
  95. package/dist/dev/define/audio/skin2.js.map +1 -1
  96. package/dist/dev/define/extensions/google-analytics.d.ts +11 -0
  97. package/dist/dev/define/extensions/google-analytics.js +8 -0
  98. package/dist/dev/define/extensions/google-analytics.js.map +1 -0
  99. package/dist/dev/define/live-audio/compat-skin.css +98 -65
  100. package/dist/dev/define/live-audio/compat-skin2.js +1 -1
  101. package/dist/dev/define/live-audio/compat-skin2.js.map +1 -1
  102. package/dist/dev/define/live-audio/neutral-skin.css +8 -5
  103. package/dist/dev/define/live-audio/neutral-skin2.js +1 -1
  104. package/dist/dev/define/live-audio/neutral-skin2.js.map +1 -1
  105. package/dist/dev/define/live-audio/scaffold-skin.css +75 -51
  106. package/dist/dev/define/live-audio/scaffold-skin2.js +1 -1
  107. package/dist/dev/define/live-audio/scaffold-skin2.js.map +1 -1
  108. package/dist/dev/define/live-audio/skin.css +6 -5
  109. package/dist/dev/define/live-audio/skin2.js +1 -1
  110. package/dist/dev/define/live-audio/skin2.js.map +1 -1
  111. package/dist/dev/define/live-video/compat-skin.css +123 -21
  112. package/dist/dev/define/live-video/compat-skin2.js +1 -1
  113. package/dist/dev/define/live-video/compat-skin2.js.map +1 -1
  114. package/dist/dev/define/live-video/neutral-skin.css +82 -14
  115. package/dist/dev/define/live-video/neutral-skin2.js +1 -1
  116. package/dist/dev/define/live-video/neutral-skin2.js.map +1 -1
  117. package/dist/dev/define/live-video/scaffold-skin.css +107 -26
  118. package/dist/dev/define/live-video/scaffold-skin2.js +1 -1
  119. package/dist/dev/define/live-video/scaffold-skin2.js.map +1 -1
  120. package/dist/dev/define/live-video/skin.css +96 -16
  121. package/dist/dev/define/live-video/skin2.js +1 -1
  122. package/dist/dev/define/live-video/skin2.js.map +1 -1
  123. package/dist/dev/define/media/dailymotion-video.d.ts +15 -0
  124. package/dist/dev/define/media/dailymotion-video.js +13 -0
  125. package/dist/dev/define/media/dailymotion-video.js.map +1 -0
  126. package/dist/dev/define/media/mixcloud-audio.d.ts +15 -0
  127. package/dist/dev/define/media/mixcloud-audio.js +13 -0
  128. package/dist/dev/define/media/mixcloud-audio.js.map +1 -0
  129. package/dist/dev/define/media/soundcloud-audio.d.ts +15 -0
  130. package/dist/dev/define/media/soundcloud-audio.js +13 -0
  131. package/dist/dev/define/media/soundcloud-audio.js.map +1 -0
  132. package/dist/dev/define/video/compat-skin.css +123 -21
  133. package/dist/dev/define/video/compat-skin2.js +1 -1
  134. package/dist/dev/define/video/compat-skin2.js.map +1 -1
  135. package/dist/dev/define/video/neutral-skin.css +85 -24
  136. package/dist/dev/define/video/neutral-skin2.js +1 -1
  137. package/dist/dev/define/video/neutral-skin2.js.map +1 -1
  138. package/dist/dev/define/video/scaffold-skin.css +107 -26
  139. package/dist/dev/define/video/scaffold-skin2.js +1 -1
  140. package/dist/dev/define/video/scaffold-skin2.js.map +1 -1
  141. package/dist/dev/define/video/skin.css +94 -16
  142. package/dist/dev/define/video/skin2.js +1 -1
  143. package/dist/dev/define/video/skin2.js.map +1 -1
  144. package/dist/dev/extensions/google-analytics/extension.d.ts +52 -0
  145. package/dist/dev/extensions/google-analytics/extension.js +77 -0
  146. package/dist/dev/extensions/google-analytics/extension.js.map +1 -0
  147. package/dist/dev/extensions/google-analytics/index.d.ts +2 -0
  148. package/dist/dev/extensions/mux-data/extension.d.ts +4 -1
  149. package/dist/dev/extensions/mux-data/extension.js +7 -2
  150. package/dist/dev/extensions/mux-data/extension.js.map +1 -1
  151. package/dist/dev/index.d.ts +3 -3
  152. package/dist/dev/internal/skins/compat-audio/register.d.ts +3 -5
  153. package/dist/dev/internal/skins/compat-audio/register.js +0 -10
  154. package/dist/dev/internal/skins/compat-audio/register.js.map +1 -1
  155. package/dist/dev/internal/skins/compat-audio/skin.css +116 -65
  156. package/dist/dev/internal/skins/compat-audio/template.js +15 -39
  157. package/dist/dev/internal/skins/compat-audio/template.js.map +1 -1
  158. package/dist/dev/internal/skins/compat-live-audio/register.d.ts +0 -2
  159. package/dist/dev/internal/skins/compat-live-audio/register.js +0 -10
  160. package/dist/dev/internal/skins/compat-live-audio/register.js.map +1 -1
  161. package/dist/dev/internal/skins/compat-live-audio/skin.css +98 -65
  162. package/dist/dev/internal/skins/compat-live-audio/template.js +2 -26
  163. package/dist/dev/internal/skins/compat-live-audio/template.js.map +1 -1
  164. package/dist/dev/internal/skins/compat-live-video/register.d.ts +2 -2
  165. package/dist/dev/internal/skins/compat-live-video/skin.css +123 -21
  166. package/dist/dev/internal/skins/compat-video/register.d.ts +5 -5
  167. package/dist/dev/internal/skins/compat-video/skin.css +123 -21
  168. package/dist/dev/internal/skins/default-audio/register.d.ts +1 -1
  169. package/dist/dev/internal/skins/default-audio/skin.css +21 -6
  170. package/dist/dev/internal/skins/default-live-audio/skin.css +6 -5
  171. package/dist/dev/internal/skins/default-live-video/register.d.ts +2 -2
  172. package/dist/dev/internal/skins/default-live-video/skin.css +96 -16
  173. package/dist/dev/internal/skins/default-video/register.d.ts +3 -3
  174. package/dist/dev/internal/skins/default-video/skin.css +94 -16
  175. package/dist/dev/internal/skins/neutral-audio/register.d.ts +3 -3
  176. package/dist/dev/internal/skins/neutral-audio/skin.css +25 -16
  177. package/dist/dev/internal/skins/neutral-audio/template.js +1 -1
  178. package/dist/dev/internal/skins/neutral-audio/template.js.map +1 -1
  179. package/dist/dev/internal/skins/neutral-live-audio/skin.css +8 -5
  180. package/dist/dev/internal/skins/neutral-live-video/register.d.ts +2 -2
  181. package/dist/dev/internal/skins/neutral-live-video/skin.css +82 -14
  182. package/dist/dev/internal/skins/neutral-video/register.d.ts +5 -5
  183. package/dist/dev/internal/skins/neutral-video/skin.css +85 -24
  184. package/dist/dev/internal/skins/neutral-video/template.js +1 -1
  185. package/dist/dev/internal/skins/neutral-video/template.js.map +1 -1
  186. package/dist/dev/internal/skins/scaffold-audio/register.d.ts +3 -5
  187. package/dist/dev/internal/skins/scaffold-audio/register.js +0 -10
  188. package/dist/dev/internal/skins/scaffold-audio/register.js.map +1 -1
  189. package/dist/dev/internal/skins/scaffold-audio/skin.css +93 -51
  190. package/dist/dev/internal/skins/scaffold-audio/template.js +15 -39
  191. package/dist/dev/internal/skins/scaffold-audio/template.js.map +1 -1
  192. package/dist/dev/internal/skins/scaffold-live-audio/register.d.ts +0 -2
  193. package/dist/dev/internal/skins/scaffold-live-audio/register.js +0 -10
  194. package/dist/dev/internal/skins/scaffold-live-audio/register.js.map +1 -1
  195. package/dist/dev/internal/skins/scaffold-live-audio/skin.css +75 -51
  196. package/dist/dev/internal/skins/scaffold-live-audio/template.js +2 -26
  197. package/dist/dev/internal/skins/scaffold-live-audio/template.js.map +1 -1
  198. package/dist/dev/internal/skins/scaffold-live-video/register.d.ts +2 -2
  199. package/dist/dev/internal/skins/scaffold-live-video/skin.css +107 -26
  200. package/dist/dev/internal/skins/scaffold-video/register.d.ts +5 -5
  201. package/dist/dev/internal/skins/scaffold-video/skin.css +107 -26
  202. package/dist/dev/media/dailymotion-video/adapter.d.ts +17 -0
  203. package/dist/dev/media/dailymotion-video/adapter.js +47 -0
  204. package/dist/dev/media/dailymotion-video/adapter.js.map +1 -0
  205. package/dist/dev/media/dailymotion-video/index.d.ts +2 -0
  206. package/dist/dev/media/hls-audio/adapter.d.ts +1 -1
  207. package/dist/dev/media/hls-audio/adapter.js +1 -1
  208. package/dist/dev/media/hls-audio/adapter.js.map +1 -1
  209. package/dist/dev/media/hls-background-video/adapter.d.ts +6 -1
  210. package/dist/dev/media/hls-background-video/adapter.js +8 -1
  211. package/dist/dev/media/hls-background-video/adapter.js.map +1 -1
  212. package/dist/dev/media/hls-video/adapter.d.ts +1 -1
  213. package/dist/dev/media/hls-video/adapter.js +1 -1
  214. package/dist/dev/media/hls-video/adapter.js.map +1 -1
  215. package/dist/dev/media/mixcloud-audio/adapter.d.ts +11 -0
  216. package/dist/dev/media/mixcloud-audio/adapter.js +52 -0
  217. package/dist/dev/media/mixcloud-audio/adapter.js.map +1 -0
  218. package/dist/dev/media/mixcloud-audio/index.d.ts +2 -0
  219. package/dist/dev/media/soundcloud-audio/adapter.d.ts +11 -0
  220. package/dist/dev/media/soundcloud-audio/adapter.js +53 -0
  221. package/dist/dev/media/soundcloud-audio/adapter.js.map +1 -0
  222. package/dist/dev/media/soundcloud-audio/index.d.ts +2 -0
  223. package/dist/dev/player/element.js +1 -5
  224. package/dist/dev/player/element.js.map +1 -1
  225. package/dist/dev/presets/background/skin.d.ts +2 -1
  226. package/dist/dev/presets/background/skin.js +2 -2
  227. package/dist/dev/presets/background/skin.js.map +1 -1
  228. package/dist/dev/presets/skin.d.ts +4 -3
  229. package/dist/dev/presets/skin.js +4 -3
  230. package/dist/dev/presets/skin.js.map +1 -1
  231. package/dist/dev/ui/dialog/element.js +13 -1
  232. package/dist/dev/ui/dialog/element.js.map +1 -1
  233. package/dist/dev/ui/gesture/element.js +3 -0
  234. package/dist/dev/ui/gesture/element.js.map +1 -1
  235. package/dist/dev/ui/media-button-element.d.ts +9 -0
  236. package/dist/dev/ui/media-button-element.js +10 -1
  237. package/dist/dev/ui/media-button-element.js.map +1 -1
  238. package/dist/dev/ui/play-button/element.d.ts +2 -0
  239. package/dist/dev/ui/playback-rate-button/element.d.ts +9 -1
  240. package/dist/dev/ui/playback-rate-button/element.js +7 -2
  241. package/dist/dev/ui/playback-rate-button/element.js.map +1 -1
  242. package/dist/dev/ui/playback-rate-radio-group/element.d.ts +4 -0
  243. package/dist/dev/ui/playback-rate-radio-group/element.js +10 -3
  244. package/dist/dev/ui/playback-rate-radio-group/element.js.map +1 -1
  245. package/dist/dev/ui/radio-options/controller.js +18 -5
  246. package/dist/dev/ui/radio-options/controller.js.map +1 -1
  247. package/dist/dev/ui/slider/element.js +3 -1
  248. package/dist/dev/ui/slider/element.js.map +1 -1
  249. package/dist/dev/ui/time-slider/element.js +19 -7
  250. package/dist/dev/ui/time-slider/element.js.map +1 -1
  251. package/dist/dev/ui/volume-slider/element.js +3 -1
  252. package/dist/dev/ui/volume-slider/element.js.map +1 -1
  253. package/dist/dev/version.js +1 -1
  254. package/docs/README.md +1 -1
  255. package/docs/guides/autoplay.md +1 -1
  256. package/docs/guides/browser-support.md +13 -11
  257. package/docs/guides/build-your-own-skin.md +383 -0
  258. package/docs/guides/cdn.md +10 -9
  259. package/docs/guides/content-security-policy.md +3 -0
  260. package/docs/guides/customize-skins.md +6 -3
  261. package/docs/guides/features.md +1 -1
  262. package/docs/guides/google-analytics.md +199 -0
  263. package/docs/guides/installation-cdn.md +4 -4
  264. package/docs/guides/installation-shadcn.md +3 -3
  265. package/docs/guides/installation-svelte.md +4 -4
  266. package/docs/guides/installation-vue.md +4 -4
  267. package/docs/guides/installation.md +4 -4
  268. package/docs/guides/internationalization.md +5 -5
  269. package/docs/guides/keyboard-shortcuts.md +1 -1
  270. package/docs/guides/llms.txt +3 -1
  271. package/docs/guides/media-sources.md +3 -0
  272. package/docs/guides/migrate-from-media-chrome.md +4 -4
  273. package/docs/guides/migrate-from-mux-player.md +9 -9
  274. package/docs/guides/migrate-from-plyr.md +8 -8
  275. package/docs/guides/migrate-from-video-js-8.md +9 -10
  276. package/docs/guides/migrate-from-vidstack.md +9 -9
  277. package/docs/guides/mux-data.md +22 -0
  278. package/docs/guides/presets.md +1 -1
  279. package/docs/guides/self-hosting.md +9 -6
  280. package/docs/guides/skins.md +17 -1
  281. package/docs/guides/ui-components.md +1 -1
  282. package/docs/llms.txt +12 -6
  283. package/docs/reference/api/feature-playback-rate.md +54 -3
  284. package/docs/reference/api/feature-playback.md +5 -1
  285. package/docs/reference/api/html-create-player.md +2 -2
  286. package/docs/reference/api/llms.txt +1 -1
  287. package/docs/reference/api/media-capabilities.md +1 -1
  288. package/docs/reference/api/player-store.md +6 -3
  289. package/docs/reference/api/preset-audio.md +13 -5
  290. package/docs/reference/api/preset-background.md +2 -2
  291. package/docs/reference/api/preset-live-audio.md +13 -5
  292. package/docs/reference/api/preset-live-video.md +13 -5
  293. package/docs/reference/api/preset-video.md +13 -5
  294. package/docs/reference/api/resolve-adapter-type.md +4 -1
  295. package/docs/reference/api/ui-element.md +2 -2
  296. package/docs/reference/components/alert-dialog.md +21 -16
  297. package/docs/reference/components/audio-compat-skin.md +8 -2
  298. package/docs/reference/components/audio-neutral-skin.md +2 -2
  299. package/docs/reference/components/audio-scaffold-skin.md +2 -2
  300. package/docs/reference/components/audio-skin.md +2 -2
  301. package/docs/reference/components/audio-track-radio-group.md +2 -2
  302. package/docs/reference/components/background-video-skin.md +1 -1
  303. package/docs/reference/components/background-video.md +1 -1
  304. package/docs/reference/components/buffering-indicator.md +2 -2
  305. package/docs/reference/components/captions-radio-group.md +2 -2
  306. package/docs/reference/components/cloudflare-video.md +1 -1
  307. package/docs/reference/components/dailymotion-video.md +192 -0
  308. package/docs/reference/components/dash-video.md +2 -1
  309. package/docs/reference/components/dialog.md +49 -22
  310. package/docs/reference/components/error-dialog.md +13 -10
  311. package/docs/reference/components/gesture.md +2 -2
  312. package/docs/reference/components/google-analytics.md +51 -0
  313. package/docs/reference/components/google-cast.md +1 -1
  314. package/docs/reference/components/hls-audio.md +2 -48
  315. package/docs/reference/components/hls-background-video.md +4 -1
  316. package/docs/reference/components/hls-video.md +2 -66
  317. package/docs/reference/components/hlsjs-video.md +3 -1
  318. package/docs/reference/components/hotkey.md +2 -2
  319. package/docs/reference/components/live-audio-compat-skin.md +8 -2
  320. package/docs/reference/components/live-audio-neutral-skin.md +2 -2
  321. package/docs/reference/components/live-audio-scaffold-skin.md +2 -2
  322. package/docs/reference/components/live-audio-skin.md +2 -2
  323. package/docs/reference/components/live-video-compat-skin.md +8 -2
  324. package/docs/reference/components/live-video-neutral-skin.md +2 -2
  325. package/docs/reference/components/live-video-scaffold-skin.md +2 -2
  326. package/docs/reference/components/live-video-skin.md +2 -2
  327. package/docs/reference/components/llms.txt +9 -5
  328. package/docs/reference/components/mixcloud-audio.md +203 -0
  329. package/docs/reference/components/mux-audio.md +3 -1
  330. package/docs/reference/components/mux-background-video.md +1 -1
  331. package/docs/reference/components/mux-data.md +22 -2
  332. package/docs/reference/components/mux-video.md +3 -1
  333. package/docs/reference/components/native-hls-video.md +2 -1
  334. package/docs/reference/components/play-button.md +7 -5
  335. package/docs/reference/components/playback-rate-button.md +26 -7
  336. package/docs/reference/components/playback-rate-radio-group.md +7 -4
  337. package/docs/reference/components/player.md +3 -3
  338. package/docs/reference/components/playerjs-video.md +1 -1
  339. package/docs/reference/components/quality-radio-group.md +2 -2
  340. package/docs/reference/components/shaka-video.md +2 -1
  341. package/docs/reference/components/slider.md +2 -0
  342. package/docs/reference/components/soundcloud-audio.md +227 -0
  343. package/docs/reference/components/spotify-audio.md +2 -2
  344. package/docs/reference/components/tiktok-video.md +2 -2
  345. package/docs/reference/components/time-slider.md +7 -5
  346. package/docs/reference/components/twitch-video.md +2 -3
  347. package/docs/reference/components/video-compat-skin.md +8 -2
  348. package/docs/reference/components/video-neutral-skin.md +2 -2
  349. package/docs/reference/components/video-scaffold-skin.md +2 -2
  350. package/docs/reference/components/video-skin.md +2 -2
  351. package/docs/reference/components/vimeo-video.md +2 -1
  352. package/docs/reference/components/volume-slider.md +8 -6
  353. package/docs/reference/components/wistia-video.md +1 -1
  354. package/docs/reference/components/youtube-video.md +2 -1
  355. package/package.json +62 -43
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","names":[],"sources":["../../../../src/ui/volume-slider/element.ts"],"sourcesContent":["import { VolumeSliderCore, VolumeSliderDataAttrs, type VolumeSliderProps } from '@videojs/core';\nimport {\n applyElementProps,\n applyStateDataAttrs,\n createSlider,\n createWheelStep,\n getSliderCSSVars,\n logMissingFeature,\n type SliderApi,\n selectControls,\n selectVolume,\n} from '@videojs/core/dom';\nimport { type Text, translateText } from '@videojs/core/i18n';\nimport type { PropertyDeclarationMap, PropertyValues } from '@videojs/element';\nimport { ContextProvider } from '@videojs/element/context';\nimport { applyStyles } from '@videojs/utils/dom';\n\nimport { i18nContext } from '../../i18n/context';\nimport { I18nController } from '../../i18n/controller';\nimport { playerContext } from '../../player/context';\nimport { PlayerController } from '../../player/controller';\nimport { sliderContext } from '../slider/context';\nimport { UIElement } from '../ui-element';\n\n/**\n * @fires drag-start - Fired when a pointer drag starts.\n * @fires drag-end - Fired when a pointer drag ends.\n */\nexport class VolumeSliderElement extends UIElement {\n static readonly tagName = 'media-volume-slider';\n\n static override properties = {\n label: { type: String },\n step: { type: Number },\n largeStep: { type: Number, attribute: 'large-step' },\n wheelStep: { type: Number, attribute: 'wheel-step' },\n orientation: { type: String },\n disabled: { type: Boolean },\n thumbAlignment: { type: String, attribute: 'thumb-alignment' },\n } satisfies PropertyDeclarationMap<Exclude<keyof VolumeSliderProps, 'value' | 'min' | 'max'>>;\n\n label: Text | string = '';\n step = VolumeSliderCore.defaultProps.step;\n largeStep = VolumeSliderCore.defaultProps.largeStep;\n wheelStep = VolumeSliderCore.defaultProps.wheelStep;\n orientation = VolumeSliderCore.defaultProps.orientation;\n disabled = VolumeSliderCore.defaultProps.disabled;\n thumbAlignment = VolumeSliderCore.defaultProps.thumbAlignment;\n\n readonly #core = new VolumeSliderCore();\n readonly #controlsState = new PlayerController(this, playerContext, selectControls);\n readonly #provider = new ContextProvider(this, { context: sliderContext });\n readonly #volumeState = new PlayerController(this, playerContext, selectVolume);\n readonly #i18n = new I18nController(this, i18nContext);\n\n #slider: SliderApi | null = null;\n #disconnect: AbortController | null = null;\n #releaseControlsLock: (() => void) | null = null;\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n if (this.destroyed) return;\n\n this.#disconnect = new AbortController();\n const signal = this.#disconnect.signal;\n\n const isDisabled = () => {\n const volume = this.#volumeState.value;\n\n return this.disabled || !volume || volume.volumeAvailability !== 'available';\n };\n\n const getPercent = () => (this.#volumeState.value?.volume ?? 0) * 100;\n const getStepPercent = () => this.#core.getStepPercent();\n const setVolume = (percent: number) => this.#setVolume(percent);\n\n this.#slider = createSlider({\n getElement: () => this,\n getThumbElement: () => this.querySelector<HTMLElement>('media-slider-thumb'),\n getOrientation: () => this.orientation,\n isDisabled,\n getPercent,\n getStepPercent,\n getLargeStepPercent: () => this.#core.getLargeStepPercent(),\n onValueChange: setVolume,\n onValueCommit: setVolume,\n onPressStart: () => {\n this.#releaseControlsLock ??= this.#controlsState.value?.requestControlsLock() ?? null;\n },\n onPressEnd: () => this.#releaseControlsVisibilityLock(),\n onDragStart: () => {\n this.dispatchEvent(new CustomEvent('drag-start', { bubbles: true }));\n },\n onDragEnd: () => {\n this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true }));\n },\n adjustPercent: (raw, thumbSize, trackSize) => this.#core.adjustPercentForAlignment(raw, thumbSize, trackSize),\n onResize: () => this.requestUpdate(),\n });\n\n const wheelProps = createWheelStep({\n isDisabled,\n getPercent,\n getStepPercent: () => this.#core.getWheelStepPercent(),\n onValueChange: (percent) => this.#volumeState.value?.setVolume(this.#core.rawValueFromPercent(percent) / 100),\n });\n\n applyElementProps(this, this.#slider.rootProps, { signal });\n applyElementProps(this, wheelProps, { signal });\n applyStyles(this, this.#slider.rootStyle);\n this.#slider.input.subscribe(() => this.requestUpdate(), { signal });\n\n if (__DEV__ && !this.#volumeState.value) {\n logMissingFeature(this.localName, this.#volumeState.displayName!);\n }\n }\n\n override disconnectedCallback(): void {\n this.#releaseControlsVisibilityLock();\n super.disconnectedCallback();\n this.#disconnect?.abort();\n this.#disconnect = null;\n }\n\n override destroyCallback(): void {\n this.#releaseControlsVisibilityLock();\n this.#slider?.destroy();\n super.destroyCallback();\n }\n\n #releaseControlsVisibilityLock(): void {\n this.#releaseControlsLock?.();\n this.#releaseControlsLock = null;\n }\n\n protected override willUpdate(_changed: PropertyValues): void {\n super.willUpdate(_changed);\n this.#core.setProps(this);\n this.#core.setFormatLocale(this.#i18n.locale);\n }\n\n protected override update(_changed: PropertyValues): void {\n super.update(_changed);\n\n if (!this.#slider) return;\n\n const media = this.#volumeState.value;\n if (!media) return;\n\n this.#core.setInput(this.#slider.input.current);\n this.#core.setMedia(media);\n const state = this.#core.getState();\n\n const cssVars = getSliderCSSVars(this.#slider.adjustForAlignment(state));\n const thumbAttrs = this.#core.getAttrs(state);\n\n applyStyles(this, cssVars);\n\n // Apply data attributes to root.\n applyStateDataAttrs(this, state, VolumeSliderDataAttrs);\n applyElementProps(this, { hidden: state.hidden ? '' : undefined });\n\n // Provide context to child elements.\n this.#provider.setValue({\n state,\n stateAttrMap: VolumeSliderDataAttrs,\n pointerValue: this.#core.valueFromPercent(state.pointerPercent),\n thumbAttrs: {\n ...thumbAttrs,\n 'aria-label': translateText(thumbAttrs['aria-label'], this.#i18n.value),\n 'aria-valuetext': translateText(\n thumbAttrs['aria-valuetext'],\n this.#i18n.value,\n this.#core.getValueTextParams(state)\n ),\n },\n thumbProps: this.#slider.thumbProps,\n formatValue: (value) => `${Math.round(value)}%`,\n });\n }\n\n #setVolume(percent: number): void {\n const media = this.#volumeState.value;\n\n media?.setVolume(this.#core.valueFromPercent(percent) / 100);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"element.js","names":[],"sources":["../../../../src/ui/volume-slider/element.ts"],"sourcesContent":["import { VolumeSliderCore, VolumeSliderDataAttrs, type VolumeSliderProps } from '@videojs/core';\nimport {\n applyElementProps,\n applyStateDataAttrs,\n createSlider,\n createWheelStep,\n getSliderCSSVars,\n logMissingFeature,\n type SliderApi,\n selectControls,\n selectVolume,\n} from '@videojs/core/dom';\nimport { type Text, translateText } from '@videojs/core/i18n';\nimport type { PropertyDeclarationMap, PropertyValues } from '@videojs/element';\nimport { ContextProvider } from '@videojs/element/context';\nimport { applyStyles } from '@videojs/utils/dom';\n\nimport { i18nContext } from '../../i18n/context';\nimport { I18nController } from '../../i18n/controller';\nimport { playerContext } from '../../player/context';\nimport { PlayerController } from '../../player/controller';\nimport { sliderContext } from '../slider/context';\nimport { UIElement } from '../ui-element';\n\n/**\n * @fires drag-start - Fired when a pointer drag starts.\n * @fires drag-end - Fired when a pointer drag ends.\n */\nexport class VolumeSliderElement extends UIElement {\n static readonly tagName = 'media-volume-slider';\n\n static override properties = {\n label: { type: String },\n step: { type: Number },\n largeStep: { type: Number, attribute: 'large-step' },\n wheelStep: { type: Number, attribute: 'wheel-step' },\n orientation: { type: String },\n disabled: { type: Boolean },\n thumbAlignment: { type: String, attribute: 'thumb-alignment' },\n } satisfies PropertyDeclarationMap<Exclude<keyof VolumeSliderProps, 'value' | 'min' | 'max'>>;\n\n label: Text | string = '';\n step = VolumeSliderCore.defaultProps.step;\n largeStep = VolumeSliderCore.defaultProps.largeStep;\n wheelStep = VolumeSliderCore.defaultProps.wheelStep;\n orientation = VolumeSliderCore.defaultProps.orientation;\n disabled = VolumeSliderCore.defaultProps.disabled;\n thumbAlignment = VolumeSliderCore.defaultProps.thumbAlignment;\n\n readonly #core = new VolumeSliderCore();\n readonly #controlsState = new PlayerController(this, playerContext, selectControls);\n readonly #provider = new ContextProvider(this, { context: sliderContext });\n readonly #volumeState = new PlayerController(this, playerContext, selectVolume);\n readonly #i18n = new I18nController(this, i18nContext);\n\n #slider: SliderApi | null = null;\n #disconnect: AbortController | null = null;\n #releaseControlsLock: (() => void) | null = null;\n\n override connectedCallback(): void {\n super.connectedCallback();\n\n if (this.destroyed) return;\n\n this.#disconnect = new AbortController();\n const signal = this.#disconnect.signal;\n\n const isDisabled = () => {\n const volume = this.#volumeState.value;\n\n return this.disabled || !volume || volume.volumeAvailability !== 'available';\n };\n\n const getPercent = () => (this.#volumeState.value?.volume ?? 0) * 100;\n const getStepPercent = () => this.#core.getStepPercent();\n const setVolume = (percent: number) => this.#setVolume(percent);\n\n this.#slider = createSlider({\n getElement: () => this,\n getThumbElement: () => this.querySelector<HTMLElement>('media-slider-thumb'),\n getOrientation: () => this.orientation,\n isDisabled,\n getPercent,\n getStepPercent,\n getLargeStepPercent: () => this.#core.getLargeStepPercent(),\n onValueChange: setVolume,\n onValueCommit: setVolume,\n onPressStart: () => {\n this.#releaseControlsLock ??= this.#controlsState.value?.requestControlsLock() ?? null;\n },\n onPressEnd: () => this.#releaseControlsVisibilityLock(),\n onDragStart: () => {\n this.dispatchEvent(new CustomEvent('drag-start', { bubbles: true }));\n },\n onDragEnd: () => {\n this.dispatchEvent(new CustomEvent('drag-end', { bubbles: true }));\n },\n adjustPercent: (raw, thumbSize, trackSize) => this.#core.adjustPercentForAlignment(raw, thumbSize, trackSize),\n // Only edge alignment depends on the slider's size.\n onResize: () => {\n if (this.thumbAlignment === 'edge') this.requestUpdate();\n },\n });\n\n const wheelProps = createWheelStep({\n isDisabled,\n getPercent,\n getStepPercent: () => this.#core.getWheelStepPercent(),\n onValueChange: (percent) => this.#volumeState.value?.setVolume(this.#core.rawValueFromPercent(percent) / 100),\n });\n\n applyElementProps(this, this.#slider.rootProps, { signal });\n applyElementProps(this, wheelProps, { signal });\n applyStyles(this, this.#slider.rootStyle);\n this.#slider.input.subscribe(() => this.requestUpdate(), { signal });\n\n if (__DEV__ && !this.#volumeState.value) {\n logMissingFeature(this.localName, this.#volumeState.displayName!);\n }\n }\n\n override disconnectedCallback(): void {\n this.#releaseControlsVisibilityLock();\n super.disconnectedCallback();\n this.#disconnect?.abort();\n this.#disconnect = null;\n }\n\n override destroyCallback(): void {\n this.#releaseControlsVisibilityLock();\n this.#slider?.destroy();\n super.destroyCallback();\n }\n\n #releaseControlsVisibilityLock(): void {\n this.#releaseControlsLock?.();\n this.#releaseControlsLock = null;\n }\n\n protected override willUpdate(_changed: PropertyValues): void {\n super.willUpdate(_changed);\n this.#core.setProps(this);\n this.#core.setFormatLocale(this.#i18n.locale);\n }\n\n protected override update(_changed: PropertyValues): void {\n super.update(_changed);\n\n if (!this.#slider) return;\n\n const media = this.#volumeState.value;\n if (!media) return;\n\n this.#core.setInput(this.#slider.input.current);\n this.#core.setMedia(media);\n const state = this.#core.getState();\n\n const cssVars = getSliderCSSVars(this.#slider.adjustForAlignment(state));\n const thumbAttrs = this.#core.getAttrs(state);\n\n applyStyles(this, cssVars);\n\n // Apply data attributes to root.\n applyStateDataAttrs(this, state, VolumeSliderDataAttrs);\n applyElementProps(this, { hidden: state.hidden ? '' : undefined });\n\n // Provide context to child elements.\n this.#provider.setValue({\n state,\n stateAttrMap: VolumeSliderDataAttrs,\n pointerValue: this.#core.valueFromPercent(state.pointerPercent),\n thumbAttrs: {\n ...thumbAttrs,\n 'aria-label': translateText(thumbAttrs['aria-label'], this.#i18n.value),\n 'aria-valuetext': translateText(\n thumbAttrs['aria-valuetext'],\n this.#i18n.value,\n this.#core.getValueTextParams(state)\n ),\n },\n thumbProps: this.#slider.thumbProps,\n formatValue: (value) => `${Math.round(value)}%`,\n });\n }\n\n #setVolume(percent: number): void {\n const media = this.#volumeState.value;\n\n media?.setVolume(this.#core.valueFromPercent(percent) / 100);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  //#region src/version.ts
2
2
  const readVersion = () => {
3
3
  try {
4
- return "10.1.0";
4
+ return "10.3.0";
5
5
  } catch {}
6
6
  return "UNKNOWN";
7
7
  };
package/docs/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @videojs/html documentation
2
2
 
3
- Bundled markdown documentation for `@videojs/html` v10.1.0.
3
+ Bundled markdown documentation for `@videojs/html` v10.3.0.
4
4
 
5
5
  Start at [`./llms.txt`](./llms.txt) — it's the structured index of every page in this directory.
6
6
 
@@ -87,7 +87,7 @@ import '@videojs/html/ui/play-button';
87
87
 
88
88
  The `autoplay`, `muted`, and `playsinline` attributes on `<video>` and similar media components pass through to the underlying media element. The media element applies the attributes it supports and requests playback as soon as it has enough data.
89
89
 
90
- The [playback feature](../reference/api/feature-playback.md) reflects the result in player state. When autoplay succeeds, `paused` becomes `false` and `started` becomes `true`. When the browser blocks autoplay, the player stays `paused: true` with `started: false`, and the [Play button](../reference/components/play-button.md) renders its play state. There is no separate autoplay failure event: a blocked autoplay looks like media that never started.
90
+ The [playback feature](../reference/api/feature-playback.md) reflects the result in player state. While the media is trying to autoplay, `autoplaying` is `true`: the [Play button](../reference/components/play-button.md) shows pause, so clicking it cancels the autoplay, and the [Buffering indicator](../reference/components/buffering-indicator.md) appears after its delay. When autoplay succeeds, `paused` becomes `false` and `started` becomes `true`. When the browser blocks autoplay, the player stays `paused: true` with `started: false`, and the [Play button](../reference/components/play-button.md) renders its play state. There is no separate autoplay failure event: a blocked autoplay looks like media that never started.
91
91
 
92
92
  Calling `play()` from player state returns the native promise from the media element. When the browser blocks playback, that promise rejects with a `NotAllowedError`.
93
93
 
@@ -12,10 +12,12 @@ Browsers and rendering environments supported by Video.js 10, and how the packag
12
12
  | Safari | 16.4 and later |
13
13
  | Safari on iOS | 16.4 and later |
14
14
 
15
- These versions account for 91.5% of global web usage according to [caniuse-lite](https://github.com/browserslist/caniuse-lite) 1.0.30001810, and this figure updates when we update that data.
15
+ These versions account for 92.5% of global web usage according to [caniuse-lite](https://github.com/browserslist/caniuse-lite) 1.0.30001815, and this figure updates when we update that data.
16
16
 
17
17
  We test in and fix bugs for these versions. Each minimum comes from a feature with no fallback: `:has()` sets Firefox, [`color-mix()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-mix) and [`oklch()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) set Chrome and Edge (Chrome 111, Firefox 113, Safari 16.2), and the JavaScript sets Safari, because it uses ES2022 syntax and [`adoptedStyleSheets`](https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets). Newer JavaScript APIs are feature-detected.
18
18
 
19
+ These versions apply to every skin. The [Compat and Scaffold skins](./skins.md#choose-a-skin) avoid newer visual effects such as backdrop blur, anchor-positioned menu highlights, and squircle corners, so they look the same in browsers that skip them. They still need the CSS features below.
20
+
19
21
  ## CSS requirements
20
22
 
21
23
  The packaged skins are generated CSS, and the build lowers them for the browsers above. It flattens CSS nesting, adds vendor prefixes such as `-webkit-backdrop-filter`, and writes each skin’s scope as a [`:where()`](https://developer.mozilla.org/en-US/docs/Web/CSS/:where) selector, which adds no specificity, rather than [`@scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope).
@@ -36,16 +38,16 @@ The table reads the first fully supporting version of each feature from [caniuse
36
38
 
37
39
  | Feature | Chrome and Edge | Firefox | Safari and iOS | Global support |
38
40
  | --- | --- | --- | --- | --- |
39
- | [`@scope`](https://caniuse.com/css-cascade-scope) Has a fallback. Packaged skins use :where() selectors instead; registry CSS skins need it. | 118 | 146 | 17.4 | 90.0% |
40
- | [`@layer`](https://caniuse.com/css-cascade-layers) Required. No component styling. | 99 | 97 | 15.4 | 95.3% |
41
- | [`:has()`](https://caniuse.com/css-has) Required. Menu and slider focus states are lost. | 105 | 121 | 15.4 | 94.1% |
42
- | [`@container`](https://caniuse.com/css-container-queries) Required. Controls do not adapt to player width. | 106 | 110 | 16 | 94.0% |
43
- | [`Media query range syntax`](https://caniuse.com/css-media-range-syntax) Required. Large-screen sizing is lost. | 104 | 63 | 16.4 | 94.0% |
44
- | [`:dir()`](https://caniuse.com/css-dir-pseudo) Has a fallback. Right-to-left layout follows the dir attribute instead. | 120 | 17 | 16.4 | 91.4% |
45
- | [`Relative color syntax`](https://caniuse.com/css-relative-colors) Degrades. Control scrims fall back to color-mix(), and subtle shadows are lost. | 131 | 133 | 18 | 85.7% |
46
- | [`scrollbar-color and scrollbar-width`](https://caniuse.com/css-scrollbar) Degrades. Menus show default scrollbars. | 121 | 64 | 26.2 | 85.6% |
47
- | [`backdrop-filter`](https://caniuse.com/css-backdrop-filter) Degrades. Surfaces lose their blur. | 76 / 17 | 103 | 9 | 95.7% |
48
- | [`Anchor positioning`](https://caniuse.com/css-anchor-positioning) Has a fallback. Popups are positioned by JavaScript. | 125 | 147 | 26 | 84.1% |
41
+ | [`@scope`](https://caniuse.com/css-cascade-scope) Has a fallback. Packaged skins use :where() selectors instead; registry CSS skins need it. | 118 | 146 | 17.4 | 91.7% |
42
+ | [`@layer`](https://caniuse.com/css-cascade-layers) Required. No component styling. | 99 | 97 | 15.4 | 96.0% |
43
+ | [`:has()`](https://caniuse.com/css-has) Required. Menu and slider focus states are lost. | 105 | 121 | 15.4 | 94.8% |
44
+ | [`@container`](https://caniuse.com/css-container-queries) Required. Controls do not adapt to player width. | 106 | 110 | 16 | 94.8% |
45
+ | [`Media query range syntax`](https://caniuse.com/css-media-range-syntax) Required. Large-screen sizing is lost. | 104 | 63 | 16.4 | 94.8% |
46
+ | [`:dir()`](https://caniuse.com/css-dir-pseudo) Has a fallback. Right-to-left layout follows the dir attribute instead. | 120 | 17 | 16.4 | 92.5% |
47
+ | [`Relative color syntax`](https://caniuse.com/css-relative-colors) Degrades. Control scrims fall back to color-mix(), and subtle shadows are lost. | 131 | 133 | 18 | 87.0% |
48
+ | [`scrollbar-color and scrollbar-width`](https://caniuse.com/css-scrollbar) Degrades. Menus show default scrollbars. | 121 | 64 | 26.2 | 87.0% |
49
+ | [`backdrop-filter`](https://caniuse.com/css-backdrop-filter) Degrades. Surfaces lose their blur. | 76 / 17 | 103 | 9 | 96.4% |
50
+ | [`Anchor positioning`](https://caniuse.com/css-anchor-positioning) Has a fallback. Popups are positioned by JavaScript. | Not supported | Not supported | 27 | 0.0% |
49
51
 
50
52
  Older browsers than the baseline are not tested. Without `:has()`, `@container`, `@layer`, `color-mix()`, or `oklch()`, the skins lose focus states, layout, or colors.
51
53
 
@@ -0,0 +1,383 @@
1
+ # Build your own skin
2
+
3
+ Create a skin project, preview it locally, and compose your own player interface from Video.js UI components
4
+
5
+ A skin is the player interface: markup that arranges Video.js UI components inside the player, plus the styles that give it a look. The components handle playback state, keyboard support, and accessibility, so building your own skin is mostly a layout and design job. [Skins](./skins.md) explains how skins fit into Video.js.
6
+
7
+ If a packaged skin is already close to what you want, [Customize skins](./customize-skins.md) is less work.
8
+
9
+ ## Create a skin project
10
+
11
+ The quickest way to start is a standalone skin project. You need [Node.js](https://nodejs.org/) to create the project, run the workbench that previews your skin, and deploy it. Run `node -v` to see which version you have. If you don’t have Node.js, or yours is a few years old, install the latest LTS release.
12
+
13
+ **npm**
14
+
15
+ ```bash
16
+ npx @videojs/cli skin create
17
+ ```
18
+
19
+ **pnpm**
20
+
21
+ ```bash
22
+ pnpm dlx @videojs/cli skin create
23
+ ```
24
+
25
+ **yarn**
26
+
27
+ ```bash
28
+ npx @videojs/cli skin create --package-manager yarn
29
+ ```
30
+
31
+ **bun**
32
+
33
+ ```bash
34
+ bunx @videojs/cli skin create
35
+ ```
36
+
37
+ The command asks for a name, a framework (React or HTML), a styling approach (Tailwind or CSS), a starting skin, and a preset (video, audio, live video, or live audio). The starting skin defaults to [Scaffold](../reference/components/video-scaffold-skin.md): every control wired up, with minimal styling and no transitions, so there’s little to undo.
38
+
39
+ The project goes in a folder named after the skin, so a skin called “My Skin” goes in `my-skin`. Open it and start the preview:
40
+
41
+ **npm**
42
+
43
+ ```bash
44
+ cd my-skin
45
+ npm run dev
46
+ ```
47
+
48
+ **pnpm**
49
+
50
+ ```bash
51
+ cd my-skin
52
+ pnpm dev
53
+ ```
54
+
55
+ **yarn**
56
+
57
+ ```bash
58
+ cd my-skin
59
+ yarn dev
60
+ ```
61
+
62
+ **bun**
63
+
64
+ ```bash
65
+ cd my-skin
66
+ bun dev
67
+ ```
68
+
69
+ The project has two parts:
70
+
71
+ - **`src/`** holds the skin and nothing else. Edit it however you like, including its markup, class names, theme tokens, and stylesheets.
72
+ - **`apps/preview/`** is a workbench that plays your skin. Use it to try different player sizes and aspect ratios, light and dark color schemes, right-to-left text, every built-in language, and playback settings such as autoplay and loop.
73
+
74
+ When you want to share it, deploy the workbench to Vercel, Netlify, or Cloudflare Pages:
75
+
76
+ **npm**
77
+
78
+ ```bash
79
+ npm run deploy
80
+ ```
81
+
82
+ **pnpm**
83
+
84
+ ```bash
85
+ pnpm run deploy
86
+ ```
87
+
88
+ **yarn**
89
+
90
+ ```bash
91
+ yarn run deploy
92
+ ```
93
+
94
+ **bun**
95
+
96
+ ```bash
97
+ bun run deploy
98
+ ```
99
+
100
+ Add the host’s name to skip the question, as in `npm run deploy vercel`. With pnpm, keep the `run`, since `pnpm deploy` is a built-in pnpm command. When the skin is done, move `src/` into your app.
101
+
102
+ ### Build it with a coding agent
103
+
104
+ If you work with a coding agent, such as Claude Code, Codex, or Cursor, it can do all of this for you. Install the Video.js skills, which include a skill for building skins:
105
+
106
+ ```bash
107
+ npx @videojs/cli agents skills
108
+ ```
109
+
110
+ The agent runs the same commands as you would, so it needs Node.js too. If Node.js isn’t installed, you can ask the agent to install it, but it may need your permission or your password to do so.
111
+
112
+ Then ask your agent something like “Help me design my own Video.js skin for our podcast player.” It asks for the same choices as the command, creates the project, starts the workbench, and agrees a design with you before styling. It can also match an existing player’s look from a screenshot or a link, then checks its work against the reference and for accessibility before handing back. [Build with AI](./build-with-ai.md) covers installing the skills in each agent.
113
+
114
+ ## Other starting points
115
+
116
+ - **Inside an existing app.** Add a skin’s source with [Install with Shadcn](./installation-shadcn.md) and pick **Scaffold** as the skin.
117
+ - **From nothing.** Put a [Container](../reference/components/player-container.md) inside your player and add the components you want, as in the example below.
118
+
119
+ No path requires Scaffold’s structure, its theme tokens, or any particular CSS approach.
120
+
121
+ ## A minimal skin
122
+
123
+ This example shows how a skin fits together without Scaffold’s extra parts. It has a play button, a timeline, the current time, mute, fullscreen, keyboard shortcuts, screen reader announcements, and an error dialog. The player provides state, the Container is the skin’s root, and the media element goes inside the Container next to the controls.
124
+
125
+ **index.html**
126
+
127
+ ```html
128
+ <script type="module">
129
+ import '@videojs/html/video/player';
130
+ import '@videojs/html/ui/container';
131
+ import '@videojs/html/ui/controls';
132
+ import '@videojs/html/ui/controls-content';
133
+ import '@videojs/html/ui/controls-group';
134
+ import '@videojs/html/ui/play-button';
135
+ import '@videojs/html/ui/time-slider';
136
+ import '@videojs/html/ui/slider-track';
137
+ import '@videojs/html/ui/slider-buffer';
138
+ import '@videojs/html/ui/slider-fill';
139
+ import '@videojs/html/ui/slider-thumb';
140
+ import '@videojs/html/ui/time';
141
+ import '@videojs/html/ui/mute-button';
142
+ import '@videojs/html/ui/fullscreen-button';
143
+ import '@videojs/html/ui/hotkey';
144
+ import '@videojs/html/ui/status-announcer';
145
+ import '@videojs/html/ui/error-dialog';
146
+ import '@videojs/html/ui/dialog-popup';
147
+ import '@videojs/html/ui/dialog-title';
148
+ import '@videojs/html/ui/dialog-description';
149
+ import '@videojs/html/ui/dialog-close';
150
+ </script>
151
+
152
+ <video-player>
153
+ <media-container class="video-skin">
154
+ <video src="video.mp4" playsinline></video>
155
+
156
+ <media-controls>
157
+ <media-controls-content class="video-skin-controls">
158
+ <media-time-slider class="video-skin-time-slider">
159
+ <media-slider-track class="video-skin-track">
160
+ <media-slider-buffer class="video-skin-buffer"></media-slider-buffer>
161
+ <media-slider-fill class="video-skin-fill"></media-slider-fill>
162
+ </media-slider-track>
163
+ <media-slider-thumb class="video-skin-thumb"></media-slider-thumb>
164
+ </media-time-slider>
165
+
166
+ <media-controls-group class="video-skin-controls-group" aria-label="Playback controls">
167
+ <media-play-button class="video-skin-play">
168
+ <span class="show-when-paused">Play</span>
169
+ <span class="show-when-playing">Pause</span>
170
+ </media-play-button>
171
+ <media-time type="current"></media-time>
172
+ <media-mute-button class="video-skin-mute">
173
+ <span class="show-when-muted">Unmute</span>
174
+ <span class="show-when-unmuted">Mute</span>
175
+ </media-mute-button>
176
+ <media-fullscreen-button class="video-skin-fullscreen">
177
+ <span class="show-when-fullscreen">Exit fullscreen</span>
178
+ <span class="show-when-not-fullscreen">Fullscreen</span>
179
+ </media-fullscreen-button>
180
+ </media-controls-group>
181
+ </media-controls-content>
182
+ </media-controls>
183
+
184
+ <media-hotkey keys="Space" action="togglePaused"></media-hotkey>
185
+ <media-hotkey keys="m" action="toggleMuted"></media-hotkey>
186
+ <media-hotkey keys="f" action="toggleFullscreen"></media-hotkey>
187
+ <media-hotkey keys="ArrowLeft" action="seekStep" value="-5"></media-hotkey>
188
+ <media-hotkey keys="ArrowRight" action="seekStep" value="5"></media-hotkey>
189
+
190
+ <media-status-announcer></media-status-announcer>
191
+
192
+ <media-error-dialog>
193
+ <media-dialog-popup class="video-skin-error">
194
+ <media-dialog-title></media-dialog-title>
195
+ <media-dialog-description></media-dialog-description>
196
+ <media-dialog-close></media-dialog-close>
197
+ </media-dialog-popup>
198
+ </media-error-dialog>
199
+ </media-container>
200
+ </video-player>
201
+ ```
202
+
203
+ The `@videojs/html/video/player` import registers `<video-player>` with the video features, which hold the state these controls need. Some controls, such as Cast or Quality, depend on features that not every preset includes. Each control’s reference page names the feature it needs.
204
+
205
+ This CSS places the controls over the video, hides them when the viewer is idle, draws the timeline, and keeps the error dialog hidden until playback fails:
206
+
207
+ **video-skin.css**
208
+
209
+ ```css
210
+ .video-skin {
211
+ position: relative;
212
+ display: block;
213
+ }
214
+
215
+ .video-skin video {
216
+ display: block;
217
+ width: 100%;
218
+ }
219
+
220
+ .video-skin-controls {
221
+ position: absolute;
222
+ inset: auto 0 0;
223
+ padding: 12px;
224
+ background: linear-gradient(to top, rgb(0 0 0 / 0.7), transparent);
225
+ color: white;
226
+ transition: opacity 0.25s;
227
+ }
228
+
229
+ /* Keep controls visible while something inside them has keyboard focus. */
230
+ .video-skin-controls:not([data-visible]):not(:has(:focus-visible)) {
231
+ opacity: 0;
232
+ }
233
+
234
+ .video-skin-controls-group {
235
+ display: flex;
236
+ gap: 8px;
237
+ align-items: center;
238
+ }
239
+
240
+ /* The slider sets its progress as CSS custom properties. */
241
+ .video-skin-time-slider {
242
+ position: relative;
243
+ display: flex;
244
+ align-items: center;
245
+ height: 20px;
246
+ cursor: pointer;
247
+ }
248
+
249
+ .video-skin-track {
250
+ position: relative;
251
+ width: 100%;
252
+ height: 4px;
253
+ background: rgb(255 255 255 / 0.3);
254
+ border-radius: 9999px;
255
+ }
256
+
257
+ .video-skin-buffer,
258
+ .video-skin-fill {
259
+ position: absolute;
260
+ top: 0;
261
+ bottom: 0;
262
+ left: 0;
263
+ border-radius: inherit;
264
+ }
265
+
266
+ .video-skin-buffer {
267
+ width: var(--media-slider-buffer);
268
+ background: rgb(255 255 255 / 0.4);
269
+ }
270
+
271
+ .video-skin-fill {
272
+ width: var(--media-slider-fill);
273
+ background: white;
274
+ }
275
+
276
+ .video-skin-thumb {
277
+ position: absolute;
278
+ left: var(--media-slider-fill);
279
+ width: 12px;
280
+ height: 12px;
281
+ background: white;
282
+ border-radius: 50%;
283
+ translate: -50% 0;
284
+ }
285
+
286
+ .video-skin-error {
287
+ position: absolute;
288
+ inset: 0;
289
+ display: grid;
290
+ gap: 12px;
291
+ place-content: center;
292
+ padding: 24px;
293
+ background: rgb(0 0 0 / 0.85);
294
+ color: white;
295
+ text-align: center;
296
+ }
297
+
298
+ .video-skin-error:not([data-open]) {
299
+ display: none;
300
+ }
301
+ ```
302
+
303
+ Each button in the HTML example holds a label for every state. Show only the one that matches:
304
+
305
+ **video-skin.css**
306
+
307
+ ```css
308
+ .video-skin-play:not([data-paused]) .show-when-paused,
309
+ .video-skin-play[data-paused] .show-when-playing,
310
+ .video-skin-mute:not([data-muted]) .show-when-muted,
311
+ .video-skin-mute[data-muted] .show-when-unmuted,
312
+ .video-skin-fullscreen:not([data-fullscreen]) .show-when-fullscreen,
313
+ .video-skin-fullscreen[data-fullscreen] .show-when-not-fullscreen {
314
+ display: none;
315
+ }
316
+ ```
317
+
318
+ From here, add the components your design needs.
319
+
320
+ ## Pick the components
321
+
322
+ Each skin is made of the same building blocks. Use the ones your design needs; their reference pages cover props, state, data attributes, and examples.
323
+
324
+ | Purpose | Components |
325
+ | --- | --- |
326
+ | Player surface, fullscreen and activity target | [Container](../reference/components/player-container.md) |
327
+ | Show and hide controls with user activity | [Controls](../reference/components/controls.md) |
328
+ | Playback buttons | [Play button](../reference/components/play-button.md), [Seek button](../reference/components/seek-button.md), [Mute button](../reference/components/mute-button.md), [Playback rate button](../reference/components/playback-rate-button.md), [Live button](../reference/components/live-button.md) |
329
+ | Screen and device buttons | [Fullscreen button](../reference/components/fullscreen-button.md), [Picture-in-picture button](../reference/components/pip-button.md), [AirPlay button](../reference/components/airplay-button.md), [Cast button](../reference/components/cast-button.md), [Captions button](../reference/components/captions-button.md) |
330
+ | Timeline and volume | [Time slider](../reference/components/time-slider.md), [Volume slider](../reference/components/volume-slider.md), [Volume popover](../reference/components/volume-popover.md), [Thumbnail](../reference/components/thumbnail.md), [Time](../reference/components/time.md) |
331
+ | Menus and popups | [Menu](../reference/components/menu.md), [Popover](../reference/components/popover.md), [Tooltip](../reference/components/tooltip.md), and the radio groups for [captions](../reference/components/captions-radio-group.md), [quality](../reference/components/quality-radio-group.md), [playback rate](../reference/components/playback-rate-radio-group.md), and [audio tracks](../reference/components/audio-track-radio-group.md) |
332
+ | Media details and loading | [Title](../reference/components/title.md), [Poster](../reference/components/poster.md), [Buffering indicator](../reference/components/buffering-indicator.md) |
333
+ | Feedback after an action | [Seek indicator](../reference/components/seek-indicator.md), [Volume indicator](../reference/components/volume-indicator.md), [Status indicator](../reference/components/status-indicator.md) |
334
+ | Errors | [Error dialog](../reference/components/error-dialog.md) |
335
+ | Keyboard, gestures, and announcements | [Hotkey](../reference/components/hotkey.md), [Gesture](../reference/components/gesture.md), [Status announcer](../reference/components/status-announcer.md) |
336
+ | Icons | [Icons](../reference/components/icons.md) |
337
+
338
+ Some of these are easy to leave out by accident because they have little or no visible UI. Include the error dialog, keyboard shortcuts, and status announcer unless you replace them with your own.
339
+
340
+ Import the registration for every element you use, including the parts of compound components. Each reference page lists its imports.
341
+
342
+ To build a control Video.js doesn’t provide, follow [Build your own UI component](./build-your-own-component.md).
343
+
344
+ ## Style it your way
345
+
346
+ The components bring no visual styles. You add the icons, text, and CSS. They describe their state with `data-*` attributes, such as `data-paused` on the play button, so you can style them with plain CSS, Tailwind, or anything else. [UI components](./ui-components.md) explains how. [Automatically show and hide controls](./controls.md) covers the controls’ visibility states.
347
+
348
+ Lay out controls in source order that matches their visual order, so keyboard focus follows the layout.
349
+
350
+ A player’s controls sit over a moving picture, in a frame whose size you don’t control, so a few design questions come up in every skin:
351
+
352
+ - **Surfaces over video.** Borders, shadows, and translucent backgrounds look different over every frame. [Shapes and surfaces](https://videojs.org/blog/videojs-v10-skin-design#shapes-and-surfaces) and [Letting the content influence the color](https://videojs.org/blog/videojs-v10-skin-design#letting-the-content-influence-the-color) cover how our skins handle them.
353
+ - **Readable controls.** Seek tracks need a visible target, icons need optical alignment, and time displays need fixed-width digits. See [Controls and typography](https://videojs.org/blog/videojs-v10-skin-design#controls-and-typography).
354
+ - **Motion.** Menus should open from the control that opened them and respond when they’re closed mid-transition. See [Motion and transitions](https://videojs.org/blog/videojs-v10-skin-design#motion-and-transitions).
355
+ - **Small players.** Captions, settings, picture-in-picture, and Cast all compete for space at narrow widths. See [Fitting the available space](https://videojs.org/blog/videojs-v10-skin-design#fitting-the-available-space).
356
+ - **Custom colors.** An accent color someone else picks still needs contrast against its text and the video. See [Customization and contrast](https://videojs.org/blog/videojs-v10-skin-design#customization-and-contrast).
357
+
358
+ [Design inspiration behind the Video.js v10 skins](https://videojs.org/blog/videojs-v10-skin-design) covers the thinking behind our own skins in full.
359
+
360
+ ## Support the public CSS properties
361
+
362
+ If the skin is only for your own site, you can skip this.
363
+
364
+ If you want to offer it as a Video.js skin, support the same CSS custom properties as the packaged skins, such as `--media-accent-color` and `--media-border-radius`. People can then restyle your skin the same way they restyle ours. [Set the skin’s styles](./customize-skins.md#set-the-skins-styles) lists them. We’re still working out how to accept community skins into Video.js.
365
+
366
+ ## Test it
367
+
368
+ Check the finished skin with real media, at the narrowest player size you support:
369
+
370
+ - Play, pause, seek, change volume, open every menu, and enter fullscreen.
371
+ - Use only a keyboard, then only touch. Focus stays visible and follows the layout, and controls hide when idle.
372
+ - Check loading, errors, ended playback, captions, and controls the browser doesn’t support.
373
+ - Check text and icon contrast over bright and dark footage, with forced colors, and with reduced motion.
374
+
375
+ [Accessibility](./accessibility.md) covers these checks in more detail.
376
+
377
+ ## Related pages
378
+
379
+ ### Guides
380
+
381
+ - [Skins](./skins.md): Packaged player designs that include both UI components and their styles.
382
+ - [Customize skins](./customize-skins.md): Style a packaged Video.js skin or add its source to change controls, layout, styles, and interactions
383
+ - [Build your own UI component](./build-your-own-component.md): Create custom player controls that read state, dispatch actions, and stay accessible.
@@ -25,6 +25,7 @@ Each installation [preset](./presets.md) ships its bundles at the package root:
25
25
  | `video.js` | `<video-player>`, `<video-skin>`, and the UI elements the skin composes |
26
26
  | `video-neutral.js` | `<video-player>`, `<video-neutral-skin>`, and the UI elements the neutral skin composes |
27
27
  | `video-compat.js` | `<video-player>`, `<video-compat-skin>`, and the UI elements the compat skin composes |
28
+ | `video-scaffold.js` | `<video-player>`, `<video-scaffold-skin>`, and the UI elements the scaffold skin composes |
28
29
  | `video-player.js` | `<video-player>` alone, for markup without a packaged skin |
29
30
 
30
31
  The other presets follow the same pattern. Background video is the exception: one bundle, `background.js`, registers the player, the skin, and the `<background-video>` media component.
@@ -36,8 +37,8 @@ A skin bundle registers every UI element its skin is built from. Any element the
36
37
  Each [UI element](./ui-components.md) is its own bundle under `ui/`, for markup that a skin bundle does not already cover: a player-only bundle, [skin source](./customize-skins.md#style-skin-source) that adds a component the packaged skin lacks, or a [dialog](../reference/components/dialog.md) your page opens beside a packaged skin.
37
38
 
38
39
  ```html
39
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/video-player.js"></script>
40
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/ui/play-button.js"></script>
40
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/video-player.js"></script>
41
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/ui/play-button.js"></script>
41
42
  ```
42
43
 
43
44
  Each path mirrors its npm subpath: `ui/play-button.js` registers the element that `@videojs/html/ui/play-button` does. Compound components list one bundle per part in their reference page’s Import section, and all of the bundles share chunks with the player.
@@ -47,8 +48,8 @@ Each path mirrors its npm subpath: `ui/play-button.js` registers the element tha
47
48
  The default presets play through the browser’s own `<video>` and `<audio>`. Every other [media component](./media-sources.md) is its own bundle under `media/`, loaded beside the player bundle:
48
49
 
49
50
  ```html
50
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/video.js"></script>
51
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/media/hlsjs-video.js"></script>
51
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/video.js"></script>
52
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/media/hlsjs-video.js"></script>
52
53
  ```
53
54
 
54
55
  Each path mirrors its npm subpath. Elements with more than one playback engine keep the extra flavors in a directory named for the element, such as `media/mux-video/spf.js`. Load one flavor per page: every flavor claims the same tag name, and the first registration wins.
@@ -58,9 +59,9 @@ Each path mirrors its npm subpath. Elements with more than one playback engine k
58
59
  Each [extension](./architecture.md) is its own bundle under `extensions/`, loaded beside the player and media bundles:
59
60
 
60
61
  ```html
61
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/video.js"></script>
62
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/media/mux-video.js"></script>
63
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/extensions/mux-data.js"></script>
62
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/video.js"></script>
63
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/media/mux-video.js"></script>
64
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/extensions/mux-data.js"></script>
64
65
  ```
65
66
 
66
67
  As with media, each path mirrors its npm subpath: `extensions/mux-data.js` registers the element that `@videojs/html/extensions/mux-data` does.
@@ -70,8 +71,8 @@ As with media, each path mirrors its npm subpath: `extensions/mux-data.js` regis
70
71
  English ships inside the skin bundles. Each other locale is a bundle under `locales/` that registers its translations on import. Load it before the player script to avoid a flash of English:
71
72
 
72
73
  ```html
73
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/locales/es.js"></script>
74
- <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.1.0/video.js"></script>
74
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/locales/es.js"></script>
75
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@videojs/cdn@10.3.0/video.js"></script>
75
76
  ```
76
77
 
77
78
  One bundle exists per locale pack, plus bare-language aliases such as `pt`. For custom strings, see [Internationalize the player](./internationalization.md).
@@ -40,9 +40,12 @@ Add only the origins for the providers you use. Extend `script-src` with their s
40
40
  | --- | --- | --- |
41
41
  | YouTube | `https://www.youtube.com` | `https://www.youtube.com` or `https://www.youtube-nocookie.com`, depending on your source URL |
42
42
  | Vimeo | — | `https://player.vimeo.com`; `https://vimeo.com` for live events |
43
+ | SoundCloud | — | `https://w.soundcloud.com` |
44
+ | Mixcloud | \- | `https://player-widget.mixcloud.com` |
43
45
  | Spotify | `https://open.spotify.com` | `https://open.spotify.com` |
44
46
  | TikTok | — | `https://www.tiktok.com` |
45
47
  | Twitch | — | `https://player.twitch.tv` |
48
+ | Dailymotion | `https://geo.dailymotion.com` | `https://geo.dailymotion.com` |
46
49
  | Cloudflare | `https://embed.videodelivery.net` | `https://iframe.videodelivery.net` or your `https://customer-<code>.cloudflarestream.com` origin |
47
50
  | Google Cast | `https://www.gstatic.com` | — |
48
51
  | Wistia | `https://fast.wistia.com` and `https://fast.wistia.net` | `https://fast.wistia.com` and `https://fast.wistia.net` if the SDK falls back to an iframe |
@@ -2,7 +2,9 @@
2
2
 
3
3
  Style a packaged Video.js skin or add its source to change controls, layout, styles, and interactions
4
4
 
5
- Start with the path that matches the change you want:
5
+ Pick the skin closest to the design you want first. To apply your brand to a finished design, the Neutral skin is the usual starting point. To build your own design, start from the Scaffold skin. See [Choose a skin](./skins.md#choose-a-skin).
6
+
7
+ Then start with the path that matches the change you want:
6
8
 
7
9
  | If you want to… | Use this path |
8
10
  | --- | --- |
@@ -36,7 +38,7 @@ Packaged skins expose [CSS custom properties](https://developer.mozilla.org/en-U
36
38
 
37
39
  If you omit `--media-accent-text-color`, the skins choose a contrasting text color from `--media-accent-color` using [`contrast-color()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value/contrast-color). Browsers before Chrome 147, Firefox 146, and Safari 26 lack `contrast-color()` and use the theme’s default text color instead, which suits only the default accent, so set both properties together if you support those browsers.
38
40
 
39
- The skins use [`Inter`](https://rsms.me/inter/) when your page loads it and fall back to the system UI font. Video.js does not load the webfont for you. Set `--media-font-family` to use another font.
41
+ The skins use [`Inter`](https://rsms.me/inter/) when your page loads it and fall back to the system UI font. Video.js does not load the webfont for you. Set `--media-font-family` to use another font. The Scaffold skin inherits your page’s font instead, and it has no hairline, so `--media-border-color` doesn’t change it.
40
42
 
41
43
  The media component and poster share `--media-object-fit` and `--media-object-position`, so a `cover` poster stays aligned with `cover` video. See [Add a poster and loading placeholder](./poster.md).
42
44
 
@@ -127,7 +129,7 @@ Installed files belong to your project, so an update replaces your local copy. C
127
129
  npx shadcn@latest add @videojs/video --overwrite
128
130
  ```
129
131
 
130
- To understand how skins fit into Video.js, read [Skins](./skins.md). To create a control instead of editing one, follow [Build your own component](./build-your-own-component.md).
132
+ To understand how skins fit into Video.js, read [Skins](./skins.md). To design a new interface from a minimal starting point, follow [Build your own skin](./build-your-own-skin.md). To create a control instead of editing one, follow [Build your own component](./build-your-own-component.md).
131
133
 
132
134
  ## Related pages
133
135
 
@@ -138,4 +140,5 @@ To understand how skins fit into Video.js, read [Skins](./skins.md). To create a
138
140
  ### Guides
139
141
 
140
142
  - [Skins](./skins.md): Packaged player designs that include both UI components and their styles.
143
+ - [Build your own skin](./build-your-own-skin.md): Create a skin project, preview it locally, and compose your own player interface from Video.js UI components
141
144
  - [Build your own UI component](./build-your-own-component.md): Create custom player controls that read state, dispatch actions, and stay accessible.
@@ -74,7 +74,7 @@ if (media?.paused) media.play();
74
74
 
75
75
  ## Feature availability
76
76
 
77
- Volume, fullscreen, and picture-in-picture expose availability properties because platform support varies. Volume has two independent properties: `volumeAvailability` for changing the volume level and `mutedAvailability` for toggling mute. For example, iOS Safari allows muting but doesn’t allow programmatic volume control.
77
+ Volume, playback rate, fullscreen, and picture-in-picture expose availability properties because platform and media support varies. Volume has two independent properties: `volumeAvailability` for changing the volume level and `mutedAvailability` for toggling mute. For example, iOS Safari allows muting but doesn’t allow programmatic volume control.
78
78
 
79
79
  | Value | Meaning |
80
80
  | --- | --- |