@videojs/react 10.0.0-beta.7 → 10.0.0-beta.9

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 (118) hide show
  1. package/dist/default/media/simple-hls-video/index.js.map +1 -1
  2. package/dist/default/presets/audio/minimal-skin.css +161 -141
  3. package/dist/default/presets/audio/minimal-skin.js +110 -100
  4. package/dist/default/presets/audio/minimal-skin.js.map +1 -1
  5. package/dist/default/presets/audio/minimal-skin.tailwind.js +120 -111
  6. package/dist/default/presets/audio/minimal-skin.tailwind.js.map +1 -1
  7. package/dist/default/presets/audio/skin.css +164 -132
  8. package/dist/default/presets/audio/skin.js +101 -91
  9. package/dist/default/presets/audio/skin.js.map +1 -1
  10. package/dist/default/presets/audio/skin.tailwind.js +111 -102
  11. package/dist/default/presets/audio/skin.tailwind.js.map +1 -1
  12. package/dist/default/presets/{video/error-dialog.js → error-dialog.js} +18 -18
  13. package/dist/default/presets/error-dialog.js.map +1 -0
  14. package/dist/default/presets/video/minimal-skin.css +224 -192
  15. package/dist/default/presets/video/minimal-skin.js +58 -62
  16. package/dist/default/presets/video/minimal-skin.js.map +1 -1
  17. package/dist/default/presets/video/minimal-skin.tailwind.js +70 -78
  18. package/dist/default/presets/video/minimal-skin.tailwind.js.map +1 -1
  19. package/dist/default/presets/video/skin.css +238 -189
  20. package/dist/default/presets/video/skin.js +58 -62
  21. package/dist/default/presets/video/skin.js.map +1 -1
  22. package/dist/default/presets/video/skin.tailwind.js +69 -77
  23. package/dist/default/presets/video/skin.tailwind.js.map +1 -1
  24. package/dist/default/skins/dist/default/default/tailwind/audio.tailwind.js +5 -4
  25. package/dist/default/skins/dist/default/default/tailwind/audio.tailwind.js.map +1 -1
  26. package/dist/default/skins/dist/default/default/tailwind/components/button.js +4 -3
  27. package/dist/default/skins/dist/default/default/tailwind/components/button.js.map +1 -1
  28. package/dist/default/skins/dist/default/default/tailwind/components/error.js +2 -2
  29. package/dist/default/skins/dist/default/default/tailwind/components/error.js.map +1 -1
  30. package/dist/default/skins/dist/default/default/tailwind/components/overlay.js +1 -1
  31. package/dist/default/skins/dist/default/default/tailwind/components/overlay.js.map +1 -1
  32. package/dist/default/skins/dist/default/default/tailwind/components/popup.js +1 -1
  33. package/dist/default/skins/dist/default/default/tailwind/components/popup.js.map +1 -1
  34. package/dist/default/skins/dist/default/default/tailwind/components/seek.js +1 -1
  35. package/dist/default/skins/dist/default/default/tailwind/components/seek.js.map +1 -1
  36. package/dist/default/skins/dist/default/default/tailwind/video.tailwind.js +5 -3
  37. package/dist/default/skins/dist/default/default/tailwind/video.tailwind.js.map +1 -1
  38. package/dist/default/skins/dist/default/minimal/tailwind/audio.tailwind.js +9 -4
  39. package/dist/default/skins/dist/default/minimal/tailwind/audio.tailwind.js.map +1 -1
  40. package/dist/default/skins/dist/default/minimal/tailwind/components/button.js +4 -3
  41. package/dist/default/skins/dist/default/minimal/tailwind/components/button.js.map +1 -1
  42. package/dist/default/skins/dist/default/minimal/tailwind/components/error.js +2 -2
  43. package/dist/default/skins/dist/default/minimal/tailwind/components/error.js.map +1 -1
  44. package/dist/default/skins/dist/default/minimal/tailwind/components/overlay.js +1 -1
  45. package/dist/default/skins/dist/default/minimal/tailwind/components/overlay.js.map +1 -1
  46. package/dist/default/skins/dist/default/minimal/tailwind/components/popup.js +1 -1
  47. package/dist/default/skins/dist/default/minimal/tailwind/components/popup.js.map +1 -1
  48. package/dist/default/skins/dist/default/minimal/tailwind/components/root.js +1 -1
  49. package/dist/default/skins/dist/default/minimal/tailwind/components/root.js.map +1 -1
  50. package/dist/default/skins/dist/default/minimal/tailwind/components/seek.js +1 -1
  51. package/dist/default/skins/dist/default/minimal/tailwind/components/seek.js.map +1 -1
  52. package/dist/default/skins/dist/default/minimal/tailwind/video.tailwind.js +10 -4
  53. package/dist/default/skins/dist/default/minimal/tailwind/video.tailwind.js.map +1 -1
  54. package/dist/dev/media/simple-hls-video/index.d.ts.map +1 -1
  55. package/dist/dev/media/simple-hls-video/index.js.map +1 -1
  56. package/dist/dev/presets/audio/minimal-skin.css +161 -141
  57. package/dist/dev/presets/audio/minimal-skin.d.ts.map +1 -1
  58. package/dist/dev/presets/audio/minimal-skin.js +110 -100
  59. package/dist/dev/presets/audio/minimal-skin.js.map +1 -1
  60. package/dist/dev/presets/audio/minimal-skin.tailwind.d.ts.map +1 -1
  61. package/dist/dev/presets/audio/minimal-skin.tailwind.js +120 -111
  62. package/dist/dev/presets/audio/minimal-skin.tailwind.js.map +1 -1
  63. package/dist/dev/presets/audio/skin.css +164 -132
  64. package/dist/dev/presets/audio/skin.d.ts.map +1 -1
  65. package/dist/dev/presets/audio/skin.js +101 -91
  66. package/dist/dev/presets/audio/skin.js.map +1 -1
  67. package/dist/dev/presets/audio/skin.tailwind.d.ts.map +1 -1
  68. package/dist/dev/presets/audio/skin.tailwind.js +111 -102
  69. package/dist/dev/presets/audio/skin.tailwind.js.map +1 -1
  70. package/dist/dev/presets/{video/error-dialog.js → error-dialog.js} +18 -18
  71. package/dist/dev/presets/error-dialog.js.map +1 -0
  72. package/dist/dev/presets/video/minimal-skin.css +224 -192
  73. package/dist/dev/presets/video/minimal-skin.d.ts.map +1 -1
  74. package/dist/dev/presets/video/minimal-skin.js +58 -62
  75. package/dist/dev/presets/video/minimal-skin.js.map +1 -1
  76. package/dist/dev/presets/video/minimal-skin.tailwind.d.ts.map +1 -1
  77. package/dist/dev/presets/video/minimal-skin.tailwind.js +70 -78
  78. package/dist/dev/presets/video/minimal-skin.tailwind.js.map +1 -1
  79. package/dist/dev/presets/video/skin.css +238 -189
  80. package/dist/dev/presets/video/skin.d.ts.map +1 -1
  81. package/dist/dev/presets/video/skin.js +58 -62
  82. package/dist/dev/presets/video/skin.js.map +1 -1
  83. package/dist/dev/presets/video/skin.tailwind.d.ts.map +1 -1
  84. package/dist/dev/presets/video/skin.tailwind.js +69 -77
  85. package/dist/dev/presets/video/skin.tailwind.js.map +1 -1
  86. package/dist/dev/skins/dist/default/default/tailwind/audio.tailwind.js +5 -4
  87. package/dist/dev/skins/dist/default/default/tailwind/audio.tailwind.js.map +1 -1
  88. package/dist/dev/skins/dist/default/default/tailwind/components/button.js +4 -3
  89. package/dist/dev/skins/dist/default/default/tailwind/components/button.js.map +1 -1
  90. package/dist/dev/skins/dist/default/default/tailwind/components/error.js +2 -2
  91. package/dist/dev/skins/dist/default/default/tailwind/components/error.js.map +1 -1
  92. package/dist/dev/skins/dist/default/default/tailwind/components/overlay.js +1 -1
  93. package/dist/dev/skins/dist/default/default/tailwind/components/overlay.js.map +1 -1
  94. package/dist/dev/skins/dist/default/default/tailwind/components/popup.js +1 -1
  95. package/dist/dev/skins/dist/default/default/tailwind/components/popup.js.map +1 -1
  96. package/dist/dev/skins/dist/default/default/tailwind/components/seek.js +1 -1
  97. package/dist/dev/skins/dist/default/default/tailwind/components/seek.js.map +1 -1
  98. package/dist/dev/skins/dist/default/default/tailwind/video.tailwind.js +5 -3
  99. package/dist/dev/skins/dist/default/default/tailwind/video.tailwind.js.map +1 -1
  100. package/dist/dev/skins/dist/default/minimal/tailwind/audio.tailwind.js +9 -4
  101. package/dist/dev/skins/dist/default/minimal/tailwind/audio.tailwind.js.map +1 -1
  102. package/dist/dev/skins/dist/default/minimal/tailwind/components/button.js +4 -3
  103. package/dist/dev/skins/dist/default/minimal/tailwind/components/button.js.map +1 -1
  104. package/dist/dev/skins/dist/default/minimal/tailwind/components/error.js +2 -2
  105. package/dist/dev/skins/dist/default/minimal/tailwind/components/error.js.map +1 -1
  106. package/dist/dev/skins/dist/default/minimal/tailwind/components/overlay.js +1 -1
  107. package/dist/dev/skins/dist/default/minimal/tailwind/components/overlay.js.map +1 -1
  108. package/dist/dev/skins/dist/default/minimal/tailwind/components/popup.js +1 -1
  109. package/dist/dev/skins/dist/default/minimal/tailwind/components/popup.js.map +1 -1
  110. package/dist/dev/skins/dist/default/minimal/tailwind/components/root.js +1 -1
  111. package/dist/dev/skins/dist/default/minimal/tailwind/components/root.js.map +1 -1
  112. package/dist/dev/skins/dist/default/minimal/tailwind/components/seek.js +1 -1
  113. package/dist/dev/skins/dist/default/minimal/tailwind/components/seek.js.map +1 -1
  114. package/dist/dev/skins/dist/default/minimal/tailwind/video.tailwind.js +10 -4
  115. package/dist/dev/skins/dist/default/minimal/tailwind/video.tailwind.js.map +1 -1
  116. package/package.json +7 -7
  117. package/dist/default/presets/video/error-dialog.js.map +0 -1
  118. package/dist/dev/presets/video/error-dialog.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/media/simple-hls-video/index.tsx"],"sourcesContent":["import { SimpleHlsMedia } from '@videojs/core/dom/media/simple-hls';\nimport type { PropsWithChildren, VideoHTMLAttributes } from 'react';\nimport { forwardRef, useEffect, useMemo } from 'react';\nimport { useMediaAttach } from '../../player/context';\nimport { attachMediaElement } from '../../utils/attach-media-element';\nimport { mediaProps } from '../../utils/media-props';\nimport { useComposedRefs } from '../../utils/use-composed-refs';\n\n// TODO: mediaProps uses media.preload which SimpleHlsMedia does not yet expose.\n// Add preload getter/setter to SimpleHlsMedia as a follow-up.\nexport type SimpleHlsVideoProps = PropsWithChildren<VideoHTMLAttributes<HTMLVideoElement>>;\n\nexport const SimpleHlsVideo = forwardRef<HTMLVideoElement, SimpleHlsVideoProps>(({ children, ...props }, ref) => {\n const mediaApi = useMemo(() => new SimpleHlsMedia(), []);\n const setMedia = useMediaAttach();\n\n useEffect(() => {\n setMedia?.(mediaApi);\n }, [mediaApi, setMedia]);\n\n const composedRef = useComposedRefs(attachMediaElement(mediaApi), ref);\n\n return (\n <video ref={composedRef} {...mediaProps(mediaApi, props)}>\n {children}\n </video>\n );\n});\n\nexport default SimpleHlsVideo;\n"],"mappings":";;;;;;;;;AAYA,MAAa,iBAAiB,YAAmD,EAAE,UAAU,GAAG,SAAS,QAAQ;CAC/G,MAAM,WAAW,cAAc,IAAI,gBAAgB,EAAE,EAAE,CAAC;CACxD,MAAM,WAAW,gBAAgB;AAEjC,iBAAgB;AACd,aAAW,SAAS;IACnB,CAAC,UAAU,SAAS,CAAC;AAIxB,QACE,oBAAC;EAAM,KAHW,gBAAgB,mBAAmB,SAAS,EAAE,IAAI;EAG3C,GAAI,WAAW,UAAU,MAAM;EACrD;GACK;EAEV"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/media/simple-hls-video/index.tsx"],"sourcesContent":["import { SimpleHlsMedia } from '@videojs/core/dom/media/simple-hls';\nimport type { PropsWithChildren, VideoHTMLAttributes } from 'react';\nimport { forwardRef, useEffect, useMemo } from 'react';\nimport { useMediaAttach } from '../../player/context';\nimport { attachMediaElement } from '../../utils/attach-media-element';\nimport { mediaProps } from '../../utils/media-props';\nimport { useComposedRefs } from '../../utils/use-composed-refs';\n\nexport type SimpleHlsVideoProps = PropsWithChildren<VideoHTMLAttributes<HTMLVideoElement>>;\n\nexport const SimpleHlsVideo = forwardRef<HTMLVideoElement, SimpleHlsVideoProps>(({ children, ...props }, ref) => {\n const mediaApi = useMemo(() => new SimpleHlsMedia(), []);\n const setMedia = useMediaAttach();\n\n useEffect(() => {\n setMedia?.(mediaApi);\n }, [mediaApi, setMedia]);\n\n const composedRef = useComposedRefs(attachMediaElement(mediaApi), ref);\n\n return (\n <video ref={composedRef} {...mediaProps(mediaApi, props)}>\n {children}\n </video>\n );\n});\n\nexport default SimpleHlsVideo;\n"],"mappings":";;;;;;;;;AAUA,MAAa,iBAAiB,YAAmD,EAAE,UAAU,GAAG,SAAS,QAAQ;CAC/G,MAAM,WAAW,cAAc,IAAI,gBAAgB,EAAE,EAAE,CAAC;CACxD,MAAM,WAAW,gBAAgB;AAEjC,iBAAgB;AACd,aAAW,SAAS;IACnB,CAAC,UAAU,SAAS,CAAC;AAIxB,QACE,oBAAC;EAAM,KAHW,gBAAgB,mBAAmB,SAAS,EAAE,IAAI;EAG3C,GAAI,WAAW,UAAU,MAAM;EACrD;GACK;EAEV"}
