mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(react): support native caption track shifting in video skins (#636)
This commit is contained in:
@@ -573,16 +573,8 @@
|
||||
z-index: 20;
|
||||
font-size: 1rem;
|
||||
text-wrap: balance;
|
||||
/* The delay must account for the controls delay/duration */
|
||||
transition: transform 150ms ease-out;
|
||||
transition-delay: 600ms;
|
||||
pointer-events: none;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.media-minimal-skin .media-captions {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
}
|
||||
.media-minimal-skin .media-captions__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -630,8 +622,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Shift captions up when controls visible */
|
||||
/* Caption shifting styles (custom and native) */
|
||||
.media-minimal-skin {
|
||||
--media-caption-track-delay: 600ms;
|
||||
--media-caption-track-y: -0.5rem;
|
||||
}
|
||||
.media-minimal-skin:has(.media-controls[data-visible]) {
|
||||
--media-caption-track-delay: 25ms;
|
||||
--media-caption-track-y: -3rem;
|
||||
}
|
||||
.media-minimal-skin .media-captions,
|
||||
.media-minimal-skin video::-webkit-media-text-track-container {
|
||||
/* NOTE: The delay must account for the controls delay/duration */
|
||||
transition: transform 150ms ease-out;
|
||||
transition-delay: var(--media-caption-track-delay);
|
||||
}
|
||||
.media-minimal-skin video::-webkit-media-text-track-container {
|
||||
transform: translateY(var(--media-caption-track-y)) scale(0.98);
|
||||
z-index: 1;
|
||||
font-family: inherit;
|
||||
}
|
||||
/* When controls are visible, shift captions up to avoid overlap */
|
||||
.media-minimal-skin .media-controls[data-visible] ~ .media-captions {
|
||||
transform: translateY(-2.5rem);
|
||||
transition-delay: 25ms;
|
||||
transform: translateY(calc(var(--media-caption-track-y) - 0.5rem));
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.media-minimal-skin .media-captions,
|
||||
.media-minimal-skin video::-webkit-media-text-track-container {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,14 +149,27 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
'[&>img]:object-cover [&>img]:pointer-events-none',
|
||||
'[&>img]:transition-opacity [&>img]:duration-[250ms]',
|
||||
'[&>img:not([data-visible])]:opacity-0',
|
||||
// Caption track CSS variables (consumed by native track container below)
|
||||
'[--media-caption-track-delay:600ms]',
|
||||
'[--media-caption-track-y:-0.5rem]',
|
||||
'has-[[data-controls][data-visible]]:[--media-caption-track-delay:25ms]',
|
||||
'has-[[data-controls][data-visible]]:[--media-caption-track-y:-3.5rem]',
|
||||
// Native caption track container
|
||||
'[&_video::-webkit-media-text-track-container]:transition-transform',
|
||||
'[&_video::-webkit-media-text-track-container]:duration-150',
|
||||
'[&_video::-webkit-media-text-track-container]:ease-out',
|
||||
'[&_video::-webkit-media-text-track-container]:delay-(--media-caption-track-delay)',
|
||||
'[&_video::-webkit-media-text-track-container]:translate-y-(--media-caption-track-y)',
|
||||
'[&_video::-webkit-media-text-track-container]:scale-98',
|
||||
'[&_video::-webkit-media-text-track-container]:z-1',
|
||||
'[&_video::-webkit-media-text-track-container]:font-[inherit]',
|
||||
'motion-reduce:[&_video::-webkit-media-text-track-container]:duration-50',
|
||||
// Fullscreen
|
||||
'[&:fullscreen]:rounded-none',
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props, state) =>
|
||||
state.visible ? (
|
||||
@@ -203,6 +216,7 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
/>
|
||||
|
||||
<Controls.Root
|
||||
data-controls="" // Used as a hook for Tailwind has-[] styles
|
||||
className={cn(
|
||||
// Peer marker for overlay/captions
|
||||
'peer/controls',
|
||||
@@ -368,6 +382,8 @@ export function MinimalVideoSkinTailwind(props: MinimalVideoSkinProps): ReactNod
|
||||
'motion-reduce:duration-100'
|
||||
)}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,8 +84,6 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
|
||||
return (
|
||||
<Container className={cn('media-minimal-skin', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props, state) =>
|
||||
state.visible ? (
|
||||
@@ -209,6 +207,8 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
|
||||
</div> */}
|
||||
|
||||
<div className="media-overlay" />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -574,16 +574,8 @@
|
||||
z-index: 20;
|
||||
font-size: 1rem;
|
||||
text-wrap: balance;
|
||||
/* The delay must account for the controls delay/duration */
|
||||
transition: transform 150ms ease-out;
|
||||
transition-delay: 600ms;
|
||||
pointer-events: none;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.media-default-skin .media-captions {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
}
|
||||
.media-default-skin .media-captions__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -631,8 +623,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Shift captions up when controls visible */
|
||||
/* Caption shifting styles (custom and native) */
|
||||
.media-default-skin {
|
||||
--media-caption-track-delay: 600ms;
|
||||
--media-caption-track-y: -0.5rem;
|
||||
}
|
||||
.media-default-skin:has(.media-controls[data-visible]) {
|
||||
--media-caption-track-delay: 25ms;
|
||||
--media-caption-track-y: -3.5rem;
|
||||
}
|
||||
.media-default-skin .media-captions,
|
||||
.media-default-skin video::-webkit-media-text-track-container {
|
||||
/* NOTE: The delay must account for the controls delay/duration */
|
||||
transition: transform 150ms ease-out;
|
||||
transition-delay: var(--media-caption-track-delay);
|
||||
}
|
||||
.media-default-skin video::-webkit-media-text-track-container {
|
||||
transform: translateY(var(--media-caption-track-y)) scale(0.98);
|
||||
z-index: 1;
|
||||
font-family: inherit;
|
||||
}
|
||||
/* When controls are visible, shift captions up to avoid overlap */
|
||||
.media-default-skin .media-controls[data-visible] ~ .media-captions {
|
||||
transform: translateY(-3rem);
|
||||
transition-delay: 25ms;
|
||||
transform: translateY(calc(var(--media-caption-track-y) - 0.5rem));
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.media-default-skin .media-captions,
|
||||
.media-default-skin video::-webkit-media-text-track-container {
|
||||
transition-duration: 50ms;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,14 +165,27 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
'[&>img]:object-cover [&>img]:pointer-events-none',
|
||||
'[&>img]:transition-opacity [&>img]:duration-[250ms]',
|
||||
'[&>img:not([data-visible])]:opacity-0',
|
||||
// Caption track CSS variables (consumed by native track container below)
|
||||
'[--media-caption-track-delay:600ms]',
|
||||
'[--media-caption-track-y:-0.5rem]',
|
||||
'has-[[data-controls][data-visible]]:[--media-caption-track-delay:25ms]',
|
||||
'has-[[data-controls][data-visible]]:[--media-caption-track-y:-3.5rem]',
|
||||
// Native caption track container
|
||||
'[&_video::-webkit-media-text-track-container]:transition-transform',
|
||||
'[&_video::-webkit-media-text-track-container]:duration-150',
|
||||
'[&_video::-webkit-media-text-track-container]:ease-out',
|
||||
'[&_video::-webkit-media-text-track-container]:delay-(--media-caption-track-delay)',
|
||||
'[&_video::-webkit-media-text-track-container]:translate-y-(--media-caption-track-y)',
|
||||
'[&_video::-webkit-media-text-track-container]:scale-98',
|
||||
'[&_video::-webkit-media-text-track-container]:z-1',
|
||||
'[&_video::-webkit-media-text-track-container]:font-[inherit]',
|
||||
'motion-reduce:[&_video::-webkit-media-text-track-container]:duration-50',
|
||||
// Fullscreen
|
||||
'[&:fullscreen]:rounded-none',
|
||||
className
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props, state) =>
|
||||
state.visible ? (
|
||||
@@ -223,6 +236,7 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
/>
|
||||
|
||||
<Controls.Root
|
||||
data-controls="" // Used as a hook for Tailwind has-[] styles
|
||||
className={cn(
|
||||
// Peer marker for overlay/captions
|
||||
'peer/controls',
|
||||
@@ -374,6 +388,8 @@ export function VideoSkinTailwind(props: VideoSkinProps): ReactNode {
|
||||
'motion-reduce:duration-100'
|
||||
)}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,8 +84,6 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
|
||||
return (
|
||||
<Container className={cn('media-default-skin', className)} {...rest}>
|
||||
{children}
|
||||
|
||||
<BufferingIndicator
|
||||
render={(props, state) =>
|
||||
state.visible ? (
|
||||
@@ -203,6 +201,8 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
|
||||
</div> */}
|
||||
|
||||
<div className="media-overlay" />
|
||||
|
||||
{children}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user