feat(skin): add error handling for audio players (#1048)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Potts
2026-03-23 09:33:55 +11:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 559d5169b5
commit df927f67fc
43 changed files with 648 additions and 377 deletions
+47 -5
View File
@@ -1,5 +1,3 @@
@import "../../shared/css/audio/icon-state.css";
@import "../../shared/css/audio/tooltip-state.css";
@import "./components/reset.css";
@import "./components/root.css";
@import "./components/surface.css";
@@ -11,6 +9,8 @@
@import "./components/icons.css";
@import "./components/slider.css";
@import "./components/popup.css";
@import "../../shared/css/audio/icon-state.css";
@import "../../shared/css/audio/tooltip-state.css";
/* ==========================================================================
Root
@@ -23,26 +23,68 @@
--media-surface-outer-border-color: oklch(0 0 0 / 0.05);
--media-surface-shadow-color: oklch(0 0 0 / 0.15);
--media-surface-backdrop-filter: blur(16px) saturate(1.5);
--media-controls-text-color: var(--media-color-primary, oklch(0 0 0));
--media-text-color: var(--media-color-primary, oklch(0 0 0));
@media (prefers-color-scheme: dark) {
--media-border-color: oklch(1 0 0 / 0.1);
--media-surface-background-color: oklch(0 0 0 / 0.4);
--media-controls-text-color: var(--media-color-primary, oklch(1 0 0));
--media-text-color: var(--media-color-primary, oklch(1 0 0));
}
}
/* ==========================================================================
Error Dialog
========================================================================== */
.media-default-skin--audio .media-error__dialog {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
gap: 0.75rem;
padding-inline: 1.25rem 0.125rem;
transition-property: opacity, filter;
transition-duration: 300ms;
transition-delay: 100ms;
transition-timing-function: ease-out;
border-radius: calc(infinity * 1px);
background-color: var(--media-surface-background-color);
backdrop-filter: var(--media-surface-backdrop-filter);
color: var(--media-text-color);
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
}
}
.media-default-skin .media-error[data-starting-style] .media-error__dialog,
.media-default-skin .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
filter: blur(4px);
}
.media-default-skin--audio .media-error__content {
flex: 1;
display: flex;
gap: 0.5rem;
align-items: center;
}
/* ==========================================================================
Controls
========================================================================== */
.media-default-skin--audio .media-controls {
color: var(--media-controls-text-color);
color: var(--media-text-color);
}
/* ==========================================================================
Sliders
========================================================================== */
.media-default-skin--audio .media-slider__track {
background-color: oklch(0 0 0 / 0.1);
@@ -9,15 +9,11 @@
justify-content: center;
flex-shrink: 0;
padding: 0.5rem 1rem;
background: oklch(1 0 0);
border: none;
border-radius: calc(infinity * 1px);
outline: 2px solid transparent;
outline-offset: -2px;
color: oklch(0 0 0);
font-weight: 500;
text-align: center;
transition-property: background-color, color, outline-offset, scale;
transition-property: background-color, outline-offset, scale;
transition-duration: 150ms;
transition-timing-function: ease-out;
cursor: pointer;
@@ -25,10 +21,14 @@
touch-action: manipulation;
&:focus-visible {
outline-color: oklch(62.3% 0.214 259.815);
outline-color: currentColor;
outline-offset: 2px;
}
&:active {
scale: 0.98;
}
&[disabled] {
opacity: 0.5;
filter: grayscale(1);
@@ -40,12 +40,16 @@
}
}
/* Icon button variant */
.media-default-skin .media-button--icon {
display: grid;
width: 2.125rem;
padding: 0;
aspect-ratio: 1;
/* Primary button variant */
.media-default-skin .media-button--primary {
background: oklch(1 0 0);
color: oklch(0 0 0);
font-weight: 500;
text-align: center;
}
/* Subtle button variant */
.media-default-skin .media-button--subtle {
background: transparent;
color: inherit;
text-shadow: inherit;
@@ -56,6 +60,14 @@
background-color: oklch(from currentColor l c h / 0.1);
text-decoration: none;
}
}
/* Icon button variant */
.media-default-skin .media-button--icon {
display: grid;
width: 2.125rem;
padding: 0;
aspect-ratio: 1;
&:active {
scale: 0.9;
@@ -72,7 +84,7 @@
position: absolute;
right: -1px;
bottom: -3px;
font-size: 0.75em;
font-size: 10px;
font-weight: 480;
font-variant-numeric: tabular-nums;
}
@@ -3,57 +3,7 @@
========================================================================== */
.media-default-skin .media-error {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
}
.media-default-skin .media-error__dialog {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 18rem;
padding: 0.75rem;
border-radius: 1.75rem;
color: oklch(1 0 0);
font-size: 0.875rem;
transition-property: opacity, scale;
transition-duration: 500ms;
transition-delay: 100ms;
transition-timing-function: linear(
0,
0.034 1.5%,
0.763 9.7%,
1.066 13.9%,
1.198 19.9%,
1.184 21.8%,
0.963 37.5%,
0.997 50.9%,
1
);
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
transition-timing-function: ease-out;
}
}
.media-default-skin .media-error[data-starting-style] .media-error__dialog,
.media-default-skin .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
scale: 0.5;
}
.media-default-skin .media-error__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.5rem 0.5rem 0.375rem;
outline: none;
}
.media-default-skin .media-error__title {
@@ -74,3 +24,7 @@
flex: 1;
}
}
.media-default-skin .media-error[data-open] ~ .media-controls * {
visibility: hidden;
}
@@ -79,6 +79,22 @@
outline-color: oklch(from currentColor l c h / 0.25);
outline-offset: 0;
}
&::after {
content: "";
position: absolute;
inset: -4px;
border-radius: inherit;
box-shadow: 0 0 0 2px oklch(1 0 0);
transition-property: opacity, scale;
transition-duration: 150ms;
transition-timing-function: ease-out;
}
&:not(:focus-visible)::after {
scale: 0.5;
opacity: 0;
}
}
.media-default-skin .media-slider:active .media-slider__thumb,
+69 -2
View File
@@ -1,5 +1,3 @@
@import "../../shared/css/video/icon-state.css";
@import "../../shared/css/video/tooltip-state.css";
@import "./components/reset.css";
@import "./components/root.css";
@import "./components/surface.css";
@@ -16,6 +14,8 @@
@import "./components/slider.css";
@import "./components/popup.css";
@import "./components/captions.css";
@import "../../shared/css/video/icon-state.css";
@import "../../shared/css/video/tooltip-state.css";
/* ==========================================================================
Root
@@ -67,6 +67,69 @@
}
}
/* ==========================================================================
Error Dialog
========================================================================== */
.media-default-skin--video .media-error {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
}
.media-default-skin--video .media-error__dialog {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 18rem;
padding: 0.75rem;
border-radius: 1.75rem;
color: oklch(1 0 0);
text-shadow: 0 1px 0 oklch(0 0 0 / 0.25);
transition-property: opacity, scale;
transition-duration: 500ms;
transition-delay: 100ms;
transition-timing-function: linear(
0,
0.034 1.5%,
0.763 9.7%,
1.066 13.9%,
1.198 19.9%,
1.184 21.8%,
0.963 37.5%,
0.997 50.9%,
1
);
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
transition-timing-function: ease-out;
}
}
.media-default-skin .media-error[data-starting-style] .media-error__dialog,
.media-default-skin .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
scale: 0.5;
}
.media-default-skin--video .media-error__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.5rem 0.5rem 0.375rem;
text-shadow: inherit;
}
.media-default-skin--video .media-error__title {
font-size: 1rem;
}
/* ==========================================================================
Controls (hide/show behavior)
========================================================================== */
@@ -111,6 +174,10 @@
}
}
.media-default-skin--video .media-error[data-open] ~ .media-controls {
display: none;
}
/* Hide cursor when controls are hidden in fullscreen */
@media (pointer: fine) {
.media-default-skin--video:fullscreen:has(.media-controls:not([data-visible])) {
@@ -13,8 +13,8 @@ import { surface as baseSurface } from './components/surface';
export const root = cn(
baseRoot,
'[--media-controls-text-color:var(--media-color-primary,oklch(0_0_0))]',
'dark:[--media-controls-text-color:var(--media-color-primary,oklch(1_0_0))]'
'[--media-text-color:var(--media-color-primary,oklch(0_0_0))]',
'dark:[--media-text-color:var(--media-color-primary,oklch(1_0_0))]'
);
/* ==========================================================================
@@ -39,7 +39,7 @@ export const surface = cn(
Controls
========================================================================== */
export const controls = cn(baseControls, surface, 'text-(--media-controls-text-color)');
export const controls = cn(baseControls, surface, 'text-(--media-text-color)', 'peer-data-open/error:[&_*]:invisible');
/* ==========================================================================
Sliders
@@ -75,7 +75,16 @@ export const bufferingIndicator = {
export const error = {
...baseError,
dialog: cn(baseError.dialog, surface),
dialog: cn(
'absolute inset-0 z-20 flex items-center gap-3 rounded-full px-5 pr-0.5',
'bg-(--media-surface-background-color) text-(--media-text-color)',
'backdrop-blur-lg backdrop-saturate-150',
'transition-[opacity,filter] duration-300 delay-100 ease-out',
'group-data-starting-style/error:opacity-0 group-data-starting-style/error:blur-[4px]',
'group-data-ending-style/error:opacity-0 group-data-ending-style/error:blur-[4px]',
'motion-reduce:duration-100 motion-reduce:delay-0'
),
content: 'flex flex-1 items-center gap-2',
};
/* ==========================================================================
@@ -2,21 +2,21 @@ import { cn } from '@videojs/utils/style';
export const button = {
base: cn(
'items-center justify-center shrink-0 border-none cursor-pointer select-none text-center touch-manipulation',
'font-medium',
'flex items-center justify-center shrink-0 border-none cursor-pointer select-none text-center touch-manipulation',
'py-2 px-4 rounded-full',
'outline-2 outline-transparent -outline-offset-2',
'transition-[background-color,color,outline-offset,scale] duration-150 ease-out',
'transition-[background-color,outline-offset,scale] duration-150 ease-out',
'active:scale-[0.98]',
'disabled:cursor-not-allowed disabled:opacity-50 disabled:grayscale',
'focus-visible:outline-blue-500 focus-visible:outline-offset-2',
'focus-visible:outline-current focus-visible:outline-offset-2',
'data-[availability=unavailable]:hidden'
),
icon: cn(
'grid w-[2.125rem] aspect-square bg-transparent rounded-full',
'text-inherit text-shadow-inherit',
primary: 'bg-white text-black font-medium text-center',
subtle: cn(
'bg-transparent text-inherit text-shadow-inherit',
'hover:bg-current/10 hover:no-underline',
'focus-visible:bg-current/10',
'aria-expanded:bg-current/10',
'active:scale-90'
'aria-expanded:bg-current/10'
),
default: cn('flex py-2 px-4 bg-white rounded-full', 'text-black'),
icon: cn('grid w-[2.125rem] aspect-square p-0', 'active:scale-90'),
};
@@ -1,9 +1,9 @@
import { cn } from '@videojs/utils/style';
export const error = {
root: 'peer/error group/error flex absolute inset-0 z-20 items-center justify-center',
root: 'peer/error group/error flex absolute inset-0 z-20 items-center justify-center outline-none',
dialog: cn(
'flex flex-col gap-3 max-w-72 p-3 rounded-[1.75rem] text-white text-sm',
'flex flex-col gap-3 max-w-72 p-3 rounded-[1.75rem] text-white',
// Animation
'transition-[opacity,scale,transform] duration-500 delay-100',
'ease-[linear(0,0.034_1.5%,0.763_9.7%,1.066_13.9%,1.198_19.9%,1.184_21.8%,0.963_37.5%,0.997_50.9%,1)]',
@@ -1,6 +1,6 @@
export const seek = {
button: '@max-md/media-controls:hidden',
label: 'text-[0.75em] font-[480] tabular-nums',
label: 'text-[10px] font-[480] tabular-nums',
labelForward: 'absolute -right-px -bottom-0.75',
labelBackward: 'absolute -left-px -bottom-0.75',
};
@@ -87,6 +87,7 @@ export const controls = cn(
// Position
'absolute bottom-3 inset-x-3',
'[color:var(--media-color-primary,oklch(1_0_0))] z-10',
'peer-data-open/error:hidden',
'ease-out origin-bottom',
'duration-(--media-controls-transition-duration)',
'delay-(--media-controls-transition-delay)',
@@ -153,7 +154,9 @@ export const bufferingIndicator = {
export const error = {
...baseError,
dialog: cn(baseError.dialog, surface),
dialog: cn(baseError.dialog, surface, 'text-shadow-[0_1px_0_oklch(0_0_0/0.25)]'),
content: cn(baseError.content, 'text-shadow-inherit'),
title: cn(baseError.title, 'text-base'),
};
/* ==========================================================================
+48 -7
View File
@@ -1,5 +1,3 @@
@import "../../shared/css/audio/icon-state.css";
@import "../../shared/css/audio/tooltip-state.css";
@import "./components/reset.css";
@import "./components/root.css";
@import "./components/buffering.css";
@@ -10,6 +8,8 @@
@import "./components/icons.css";
@import "./components/slider.css";
@import "./components/popup.css";
@import "../../shared/css/audio/icon-state.css";
@import "../../shared/css/audio/tooltip-state.css";
/* ==========================================================================
Root
@@ -18,26 +18,67 @@
.media-minimal-skin--audio {
--media-controls-background-color: oklch(1 0 0);
--media-controls-border-color: oklch(0 0 0 / 0.1);
--media-controls-text-color: var(--media-color-primary, oklch(0 0 0));
--media-controls-padding: 0.375rem;
--media-text-color: var(--media-color-primary, oklch(0 0 0));
@media (prefers-color-scheme: dark) {
--media-controls-background-color: oklch(0 0 0);
--media-controls-border-color: oklch(1 0 0 / 0.1);
--media-controls-text-color: var(--media-color-primary, oklch(1 0 0));
--media-text-color: var(--media-color-primary, oklch(1 0 0));
}
}
/* ==========================================================================
Error Dialog
========================================================================== */
.media-minimal-skin--audio .media-error__dialog {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
gap: 1rem;
padding-inline: 1.25rem 0.5rem;
transition-property: opacity, filter, scale;
transition-duration: 300ms;
transition-delay: 100ms;
transition-timing-function: ease-out;
border-radius: calc(infinity * 1px);
background-color: oklch(from var(--media-controls-background-color) l c h / 1);
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
}
}
.media-minimal-skin .media-error[data-starting-style] .media-error__dialog,
.media-minimal-skin .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
filter: blur(4px);
scale: 0.95;
}
.media-minimal-skin--audio .media-error__content {
flex: 1;
display: flex;
gap: 0.5rem;
align-items: center;
}
/* ==========================================================================
Controls
========================================================================== */
.media-minimal-skin--audio .media-controls {
gap: 0.5rem;
padding: 0.375rem;
padding: var(--media-controls-padding);
background-color: var(--media-controls-background-color);
backdrop-filter: blur(16px) saturate(1.5);
border-radius: var(--media-border-radius, 0.75rem);
color: var(--media-controls-text-color);
border-radius: var(--media-controls-radius);
color: var(--media-text-color);
box-shadow: 0 0 0 1px var(--media-controls-border-color);
}
@@ -23,16 +23,11 @@
justify-content: center;
flex-shrink: 0;
padding: 0.5rem 1rem;
background: oklch(1 0 0);
border: none;
border-radius: 0.5rem;
border-radius: calc(var(--media-controls-radius) - var(--media-controls-padding));
outline: 2px solid transparent;
outline-offset: -2px;
color: oklch(0 0 0);
font-weight: 500;
text-align: center;
text-shadow: inherit;
transition-property: background-color, color, outline-offset, scale;
transition-property: background-color, outline-offset, scale;
transition-duration: 150ms;
transition-timing-function: ease-out;
cursor: pointer;
@@ -44,6 +39,10 @@
outline-offset: 2px;
}
&:active {
scale: 0.98;
}
&[disabled] {
opacity: 0.5;
filter: grayscale(1);
@@ -55,21 +54,42 @@
}
}
/* biome-ignore lint/correctness/noUnknownProperty: corner-shape is an emerging CSS spec */
@supports (corner-shape: squircle) {
.media-minimal-skin .media-button {
border-radius: var(--media-controls-radius);
/* biome-ignore lint/correctness/noUnknownProperty: corner-shape is an emerging CSS spec */
corner-shape: squircle;
}
}
/* Primary button variant */
.media-minimal-skin .media-button--primary {
background: oklch(1 0 0);
color: oklch(0 0 0);
font-weight: 500;
text-align: center;
}
/* Subtle button variant */
.media-minimal-skin .media-button--subtle {
background: transparent;
color: inherit;
text-shadow: inherit;
&:hover,
&:focus-visible,
&[aria-expanded="true"] {
background: oklch(from currentColor l c h / 0.1);
}
}
/* Icon button variant */
.media-minimal-skin .media-button--icon {
display: grid;
width: 2.375rem;
padding: 0;
aspect-ratio: 1;
background: transparent;
color: inherit;
&:hover,
&:focus-visible,
&[aria-expanded="true"] {
color: oklch(from currentColor l c h / 0.8);
text-decoration: none;
}
&:active {
scale: 0.9;
@@ -86,7 +106,7 @@
position: absolute;
right: -1px;
bottom: -3px;
font-size: 0.75em;
font-size: 10px;
font-weight: 480;
font-variant-numeric: tabular-nums;
}
@@ -2,61 +2,6 @@
Error Dialog
========================================================================== */
.media-minimal-skin .media-error {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.media-minimal-skin .media-error__dialog {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 16rem;
padding: 1rem;
color: oklch(1 0 0);
font-size: 0.875rem;
text-shadow: 0 1px 0 oklch(0 0 0 / 0.5);
transition-property: opacity, scale;
transition-duration: 500ms;
transition-delay: 100ms;
transition-timing-function: linear(
0,
0.034 1.5%,
0.763 9.7%,
1.066 13.9%,
1.198 19.9%,
1.184 21.8%,
0.963 37.5%,
0.997 50.9%,
1
);
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
transition-timing-function: ease-out;
}
}
.media-minimal-skin .media-error[data-starting-style] .media-error__dialog,
.media-minimal-skin .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
scale: 0.5;
}
.media-minimal-skin .media-error__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.375rem 0;
}
.media-minimal-skin .media-error__title {
font-weight: 600;
line-height: 1.25;
@@ -75,3 +20,7 @@
flex: 1;
}
}
.media-minimal-skin .media-error[data-open] ~ .media-controls * {
visibility: hidden;
}
@@ -21,4 +21,6 @@
letter-spacing: normal;
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
--media-controls-radius: var(--media-border-radius, 1rem);
}
+72 -3
View File
@@ -1,5 +1,3 @@
@import "../../shared/css/video/icon-state.css";
@import "../../shared/css/video/tooltip-state.css";
@import "./components/reset.css";
@import "./components/root.css";
@import "./components/media.css";
@@ -15,6 +13,8 @@
@import "./components/slider.css";
@import "./components/popup.css";
@import "./components/captions.css";
@import "../../shared/css/video/icon-state.css";
@import "../../shared/css/video/tooltip-state.css";
/* ==========================================================================
Root
@@ -25,6 +25,7 @@
background: oklch(0 0 0);
--media-border-color: oklch(0 0 0 / 0.15);
--media-video-border-radius: var(--media-border-radius, 0.75rem);
--media-controls-padding: 0.375rem;
--media-controls-transition-duration: 100ms;
--media-controls-transition-delay: 0ms;
@@ -63,6 +64,74 @@
}
}
/* ==========================================================================
Error Dialog
========================================================================== */
.media-minimal-skin--video .media-error {
position: absolute;
inset: 0;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
outline: none;
}
.media-minimal-skin--video .media-error__dialog {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 16rem;
padding: 1rem;
color: oklch(1 0 0);
text-shadow: 0 1px 0 oklch(0 0 0 / 0.5);
transition-property: opacity, scale;
transition-duration: 500ms;
transition-delay: 100ms;
transition-timing-function: linear(
0,
0.034 1.5%,
0.763 9.7%,
1.066 13.9%,
1.198 19.9%,
1.184 21.8%,
0.963 37.5%,
0.997 50.9%,
1
);
pointer-events: auto;
/* Simple, fast transition for reduced motion users */
@media (prefers-reduced-motion: reduce) {
transition-duration: 100ms;
transition-delay: 0ms;
transition-timing-function: ease-out;
}
}
.media-minimal-skin--video .media-error[data-starting-style] .media-error__dialog,
.media-minimal-skin--video .media-error[data-ending-style] .media-error__dialog {
opacity: 0;
scale: 0.5;
}
.media-minimal-skin--video .media-error__content {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.375rem 0;
}
.media-minimal-skin--video .media-error__title {
font-size: 1.125rem;
}
.media-minimal-skin--video .media-error[data-open] ~ .media-controls {
display: none;
}
/* ==========================================================================
Controls (hide/show behavior)
========================================================================== */
@@ -73,7 +142,7 @@
inset-inline: 0;
z-index: 10;
gap: 0.5rem;
padding: 2rem 0.375rem 0.375rem 0.375rem;
padding: 2rem var(--media-controls-padding) var(--media-controls-padding) var(--media-controls-padding);
color: var(--media-color-primary, oklch(1 0 0));
transition-duration: var(--media-controls-transition-duration);
transition-delay: var(--media-controls-transition-delay);
@@ -1,5 +1,6 @@
import { cn } from '@videojs/utils/style';
import { controls as baseControls } from './components/controls';
import { error as baseError } from './components/error';
import { popup as basePopup } from './components/popup';
import { root as baseRoot } from './components/root';
@@ -11,10 +12,11 @@ export const root = cn(
baseRoot,
'[--media-controls-background-color:oklch(1_0_0)]',
'[--media-controls-border-color:oklch(0_0_0/0.1)]',
'[--media-controls-text-color:var(--media-color-primary,oklch(0_0_0))]',
'[--media-controls-padding:0.375rem]',
'[--media-text-color:var(--media-color-primary,oklch(0_0_0))]',
'dark:[--media-controls-background-color:oklch(0_0_0)]',
'dark:[--media-controls-border-color:oklch(1_0_0/0.1)]',
'dark:[--media-controls-text-color:var(--media-color-primary,oklch(1_0_0))]'
'dark:[--media-text-color:var(--media-color-primary,oklch(1_0_0))]'
);
/* ==========================================================================
@@ -25,10 +27,11 @@ export const controls = cn(
baseControls,
// Layout
'gap-2 p-1.5',
'peer-data-open/error:[&_*]:invisible',
// Appearance
'rounded-(--media-border-radius,0.75rem)',
'bg-(--media-controls-background-color)',
'text-(--media-controls-text-color)',
'text-(--media-text-color)',
// Backdrop filter
'backdrop-blur backdrop-brightness-[0.98] backdrop-saturate-[1.2]',
// Border
@@ -49,6 +52,23 @@ export const popup = {
),
};
/* ==========================================================================
Error
========================================================================== */
export const error = {
...baseError,
dialog: cn(
'absolute inset-0 z-20 flex items-center gap-4 rounded-full px-5 pr-2',
'bg-(--media-controls-background-color)',
'transition-[opacity,filter,scale] duration-300 delay-100 ease-out',
'group-data-starting-style/error:opacity-0 group-data-starting-style/error:blur-[4px] group-data-starting-style/error:scale-95',
'group-data-ending-style/error:opacity-0 group-data-ending-style/error:blur-[4px] group-data-ending-style/error:scale-95',
'motion-reduce:duration-100 motion-reduce:delay-0'
),
content: 'flex flex-1 items-center gap-2',
};
/* ==========================================================================
Shared components (no overrides)
========================================================================== */
@@ -58,7 +78,6 @@ export { tooltipState } from '../../shared/tailwind/tooltip-state';
export { bufferingIndicator } from './components/buffering';
export { button } from './components/button';
export { buttonGroup } from './components/button-group';
export { error } from './components/error';
export { icon, iconContainer, iconFlipped, iconHidden } from './components/icon';
export { seek } from './components/seek';
export { slider } from './components/slider';
@@ -2,21 +2,23 @@ import { cn } from '@videojs/utils/style';
export const button = {
base: cn(
'items-center justify-center shrink-0 border-none cursor-pointer select-none text-center touch-manipulation',
'flex items-center justify-center shrink-0 border-none cursor-pointer select-none text-center touch-manipulation',
'py-2 px-4 rounded-[calc(var(--media-controls-radius)-var(--media-controls-padding))]',
'outline-2 outline-transparent -outline-offset-2',
'font-medium text-shadow-inherit',
'transition-[background-color,color,outline-offset,scale] duration-150 ease-out',
'transition-[background-color,outline-offset,scale] duration-150 ease-out',
'focus-visible:outline-current focus-visible:outline-offset-2',
'active:scale-[0.98]',
'disabled:cursor-not-allowed disabled:opacity-50 disabled:grayscale',
'supports-[corner-shape:squircle]:rounded-(--media-controls-radius)',
'supports-[corner-shape:squircle]:[corner-shape:squircle]',
'data-[availability=unavailable]:hidden'
),
icon: cn(
'grid w-[2.375rem] aspect-square bg-transparent rounded-lg',
'text-inherit',
'hover:text-current/80 hover:no-underline',
'focus-visible:text-current/80',
'aria-expanded:text-current/80',
'active:scale-90'
primary: 'bg-white text-black font-medium text-center',
subtle: cn(
'bg-transparent text-inherit text-shadow-inherit',
'hover:bg-current/10',
'focus-visible:bg-current/10',
'aria-expanded:bg-current/10'
),
default: cn('flex py-2 px-4 bg-white rounded-lg', 'text-black'),
icon: cn('grid w-[2.375rem] aspect-square p-0', 'active:scale-90'),
};
@@ -1,9 +1,9 @@
import { cn } from '@videojs/utils/style';
export const error = {
root: 'peer/error group/error flex absolute inset-0 z-20 items-center justify-center',
root: 'peer/error group/error flex absolute inset-0 z-20 items-center justify-center outline-none',
dialog: cn(
'flex flex-col gap-3 max-w-64 p-4 text-white text-sm',
'flex flex-col gap-3 max-w-64 p-4 text-white',
'text-shadow-2xs text-shadow-black/50',
// Animation
'transition-[opacity,scale,transform] duration-500 delay-100',
@@ -5,6 +5,7 @@ export const root = cn(
'block relative isolate @container/media-root',
// Appearance
'rounded-(--media-border-radius,0.75rem)',
'[--media-controls-radius:var(--media-border-radius,1rem)]',
'font-[Inter_Variable,Inter,ui-sans-serif,system-ui,sans-serif] text-[0.8125rem] leading-normal subpixel-antialiased',
// Resets
'**:box-border',
@@ -1,6 +1,6 @@
export const seek = {
button: '@max-md/media-controls:hidden',
label: 'text-[0.75em] font-[480] tabular-nums',
label: 'text-[10px] font-[480] tabular-nums',
labelForward: 'absolute -right-px -bottom-0.75',
labelBackward: 'absolute -left-px -bottom-0.75',
};
@@ -1,5 +1,6 @@
import { cn } from '@videojs/utils/style';
import { controls as baseControls } from './components/controls';
import { error as baseError } from './components/error';
import { popup as basePopup } from './components/popup';
import { preview as basePreview } from './components/preview';
import { root as baseRoot } from './components/root';
@@ -24,6 +25,7 @@ export const root = (isShadowDOM: boolean) =>
!isShadowDOM,
},
'[--media-video-border-radius:var(--media-border-radius,0.75rem)]',
'[--media-controls-padding:0.375rem]',
'[--media-controls-transition-duration:100ms]',
'[--media-controls-transition-delay:0ms]',
'[@media(pointer:fine)]:has-[[data-controls]:not([data-visible])]:[--media-controls-transition-delay:500ms]',
@@ -64,8 +66,9 @@ export const controls = cn(
baseControls,
// Position
'absolute bottom-0 inset-x-0',
'pt-8 px-1.5 pb-1.5 gap-2',
'pt-8 px-(--media-controls-padding) pb-(--media-controls-padding) gap-2',
'[color:var(--media-color-primary,oklch(1_0_0))] z-10',
'peer-data-open/error:hidden',
'ease-out',
'duration-(--media-controls-transition-duration)',
'delay-(--media-controls-transition-delay)',
@@ -82,6 +85,17 @@ export const controls = cn(
'@sm/media-root:gap-3.5'
);
/* ==========================================================================
Error
========================================================================== */
export const error = {
...baseError,
root: cn(baseError.root, 'pointer-events-none outline-none'),
dialog: cn(baseError.dialog, 'pointer-events-auto'),
title: cn(baseError.title, 'text-lg'),
};
/* ==========================================================================
Preview
========================================================================== */
@@ -132,7 +146,6 @@ export { tooltipState } from '../../shared/tailwind/tooltip-state';
export { bufferingIndicator } from './components/buffering';
export { button } from './components/button';
export { buttonGroup } from './components/button-group';
export { error } from './components/error';
export { icon, iconContainer, iconFlipped, iconHidden } from './components/icon';
export { overlay } from './components/overlay';
export { playbackRate } from './components/playback-rate';