@@ -1,67 +1,3 @@
1
- /* ==========================================================================
2
- Icon State Visibility for Audio Skins
3
-
4
- Data-attribute-driven visibility rules for multi-state icon buttons.
5
- Uses :is() with both element selectors (for HTML custom element wrappers)
6
- and class selectors (for React rendered SVG elements).
7
- ========================================================================== */
8
-
9
- /* --- All icons hidden by default --- */
10
-
11
- .media-button--play .media-icon--restart,
12
- .media-button--play .media-icon--play,
13
- .media-button--play .media-icon--pause,
14
- .media-button--mute .media-icon--volume-off,
15
- .media-button--mute .media-icon--volume-low,
16
- .media-button--mute .media-icon--volume-high {
17
- display: none;
18
- opacity: 0;
19
- }
20
-
21
- /* --- Active icon per state --- */
22
-
23
- /* Play: ended → restart */
24
- .media-button--play[data-ended] .media-icon--restart,
25
- /* Play: paused (not ended) → play */
26
- .media-button--play:not([data-ended])[data-paused] .media-icon--play,
27
- /* Play: playing (not paused, not ended) → pause */
28
- .media-button--play:not([data-paused]):not([data-ended]) .media-icon--pause,
29
- /* Mute: muted → volume off */
30
- .media-button--mute[data-muted] .media-icon--volume-off,
31
- /* Mute: volume low (not muted) → volume low */
32
- .media-button--mute:not([data-muted])[data-volume-level="low"] .media-icon--volume-low,
33
- /* Mute: volume high (not muted, not low) → volume high */
34
- .media-button--mute:not([data-muted]):not([data-volume-level="low"]) .media-icon--volume-high {
35
- display: block;
36
- opacity: 1;
37
- }
38
-
39
- /* ==========================================================================
40
- Tooltip Label State Visibility for Audio Skins
41
-
42
- Data-attribute-driven visibility rules for multi-state tooltip labels.
43
- Uses adjacent sibling selectors to match button state → tooltip content.
44
- ========================================================================== */
45
-
46
- /* --- All multi-state labels hidden by default --- */
47
-
48
- .media-tooltip-label {
49
- display: none;
50
- }
51
-
52
- /* --- Active label per state --- */
53
-
54
- /* Play: ended → replay */
55
- .media-button--play[data-ended] + .media-tooltip .media-tooltip-label--replay,
56
- /* Play: paused (not ended) → play */
57
- .media-button--play:not([data-ended])[data-paused] + .media-tooltip
58
- .media-tooltip-label--play,
59
- /* Play: playing (not paused, not ended) → pause */
60
- .media-button--play:not([data-paused]):not([data-ended]) + .media-tooltip
61
- .media-tooltip-label--pause {
62
- display: block;
63
- }
64
-
65
1
  /* ==========================================================================
66
2
  Reset
67
3
  ========================================================================== */
@@ -109,6 +45,8 @@
109
45
  letter-spacing: normal;
110
46
  -webkit-font-smoothing: auto;
111
47
  -moz-osx-font-smoothing: auto;
48
+
49
+ --media-controls-radius: var(--media-border-radius, 1rem);
112
50
  }
113
51
 
114
52
  /* ==========================================================================
@@ -133,61 +71,6 @@
133
71
  Error Dialog
134
72
  ========================================================================== */
135
73
 
136
- .media-minimal-skin .media-error {
137
- position: absolute;
138
- inset: 0;
139
- z-index: 20;
140
- display: flex;
141
- align-items: center;
142
- justify-content: center;
143
- pointer-events: none;
144
- }
145
-
146
- .media-minimal-skin .media-error__dialog {
147
- display: flex;
148
- flex-direction: column;
149
- gap: 0.75rem;
150
- max-width: 16rem;
151
- padding: 1rem;
152
- color: oklch(1 0 0);
153
- font-size: 0.875rem;
154
- text-shadow: 0 1px 0 oklch(0 0 0 / 0.5);
155
- transition-property: opacity, scale;
156
- transition-duration: 500ms;
157
- transition-delay: 100ms;
158
- transition-timing-function: linear(
159
- 0,
160
- 0.034 1.5%,
161
- 0.763 9.7%,
162
- 1.066 13.9%,
163
- 1.198 19.9%,
164
- 1.184 21.8%,
165
- 0.963 37.5%,
166
- 0.997 50.9%,
167
- 1
168
- );
169
-
170
- /* Simple, fast transition for reduced motion users */
171
- @media (prefers-reduced-motion: reduce) {
172
- transition-duration: 100ms;
173
- transition-delay: 0ms;
174
- transition-timing-function: ease-out;
175
- }
176
- }
177
-
178
- .media-minimal-skin .media-error[data-starting-style] .media-error__dialog,
179
- .media-minimal-skin .media-error[data-ending-style] .media-error__dialog {
180
- opacity: 0;
181
- scale: 0.5;
182
- }
183
-
184
- .media-minimal-skin .media-error__content {
185
- display: flex;
186
- flex-direction: column;
187
- gap: 0.5rem;
188
- padding: 0.375rem 0;
189
- }
190
-
191
74
  .media-minimal-skin .media-error__title {
192
75
  font-weight: 600;
193
76
  line-height: 1.25;
@@ -207,6 +90,10 @@
207
90
  }
208
91
  }
209
92
 
93
+ .media-minimal-skin .media-error[data-open] ~ .media-controls * {
94
+ visibility: hidden;
95
+ }
96
+
210
97
  /* ==========================================================================
211
98
  Controls
212
99
  ========================================================================== */
@@ -285,16 +172,11 @@
285
172
  justify-content: center;
286
173
  flex-shrink: 0;
287
174
  padding: 0.5rem 1rem;
288
- background: oklch(1 0 0);
289
175
  border: none;
290
- border-radius: 0.5rem;
176
+ border-radius: calc(var(--media-controls-radius) - var(--media-controls-padding));
291
177
  outline: 2px solid transparent;
292
178
  outline-offset: -2px;
293
- color: oklch(0 0 0);
294
- font-weight: 500;
295
- text-align: center;
296
- text-shadow: inherit;
297
- transition-property: background-color, color, outline-offset, scale;
179
+ transition-property: background-color, outline-offset, scale;
298
180
  transition-duration: 150ms;
299
181
  transition-timing-function: ease-out;
300
182
  cursor: pointer;
@@ -306,6 +188,10 @@
306
188
  outline-offset: 2px;
307
189
  }
308
190
 
191
+ &:active {
192
+ scale: 0.98;
193
+ }
194
+
309
195
  &[disabled] {
310
196
  opacity: 0.5;
311
197
  filter: grayscale(1);
@@ -317,21 +203,42 @@
317
203
  }
318
204
  }
319
205
 
320
- /* Icon button variant */
321
- .media-minimal-skin .media-button--icon {
322
- display: grid;
323
- width: 2.375rem;
324
- padding: 0;
325
- aspect-ratio: 1;
206
+ /* biome-ignore lint/correctness/noUnknownProperty: corner-shape is an emerging CSS spec */
207
+ @supports (corner-shape: squircle) {
208
+ .media-minimal-skin .media-button {
209
+ border-radius: var(--media-controls-radius);
210
+ /* biome-ignore lint/correctness/noUnknownProperty: corner-shape is an emerging CSS spec */
211
+ corner-shape: squircle;
212
+ }
213
+ }
214
+
215
+ /* Primary button variant */
216
+ .media-minimal-skin .media-button--primary {
217
+ background: oklch(1 0 0);
218
+ color: oklch(0 0 0);
219
+ font-weight: 500;
220
+ text-align: center;
221
+ }
222
+
223
+ /* Subtle button variant */
224
+ .media-minimal-skin .media-button--subtle {
326
225
  background: transparent;
327
226
  color: inherit;
227
+ text-shadow: inherit;
328
228
 
329
229
  &:hover,
330
230
  &:focus-visible,
331
231
  &[aria-expanded="true"] {
332
- color: oklch(from currentColor l c h / 0.8);
333
- text-decoration: none;
232
+ background: oklch(from currentColor l c h / 0.1);
334
233
  }
234
+ }
235
+
236
+ /* Icon button variant */
237
+ .media-minimal-skin .media-button--icon {
238
+ display: grid;
239
+ width: 2.375rem;
240
+ padding: 0;
241
+ aspect-ratio: 1;
335
242
 
336
243
  &:active {
337
244
  scale: 0.9;
@@ -348,7 +255,7 @@
348
255
  position: absolute;
349
256
  right: -1px;
350
257
  bottom: -3px;
351
- font-size: 0.75em;
258
+ font-size: 10px;
352
259
  font-weight: 480;
353
260
  font-variant-numeric: tabular-nums;
354
261
  }
@@ -549,7 +456,8 @@
549
456
  color: inherit;
550
457
  overflow: visible;
551
458
  transition-property: scale, opacity, filter;
552
- transition-duration: 150ms;
459
+ transition-duration: var(--media-popup-transition-duration);
460
+ transition-timing-function: var(--media-popup-transition-timing-function);
553
461
 
554
462
  &[data-starting-style],
555
463
  &[data-ending-style] {
@@ -652,6 +560,70 @@
652
560
  display: none;
653
561
  }
654
562
 
563
+ /* ==========================================================================
564
+ Icon State Visibility for Audio Skins
565
+
566
+ Data-attribute-driven visibility rules for multi-state icon buttons.
567
+ Uses :is() with both element selectors (for HTML custom element wrappers)
568
+ and class selectors (for React rendered SVG elements).
569
+ ========================================================================== */
570
+
571
+ /* --- All icons hidden by default --- */
572
+
573
+ .media-button--play .media-icon--restart,
574
+ .media-button--play .media-icon--play,
575
+ .media-button--play .media-icon--pause,
576
+ .media-button--mute .media-icon--volume-off,
577
+ .media-button--mute .media-icon--volume-low,
578
+ .media-button--mute .media-icon--volume-high {
579
+ display: none;
580
+ opacity: 0;
581
+ }
582
+
583
+ /* --- Active icon per state --- */
584
+
585
+ /* Play: ended → restart */
586
+ .media-button--play[data-ended] .media-icon--restart,
587
+ /* Play: paused (not ended) → play */
588
+ .media-button--play:not([data-ended])[data-paused] .media-icon--play,
589
+ /* Play: playing (not paused, not ended) → pause */
590
+ .media-button--play:not([data-paused]):not([data-ended]) .media-icon--pause,
591
+ /* Mute: muted → volume off */
592
+ .media-button--mute[data-muted] .media-icon--volume-off,
593
+ /* Mute: volume low (not muted) → volume low */
594
+ .media-button--mute:not([data-muted])[data-volume-level="low"] .media-icon--volume-low,
595
+ /* Mute: volume high (not muted, not low) → volume high */
596
+ .media-button--mute:not([data-muted]):not([data-volume-level="low"]) .media-icon--volume-high {
597
+ display: block;
598
+ opacity: 1;
599
+ }
600
+
601
+ /* ==========================================================================
602
+ Tooltip Label State Visibility for Audio Skins
603
+
604
+ Data-attribute-driven visibility rules for multi-state tooltip labels.
605
+ Uses adjacent sibling selectors to match button state → tooltip content.
606
+ ========================================================================== */
607
+
608
+ /* --- All multi-state labels hidden by default --- */
609
+
610
+ .media-tooltip-label {
611
+ display: none;
612
+ }
613
+
614
+ /* --- Active label per state --- */
615
+
616
+ /* Play: ended → replay */
617
+ .media-button--play[data-ended] + .media-tooltip .media-tooltip-label--replay,
618
+ /* Play: paused (not ended) → play */
619
+ .media-button--play:not([data-ended])[data-paused] + .media-tooltip
620
+ .media-tooltip-label--play,
621
+ /* Play: playing (not paused, not ended) → pause */
622
+ .media-button--play:not([data-paused]):not([data-ended]) + .media-tooltip
623
+ .media-tooltip-label--pause {
624
+ display: block;
625
+ }
626
+
655
627
 
656
628
  /* ==========================================================================
657
629
  Root
@@ -660,26 +632,74 @@
660
632
  .media-minimal-skin--audio {
661
633
  --media-controls-background-color: oklch(1 0 0);
662
634
  --media-controls-border-color: oklch(0 0 0 / 0.1);
663
- --media-controls-text-color: var(--media-color-primary, oklch(0 0 0));
635
+ --media-controls-padding: 0.375rem;
636
+ --media-text-color: var(--media-color-primary, oklch(0 0 0));
637
+ --media-error-dialog-transition-duration: 250ms;
638
+ --media-error-dialog-transition-delay: 100ms;
639
+ --media-popup-transition-duration: 100ms;
640
+ --media-popup-transition-timing-function: ease-out;
641
+
642
+ @media (prefers-reduced-motion: reduce) {
643
+ --media-error-dialog-transition-duration: 50ms;
644
+ --media-error-dialog-transition-delay: 0ms;
645
+ --media-popup-transition-duration: 0ms;
646
+ }
664
647
 
665
648
  @media (prefers-color-scheme: dark) {
666
649
  --media-controls-background-color: oklch(0 0 0);
667
650
  --media-controls-border-color: oklch(1 0 0 / 0.1);
668
- --media-controls-text-color: var(--media-color-primary, oklch(1 0 0));
651
+ --media-text-color: var(--media-color-primary, oklch(1 0 0));
669
652
  }
670
653
  }
671
654
 
655
+ /* ==========================================================================
656
+ Error Dialog
657
+ ========================================================================== */
658
+
659
+ .media-minimal-skin--audio .media-error__dialog {
660
+ position: absolute;
661
+ inset: 0;
662
+ z-index: 20;
663
+ display: flex;
664
+ align-items: center;
665
+ gap: 1rem;
666
+ padding-inline: 1.25rem 0.5rem;
667
+ transition-property: opacity, filter, scale;
668
+ transition-duration: var(--media-error-dialog-transition-duration);
669
+ transition-delay: var(--media-error-dialog-transition-delay);
670
+ transition-timing-function: ease-out;
671
+ border-radius: calc(infinity * 1px);
672
+ background-color: oklch(from var(--media-controls-background-color) l c h / 1);
673
+ }
674
+
675
+ .media-minimal-skin--audio .media-error[data-starting-style] .media-error__dialog,
676
+ .media-minimal-skin--audio .media-error[data-ending-style] .media-error__dialog {
677
+ opacity: 0;
678
+ filter: blur(4px);
679
+ scale: 0.95;
680
+ }
681
+ .media-minimal-skin--audio .media-error[data-ending-style] .media-error__dialog {
682
+ transition-delay: 0ms;
683
+ }
684
+
685
+ .media-minimal-skin--audio .media-error__content {
686
+ flex: 1;
687
+ display: flex;
688
+ gap: 0.5rem;
689
+ align-items: center;
690
+ }
691
+
672
692
  /* ==========================================================================
673
693
  Controls
674
694
  ========================================================================== */
675
695
 
676
696
  .media-minimal-skin--audio .media-controls {
677
697
  gap: 0.5rem;
678
- padding: 0.375rem;
698
+ padding: var(--media-controls-padding);
679
699
  background-color: var(--media-controls-background-color);
680
700
  backdrop-filter: blur(16px) saturate(1.5);
681
- border-radius: var(--media-border-radius, 0.75rem);
682
- color: var(--media-controls-text-color);
701
+ border-radius: var(--media-controls-radius);
702
+ color: var(--media-text-color);
683
703
  box-shadow: 0 0 0 1px var(--media-controls-border-color);
684
704
  }
685
705
 
@@ -26,36 +26,46 @@ import SeekIcon from "../../icons/dist/react/minimal/seek.js";
26
26
  import VolumeHighIcon from "../../icons/dist/react/minimal/volume-high.js";
27
27
  import VolumeLowIcon from "../../icons/dist/react/minimal/volume-low.js";
28
28
  import VolumeOffIcon from "../../icons/dist/react/minimal/volume-off.js";
29
+ import { ErrorDialog } from "../error-dialog.js";
29
30
  import { forwardRef } from "react";
30
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
31
+ import { jsx, jsxs } from "react/jsx-runtime";
31
32
  import { cn } from "@videojs/utils/style";
32
33
 
33
34
  //#region src/presets/audio/minimal-skin.tsx
34
35
  const SEEK_TIME = 10;
36
+ const ERROR_CLASSNAMES = {
37
+ root: "media-error",
38
+ dialog: "media-error__dialog",
39
+ content: "media-error__content",
40
+ title: "media-error__title",
41
+ description: "media-error__description",
42
+ actions: "media-error__actions",
43
+ close: "media-button media-button--subtle"
44
+ };
35
45
  const Button = forwardRef(function Button({ className, ...props }, ref) {
36
46
  return /* @__PURE__ */ jsx("button", {
37
47
  ref,
38
48
  type: "button",
39
- className: cn("media-button", className),
49
+ className: cn("media-button media-button--subtle media-button--icon", className),
40
50
  ...props
41
51
  });
42
52
  });
43
53
  function PlayLabel() {
44
54
  const paused = usePlayer((s) => Boolean(s.paused));
45
- if (usePlayer((s) => Boolean(s.ended))) return /* @__PURE__ */ jsx(Fragment, { children: "Replay" });
46
- return paused ? /* @__PURE__ */ jsx(Fragment, { children: "Play" }) : /* @__PURE__ */ jsx(Fragment, { children: "Pause" });
55
+ if (usePlayer((s) => Boolean(s.ended))) return "Replay";
56
+ return paused ? "Play" : "Pause";
47
57
  }
48
58
  function VolumePopover() {
49
59
  const volumeUnsupported = usePlayer((s) => s.volumeAvailability === "unsupported");
50
- const muteButton = /* @__PURE__ */ jsx(MuteButton, { render: (props) => /* @__PURE__ */ jsxs(Button, {
51
- ...props,
52
- className: "media-button--icon media-button--mute",
60
+ const muteButton = /* @__PURE__ */ jsxs(MuteButton, {
61
+ className: "media-button--mute",
62
+ render: /* @__PURE__ */ jsx(Button, {}),
53
63
  children: [
54
64
  /* @__PURE__ */ jsx(VolumeOffIcon, { className: "media-icon media-icon--volume-off" }),
55
65
  /* @__PURE__ */ jsx(VolumeLowIcon, { className: "media-icon media-icon--volume-low" }),
56
66
  /* @__PURE__ */ jsx(VolumeHighIcon, { className: "media-icon media-icon--volume-high" })
57
67
  ]
58
- }) });
68
+ });
59
69
  if (volumeUnsupported) return muteButton;
60
70
  return /* @__PURE__ */ jsxs(PopoverRoot, {
61
71
  openOnHover: true,
@@ -81,34 +91,36 @@ function MinimalAudioSkin(props) {
81
91
  return /* @__PURE__ */ jsxs(Container, {
82
92
  className: cn("media-minimal-skin media-minimal-skin--audio", className),
83
93
  ...rest,
84
- children: [children, /* @__PURE__ */ jsx("div", {
85
- className: "media-controls",
86
- children: /* @__PURE__ */ jsxs(TooltipProvider, { children: [
87
- /* @__PURE__ */ jsxs("div", {
88
- className: "media-button-group",
89
- children: [
90
- /* @__PURE__ */ jsxs(TooltipRoot, {
91
- side: "top",
92
- children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(PlayButton, { render: (props) => /* @__PURE__ */ jsxs(Button, {
93
- ...props,
94
- className: "media-button--icon media-button--play",
95
- children: [
96
- /* @__PURE__ */ jsx(RestartIcon, { className: "media-icon media-icon--restart" }),
97
- /* @__PURE__ */ jsx(PlayIcon, { className: "media-icon media-icon--play" }),
98
- /* @__PURE__ */ jsx(PauseIcon, { className: "media-icon media-icon--pause" })
99
- ]
100
- }) }) }), /* @__PURE__ */ jsx(TooltipPopup, {
101
- className: "media-tooltip",
102
- children: /* @__PURE__ */ jsx(PlayLabel, {})
103
- })]
104
- }),
105
- /* @__PURE__ */ jsxs(TooltipRoot, {
106
- side: "top",
107
- children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(SeekButton, {
108
- seconds: -SEEK_TIME,
109
- render: (props) => /* @__PURE__ */ jsx(Button, {
110
- ...props,
111
- className: "media-button--icon media-button--seek",
94
+ children: [
95
+ children,
96
+ /* @__PURE__ */ jsx(ErrorDialog, { classNames: ERROR_CLASSNAMES }),
97
+ /* @__PURE__ */ jsx("div", {
98
+ className: "media-controls",
99
+ children: /* @__PURE__ */ jsxs(TooltipProvider, { children: [
100
+ /* @__PURE__ */ jsxs("div", {
101
+ className: "media-button-group",
102
+ children: [
103
+ /* @__PURE__ */ jsxs(TooltipRoot, {
104
+ side: "top",
105
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsxs(PlayButton, {
106
+ className: "media-button--play",
107
+ render: /* @__PURE__ */ jsx(Button, {}),
108
+ children: [
109
+ /* @__PURE__ */ jsx(RestartIcon, { className: "media-icon media-icon--restart" }),
110
+ /* @__PURE__ */ jsx(PlayIcon, { className: "media-icon media-icon--play" }),
111
+ /* @__PURE__ */ jsx(PauseIcon, { className: "media-icon media-icon--pause" })
112
+ ]
113
+ }) }), /* @__PURE__ */ jsx(TooltipPopup, {
114
+ className: "media-tooltip",
115
+ children: /* @__PURE__ */ jsx(PlayLabel, {})
116
+ })]
117
+ }),
118
+ /* @__PURE__ */ jsxs(TooltipRoot, {
119
+ side: "top",
120
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(SeekButton, {
121
+ seconds: -SEEK_TIME,
122
+ className: "media-button--seek",
123
+ render: /* @__PURE__ */ jsx(Button, {}),
112
124
  children: /* @__PURE__ */ jsxs("span", {
113
125
  className: "media-icon__container",
114
126
  children: [/* @__PURE__ */ jsx(SeekIcon, { className: "media-icon media-icon--seek media-icon--flipped" }), /* @__PURE__ */ jsx("span", {
@@ -116,23 +128,21 @@ function MinimalAudioSkin(props) {
116
128
  children: SEEK_TIME
117
129
  })]
118
130
  })
119
- })
120
- }) }), /* @__PURE__ */ jsxs(TooltipPopup, {
121
- className: "media-tooltip",
122
- children: [
123
- "Seek backward ",
124
- SEEK_TIME,
125
- " seconds"
126
- ]
127
- })]
128
- }),
129
- /* @__PURE__ */ jsxs(TooltipRoot, {
130
- side: "top",
131
- children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(SeekButton, {
132
- seconds: SEEK_TIME,
133
- render: (props) => /* @__PURE__ */ jsx(Button, {
134
- ...props,
135
- className: "media-button--icon media-button--seek",
131
+ }) }), /* @__PURE__ */ jsxs(TooltipPopup, {
132
+ className: "media-tooltip",
133
+ children: [
134
+ "Seek backward ",
135
+ SEEK_TIME,
136
+ " seconds"
137
+ ]
138
+ })]
139
+ }),
140
+ /* @__PURE__ */ jsxs(TooltipRoot, {
141
+ side: "top",
142
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(SeekButton, {
143
+ seconds: SEEK_TIME,
144
+ className: "media-button--seek",
145
+ render: /* @__PURE__ */ jsx(Button, {}),
136
146
  children: /* @__PURE__ */ jsxs("span", {
137
147
  className: "media-icon__container",
138
148
  children: [/* @__PURE__ */ jsx(SeekIcon, { className: "media-icon media-icon--seek" }), /* @__PURE__ */ jsx("span", {
@@ -140,56 +150,56 @@ function MinimalAudioSkin(props) {
140
150
  children: SEEK_TIME
141
151
  })]
142
152
  })
143
- })
144
- }) }), /* @__PURE__ */ jsxs(TooltipPopup, {
145
- className: "media-tooltip",
146
- children: [
147
- "Seek forward ",
148
- SEEK_TIME,
149
- " seconds"
150
- ]
151
- })]
152
- })
153
- ]
154
- }),
155
- /* @__PURE__ */ jsxs("div", {
156
- className: "media-time-controls",
157
- children: [/* @__PURE__ */ jsxs(Group, {
158
- className: "media-time",
159
- children: [
160
- /* @__PURE__ */ jsx(Value, {
161
- type: "current",
162
- className: "media-time__value media-time__value--current"
163
- }),
164
- /* @__PURE__ */ jsx(Separator, { className: "media-time__separator" }),
165
- /* @__PURE__ */ jsx(Value, {
166
- type: "duration",
167
- className: "media-time__value media-time__value--duration"
153
+ }) }), /* @__PURE__ */ jsxs(TooltipPopup, {
154
+ className: "media-tooltip",
155
+ children: [
156
+ "Seek forward ",
157
+ SEEK_TIME,
158
+ " seconds"
159
+ ]
160
+ })]
168
161
  })
169
162
  ]
170
- }), /* @__PURE__ */ jsxs(TimeSliderRoot, {
171
- className: "media-slider",
172
- children: [/* @__PURE__ */ jsxs(SliderTrack, {
173
- className: "media-slider__track",
174
- children: [/* @__PURE__ */ jsx(SliderFill, { className: "media-slider__fill" }), /* @__PURE__ */ jsx(SliderBuffer, { className: "media-slider__buffer" })]
175
- }), /* @__PURE__ */ jsx(SliderThumb, { className: "media-slider__thumb" })]
176
- })]
177
- }),
178
- /* @__PURE__ */ jsxs("div", {
179
- className: "media-button-group",
180
- children: [/* @__PURE__ */ jsxs(TooltipRoot, {
181
- side: "top",
182
- children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(PlaybackRateButton, { render: (props) => /* @__PURE__ */ jsx(Button, {
183
- ...props,
184
- className: "media-button--icon media-button--playback-rate"
185
- }) }) }), /* @__PURE__ */ jsx(TooltipPopup, {
186
- className: "media-tooltip",
187
- children: "Toggle playback rate"
163
+ }),
164
+ /* @__PURE__ */ jsxs("div", {
165
+ className: "media-time-controls",
166
+ children: [/* @__PURE__ */ jsxs(Group, {
167
+ className: "media-time",
168
+ children: [
169
+ /* @__PURE__ */ jsx(Value, {
170
+ type: "current",
171
+ className: "media-time__value media-time__value--current"
172
+ }),
173
+ /* @__PURE__ */ jsx(Separator, { className: "media-time__separator" }),
174
+ /* @__PURE__ */ jsx(Value, {
175
+ type: "duration",
176
+ className: "media-time__value media-time__value--duration"
177
+ })
178
+ ]
179
+ }), /* @__PURE__ */ jsxs(TimeSliderRoot, {
180
+ className: "media-slider",
181
+ children: [/* @__PURE__ */ jsxs(SliderTrack, {
182
+ className: "media-slider__track",
183
+ children: [/* @__PURE__ */ jsx(SliderFill, { className: "media-slider__fill" }), /* @__PURE__ */ jsx(SliderBuffer, { className: "media-slider__buffer" })]
184
+ }), /* @__PURE__ */ jsx(SliderThumb, { className: "media-slider__thumb" })]
188
185
  })]
189
- }), /* @__PURE__ */ jsx(VolumePopover, {})]
190
- })
191
- ] })
192
- })]
186
+ }),
187
+ /* @__PURE__ */ jsxs("div", {
188
+ className: "media-button-group",
189
+ children: [/* @__PURE__ */ jsxs(TooltipRoot, {
190
+ side: "top",
191
+ children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx(PlaybackRateButton, {
192
+ className: "media-button--playback-rate",
193
+ render: /* @__PURE__ */ jsx(Button, {})
194
+ }) }), /* @__PURE__ */ jsx(TooltipPopup, {
195
+ className: "media-tooltip",
196
+ children: "Toggle playback rate"
197
+ })]
198
+ }), /* @__PURE__ */ jsx(VolumePopover, {})]
199
+ })
200
+ ] })
201
+ })
202
+ ]
193
203
  });
194
204
  }
195
205