mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat(ui): skin design tweaks (#126)
This commit is contained in:
@@ -2,11 +2,6 @@ import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import App from './App';
|
||||
import '@videojs/react/skins/frosted.css';
|
||||
|
||||
import '@videojs/react/skins/minimal.css';
|
||||
|
||||
import './globals.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
{/* Background gradient to help with controls contrast. */}
|
||||
<div className={styles.Overlay} aria-hidden="true" />
|
||||
|
||||
<div className={styles.Controls} data-testid="media-controls">
|
||||
<div className={`${styles.Surface} ${styles.Controls}`} data-testid="media-controls">
|
||||
<Tooltip.Root delay={500}>
|
||||
<Tooltip.Trigger>
|
||||
<PlayButton className={`${styles.Button} ${styles.IconButton} ${styles.PlayButton}`}>
|
||||
@@ -45,7 +45,7 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top" sideOffset={12} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup} ${styles.PlayTooltipPopup}`}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup} ${styles.Surface} ${styles.PopupAnimation} ${styles.PlayTooltipPopup}`}>
|
||||
<span className={styles.PlayTooltip}>Play</span>
|
||||
<span className={styles.PauseTooltip}>Pause</span>
|
||||
</Tooltip.Popup>
|
||||
@@ -62,7 +62,7 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
|
||||
<Tooltip.Root trackCursorAxis="x">
|
||||
<Tooltip.Trigger>
|
||||
<TimeSlider.Root className={`${styles.SliderRoot} ${styles.TimeSliderRoot}`}>
|
||||
<TimeSlider.Root className={styles.SliderRoot}>
|
||||
<TimeSlider.Track className={styles.SliderTrack}>
|
||||
<TimeSlider.Progress className={styles.SliderProgress} />
|
||||
<TimeSlider.Pointer className={styles.SliderPointer} />
|
||||
@@ -72,8 +72,8 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top" sideOffset={18} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup}`}>
|
||||
<PreviewTimeDisplay />
|
||||
<Tooltip.Popup className={`${styles.Surface} ${styles.PopupAnimation} ${styles.TooltipPopup}`}>
|
||||
<PreviewTimeDisplay className={styles.TimeDisplay} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Positioner>
|
||||
</Tooltip.Portal>
|
||||
@@ -92,8 +92,8 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Positioner side="top" sideOffset={12}>
|
||||
<Popover.Popup className={styles.PopoverPopup}>
|
||||
<VolumeSlider.Root className={`${styles.SliderRoot} ${styles.VolumeSliderRoot}`} orientation="vertical">
|
||||
<Popover.Popup className={`${styles.Surface} ${styles.PopupAnimation} ${styles.PopoverPopup}`}>
|
||||
<VolumeSlider.Root className={styles.SliderRoot} orientation="vertical">
|
||||
<VolumeSlider.Track className={styles.SliderTrack}>
|
||||
<VolumeSlider.Progress className={styles.SliderProgress} />
|
||||
</VolumeSlider.Track>
|
||||
@@ -113,7 +113,7 @@ export default function FrostedSkin({ children, className = '' }: SkinProps): JS
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top" sideOffset={12} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup} ${styles.FullscreenTooltipPopup}`}>
|
||||
<Tooltip.Popup className={`${styles.Surface} ${styles.PopupAnimation} ${styles.TooltipPopup} ${styles.FullscreenTooltipPopup}`}>
|
||||
<span className={styles.FullscreenEnterTooltip}>Enter Fullscreen</span>
|
||||
<span className={styles.FullscreenExitTooltip}>Exit Fullscreen</span>
|
||||
</Tooltip.Popup>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@layer theme, base, components, utilities;
|
||||
@import 'tailwindcss/theme.css' layer(theme) prefix(vjs);
|
||||
@import 'tailwindcss/utilities.css' layer(utilities) prefix(vjs);
|
||||
|
||||
/* Custom variants used by the frosted skin */
|
||||
@custom-variant reduced-transparency @media (prefers-reduced-transparency: reduce);
|
||||
.vjs {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ function cn(...classes: (string | undefined)[]): string {
|
||||
|
||||
const styles: FrostedSkinStyles = {
|
||||
MediaContainer: cn(
|
||||
'vjs', // scope preflight
|
||||
'vjs', // Scope preflight
|
||||
'vjs:relative vjs:isolate vjs:@container/root vjs:group/root vjs:overflow-clip',
|
||||
// Base typography
|
||||
'vjs:text-[0.8125rem]', // 13px
|
||||
@@ -31,19 +31,10 @@ const styles: FrostedSkinStyles = {
|
||||
'vjs:has-[+.controls_[aria-expanded="true"]]:opacity-100 vjs:has-[+.controls_[aria-expanded="true"]]:delay-0',
|
||||
'vjs:group-hover/root:opacity-100 vjs:group-hover/root:delay-0',
|
||||
),
|
||||
Controls: cn(
|
||||
'controls', // FIXME: Temporary className hook for above logic in the overlay. Can be removed once have a proper way to handle controls visibility.
|
||||
'vjs:@container/controls vjs:absolute vjs:inset-x-3 vjs:bottom-3 vjs:rounded-full vjs:flex vjs:items-center vjs:p-1 vjs:ring vjs:ring-white/10 vjs:ring-inset vjs:gap-0.5 vjs:text-white',
|
||||
'vjs:shadow-sm vjs:shadow-black/15',
|
||||
// Background
|
||||
Surface: cn(
|
||||
'vjs:bg-white/10 vjs:backdrop-blur-3xl vjs:backdrop-saturate-150 vjs:backdrop-brightness-90',
|
||||
// Animation
|
||||
'vjs:transition vjs:will-change-transform vjs:origin-bottom vjs:ease-out',
|
||||
// FIXME: Temporary hide/show logic
|
||||
'vjs:scale-90 vjs:opacity-0 vjs:delay-500 vjs:duration-300',
|
||||
'vjs:has-[[data-paused]]:scale-100 vjs:has-[[data-paused]]:opacity-100 vjs:has-[[data-paused]]:delay-0',
|
||||
'vjs:has-[[aria-expanded="true"]]:scale-100 vjs:has-[[aria-expanded="true"]]:opacity-100 vjs:has-[[aria-expanded="true"]]:delay-0',
|
||||
'vjs:group-hover/root:scale-100 vjs:group-hover/root:opacity-100 vjs:group-hover/root:delay-0',
|
||||
// Ring and shadow
|
||||
'vjs:ring vjs:ring-white/10 vjs:ring-inset vjs:shadow-sm vjs:shadow-black/15',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'vjs:after:absolute vjs:after:inset-0 vjs:after:ring vjs:after:rounded-[inherit] vjs:after:ring-black/15 vjs:after:pointer-events-none vjs:after:z-10',
|
||||
// Reduced transparency for users with preference
|
||||
@@ -52,6 +43,18 @@ const styles: FrostedSkinStyles = {
|
||||
// High contrast mode
|
||||
'vjs:contrast-more:bg-black/90 vjs:contrast-more:ring-black vjs:contrast-more:after:ring-white/20',
|
||||
),
|
||||
Controls: cn(
|
||||
'vjs:@container/controls vjs:absolute vjs:inset-x-3 vjs:bottom-3 vjs:rounded-full vjs:flex vjs:items-center vjs:p-1 vjs:gap-0.5 vjs:text-white',
|
||||
// Animation
|
||||
'vjs:transition vjs:will-change-transform vjs:origin-bottom vjs:ease-out',
|
||||
// FIXME: Temporary className hook for above logic in the overlay. Can be removed once have a proper way to handle controls visibility.
|
||||
'controls',
|
||||
// FIXME: Temporary hide/show logic
|
||||
'vjs:scale-90 vjs:opacity-0 vjs:delay-500 vjs:duration-300',
|
||||
'vjs:has-[[data-paused]]:scale-100 vjs:has-[[data-paused]]:opacity-100 vjs:has-[[data-paused]]:delay-0',
|
||||
'vjs:has-[[aria-expanded="true"]]:scale-100 vjs:has-[[aria-expanded="true"]]:opacity-100 vjs:has-[[aria-expanded="true"]]:delay-0',
|
||||
'vjs:group-hover/root:scale-100 vjs:group-hover/root:opacity-100 vjs:group-hover/root:delay-0',
|
||||
),
|
||||
Icon: cn('icon'),
|
||||
Button: cn(
|
||||
'vjs:group/button vjs:cursor-pointer vjs:relative vjs:shrink-0 vjs:transition vjs:select-none vjs:p-2 vjs:rounded-full',
|
||||
@@ -72,25 +75,14 @@ const styles: FrostedSkinStyles = {
|
||||
),
|
||||
IconButton: cn(
|
||||
'vjs:grid vjs:[&_.icon]:[grid-area:1/1]',
|
||||
'vjs:[&_.icon]:shrink-0 vjs:[&_.icon]:transition-opacity vjs:[&_.icon]:duration-300 vjs:[&_.icon]:ease-out vjs:[&_.icon]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] vjs:[&_.icon]:shadow-black/25',
|
||||
'vjs:[&_.icon]:shrink-0 vjs:[&_.icon]:transition-[opacity,filter] vjs:[&_.icon]:duration-500 vjs:[&_.icon]:linear vjs:[&_.icon]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] vjs:[&_.icon]:shadow-black/25',
|
||||
),
|
||||
PlayButton: cn(
|
||||
'vjs:[&_.pause-icon]:opacity-100 vjs:[&[data-paused]_.pause-icon]:opacity-0',
|
||||
'vjs:[&_.play-icon]:opacity-0 vjs:[&[data-paused]_.play-icon]:opacity-100',
|
||||
'vjs:[&_.pause-icon]:opacity-100 vjs:[&[data-paused]_.pause-icon]:opacity-0 vjs:[&[data-paused]_.pause-icon]:blur-sm',
|
||||
'vjs:[&_.play-icon]:opacity-0 vjs:[&_.play-icon]:blur-sm vjs:[&[data-paused]_.play-icon]:opacity-100 vjs:[&[data-paused]_.play-icon]:blur-none',
|
||||
),
|
||||
PlayIcon: cn('play-icon'),
|
||||
PauseIcon: cn('pause-icon'),
|
||||
TooltipPopup: cn(
|
||||
'vjs:whitespace-nowrap vjs:flex vjs:origin-[var(--transform-origin)] vjs:flex-col vjs:rounded-md vjs:text-white vjs:text-xs vjs:@7xl/root:text-sm vjs:px-2 vjs:py-1',
|
||||
// Background
|
||||
'vjs:bg-white/10 vjs:backdrop-blur-3xl vjs:backdrop-saturate-150 vjs:backdrop-brightness-90',
|
||||
// Animation
|
||||
'vjs:transition-[transform,scale,opacity] vjs:data-[ending-style]:scale-90 vjs:data-[ending-style]:opacity-0 vjs:data-[instant]:duration-0 vjs:data-[starting-style]:scale-90 vjs:data-[starting-style]:opacity-0',
|
||||
// Ring
|
||||
'vjs:ring-1 vjs:ring-white/10 vjs:ring-inset',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'vjs:after:absolute vjs:after:inset-0 vjs:after:ring vjs:after:rounded-[inherit] vjs:after:ring-black/15 vjs:after:pointer-events-none',
|
||||
),
|
||||
PlayTooltipPopup: cn(
|
||||
'vjs:[&_.pause-tooltip]:inline vjs:[&[data-paused]_.pause-tooltip]:hidden',
|
||||
'vjs:[&_.play-tooltip]:hidden vjs:[&[data-paused]_.play-tooltip]:inline',
|
||||
@@ -98,11 +90,11 @@ const styles: FrostedSkinStyles = {
|
||||
PlayTooltip: cn('play-tooltip'),
|
||||
PauseTooltip: cn('pause-tooltip'),
|
||||
MuteButton: cn(
|
||||
'vjs:[&_.icon]:opacity-0',
|
||||
'vjs:[&[data-volume-level="high"]_.volume-high-icon]:opacity-100',
|
||||
'vjs:[&[data-volume-level="medium"]_.volume-low-icon]:opacity-100',
|
||||
'vjs:[&[data-volume-level="low"]_.volume-low-icon]:opacity-100',
|
||||
'vjs:[&[data-volume-level="off"]_.volume-off-icon]:opacity-100',
|
||||
'vjs:[&_.icon]:hidden',
|
||||
'vjs:[&[data-volume-level="high"]_.volume-high-icon]:inline',
|
||||
'vjs:[&[data-volume-level="medium"]_.volume-low-icon]:inline',
|
||||
'vjs:[&[data-volume-level="low"]_.volume-low-icon]:inline',
|
||||
'vjs:[&[data-volume-level="off"]_.volume-off-icon]:inline',
|
||||
),
|
||||
VolumeHighIcon: cn('volume-high-icon'),
|
||||
VolumeLowIcon: cn('volume-low-icon'),
|
||||
@@ -133,13 +125,14 @@ const styles: FrostedSkinStyles = {
|
||||
TimeControls: cn('vjs:flex-1 vjs:flex vjs:items-center vjs:gap-3 vjs:px-1.5'),
|
||||
TimeDisplay: cn('vjs:tabular-nums vjs:text-shadow-2xs/25'),
|
||||
SliderRoot: cn(
|
||||
'vjs:group/slider-root',
|
||||
'vjs:outline-0',
|
||||
'vjs:flex vjs:items-center vjs:justify-center vjs:flex-1 vjs:group/slider vjs:relative vjs:rounded-full',
|
||||
'vjs:[&[data-orientation="horizontal"]]:h-5 vjs:[&[data-orientation="horizontal"]]:min-w-20',
|
||||
'vjs:[&[data-orientation="vertical"]]:w-5 vjs:[&[data-orientation="vertical"]]:h-20',
|
||||
// Focus state
|
||||
'vjs:focus-visible:outline-2 vjs:focus-visible:outline-blue-500',
|
||||
),
|
||||
SliderTrack: cn(
|
||||
'vjs:-outline-offset-2 vjs:group-focus-visible/slider-root:outline-2 vjs:group-focus-visible/slider-root:outline-offset-2 vjs:group-focus-visible/slider-root:outline-blue-500',
|
||||
'vjs:w-full vjs:relative vjs:select-none vjs:rounded-full vjs:bg-white/20 vjs:ring-1 vjs:ring-black/5',
|
||||
'vjs:[&[data-orientation="horizontal"]]:h-1',
|
||||
'vjs:[&[data-orientation="vertical"]]:w-1',
|
||||
@@ -156,27 +149,18 @@ const styles: FrostedSkinStyles = {
|
||||
'vjs:[&[data-orientation="horizontal"]]:hover:cursor-ew-resize',
|
||||
'vjs:[&[data-orientation="vertical"]]:hover:cursor-ns-resize',
|
||||
),
|
||||
TimeSliderRoot: cn(
|
||||
// Focus state
|
||||
'vjs:-outline-offset-8 vjs:focus-visible:outline-offset-8',
|
||||
),
|
||||
VolumeSliderRoot: cn(
|
||||
// Focus state
|
||||
'vjs:-outline-offset-4 vjs:focus-visible:outline-offset-4',
|
||||
PopupAnimation: cn(
|
||||
// Animation
|
||||
// XXX: We can't use transforms since floating UI uses them for positioning.
|
||||
'vjs:transition-[transform,scale,opacity,filter] vjs:origin-bottom vjs:duration-200 vjs:data-[instant]:duration-0',
|
||||
'vjs:data-[starting-style]:scale-0 vjs:data-[starting-style]:opacity-0 vjs:data-[starting-style]:blur-sm',
|
||||
'vjs:data-[ending-style]:scale-0 vjs:data-[ending-style]:opacity-0 vjs:data-[ending-style]:blur-sm',
|
||||
),
|
||||
PopoverPopup: cn(
|
||||
'vjs:relative vjs:px-1.5 vjs:py-3.5 vjs:rounded-full',
|
||||
'vjs:bg-white/10 vjs:backdrop-blur-3xl vjs:backdrop-saturate-150 vjs:backdrop-brightness-90',
|
||||
'vjs:ring vjs:ring-white/10 vjs:ring-inset vjs:shadow-sm vjs:shadow-black/15',
|
||||
// Animation
|
||||
'vjs:transition-[transform,scale,opacity] vjs:data-[ending-style]:scale-90 vjs:data-[ending-style]:opacity-0 vjs:data-[instant]:duration-0 vjs:data-[starting-style]:scale-90 vjs:data-[starting-style]:opacity-0',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'vjs:after:absolute vjs:after:inset-0 vjs:after:ring vjs:after:rounded-[inherit] vjs:after:ring-black/15 vjs:after:pointer-events-none vjs:after:z-10',
|
||||
// Reduced transparency for users with preference
|
||||
// XXX: This requires a Tailwind custom variant (see 1 below)
|
||||
'vjs:reduced-transparency:bg-black/70 vjs:reduced-transparency:ring-black vjs:reduced-transparency:after:ring-white/20',
|
||||
// High contrast mode
|
||||
'vjs:contrast-more:bg-black/90 vjs:contrast-more:ring-black vjs:contrast-more:after:ring-white/20',
|
||||
'vjs:relative vjs:px-1 vjs:py-3 vjs:rounded-full',
|
||||
),
|
||||
TooltipPopup: cn(
|
||||
'vjs:whitespace-nowrap vjs:flex vjs:flex-col vjs:rounded-full vjs:text-white vjs:text-xs vjs:@7xl/root:text-sm vjs:px-2.5 vjs:py-1',
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface FrostedSkinStyles {
|
||||
readonly MediaContainer: string;
|
||||
readonly Surface: string;
|
||||
readonly Overlay: string;
|
||||
readonly Controls: string;
|
||||
readonly Icon: string;
|
||||
@@ -11,7 +12,6 @@ export interface FrostedSkinStyles {
|
||||
readonly PlayTooltipPopup: string;
|
||||
readonly PlayTooltip: string;
|
||||
readonly PauseTooltip: string;
|
||||
readonly TooltipPopup: string;
|
||||
readonly MuteButton: string;
|
||||
readonly VolumeHighIcon: string;
|
||||
readonly VolumeLowIcon: string;
|
||||
@@ -27,9 +27,9 @@ export interface FrostedSkinStyles {
|
||||
readonly SliderProgress: string;
|
||||
readonly SliderPointer: string;
|
||||
readonly SliderThumb: string;
|
||||
readonly TimeSliderRoot: string;
|
||||
readonly VolumeSliderRoot: string;
|
||||
readonly PopoverPopup: string;
|
||||
readonly TimeControls: string;
|
||||
readonly TimeDisplay: string;
|
||||
readonly PopupAnimation: string;
|
||||
readonly PopoverPopup: string;
|
||||
readonly TooltipPopup: string;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ import type { PropsWithChildren } from 'react';
|
||||
|
||||
import Popover from '@/components/Popover';
|
||||
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import PreviewTimeDisplay from '@/components/PreviewTimeDisplay';
|
||||
|
||||
import Tooltip from '@/components/Tooltip';
|
||||
import VolumeSlider from '@/components/VolumeSlider';
|
||||
import {
|
||||
FullscreenEnterAltIcon,
|
||||
@@ -44,7 +45,7 @@ export default function MinimalSkin({ children, className = '' }: SkinProps): JS
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top-start" sideOffset={6} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup} ${styles.PlayTooltipPopup}`}>
|
||||
<Tooltip.Popup className={`${styles.PopupAnimation} ${styles.TooltipPopup} ${styles.PlayTooltipPopup}`}>
|
||||
<span className={styles.PlayTooltip}>Play</span>
|
||||
<span className={styles.PauseTooltip}>Pause</span>
|
||||
</Tooltip.Popup>
|
||||
@@ -65,13 +66,24 @@ export default function MinimalSkin({ children, className = '' }: SkinProps): JS
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<TimeSlider.Root className={`${styles.SliderRoot} ${styles.TimeSliderRoot}`}>
|
||||
<TimeSlider.Track className={styles.SliderTrack}>
|
||||
<TimeSlider.Progress className={styles.SliderProgress} />
|
||||
<TimeSlider.Pointer className={styles.SliderPointer} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className={styles.SliderThumb} />
|
||||
</TimeSlider.Root>
|
||||
<Tooltip.Root trackCursorAxis="x">
|
||||
<Tooltip.Trigger>
|
||||
<TimeSlider.Root className={styles.SliderRoot}>
|
||||
<TimeSlider.Track className={styles.SliderTrack}>
|
||||
<TimeSlider.Progress className={styles.SliderProgress} />
|
||||
<TimeSlider.Pointer className={styles.SliderPointer} />
|
||||
</TimeSlider.Track>
|
||||
<TimeSlider.Thumb className={styles.SliderThumb} />
|
||||
</TimeSlider.Root>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top" sideOffset={12} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.PopupAnimation} ${styles.TooltipPopup}`}>
|
||||
<PreviewTimeDisplay className={styles.TimeDisplay} />
|
||||
</Tooltip.Popup>
|
||||
</Tooltip.Positioner>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
||||
<div className={styles.ButtonGroup}>
|
||||
<Popover.Root openOnHover delay={200} closeDelay={300}>
|
||||
@@ -84,7 +96,7 @@ export default function MinimalSkin({ children, className = '' }: SkinProps): JS
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Positioner side="top" sideOffset={2}>
|
||||
<Popover.Popup className={styles.PopoverPopup}>
|
||||
<Popover.Popup className={`${styles.PopupAnimation} ${styles.PopoverPopup}`}>
|
||||
<VolumeSlider.Root className={styles.SliderRoot} orientation="vertical">
|
||||
<VolumeSlider.Track className={styles.SliderTrack}>
|
||||
<VolumeSlider.Progress className={styles.SliderProgress} />
|
||||
@@ -105,7 +117,7 @@ export default function MinimalSkin({ children, className = '' }: SkinProps): JS
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Positioner side="top-end" sideOffset={6} collisionPadding={12}>
|
||||
<Tooltip.Popup className={`${styles.TooltipPopup} ${styles.FullscreenTooltipPopup}`}>
|
||||
<Tooltip.Popup className={`${styles.PopupAnimation} ${styles.TooltipPopup} ${styles.FullscreenTooltipPopup}`}>
|
||||
<span className={styles.FullscreenEnterTooltip}>Enter Fullscreen</span>
|
||||
<span className={styles.FullscreenExitTooltip}>Exit Fullscreen</span>
|
||||
</Tooltip.Popup>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
@layer theme, base, components, utilities;
|
||||
@import 'tailwindcss/theme.css' layer(theme) prefix(vjs);
|
||||
@import 'tailwindcss/utilities.css' layer(utilities) prefix(vjs);
|
||||
.vjs {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ function cn(...classes: (string | undefined)[]): string {
|
||||
|
||||
const styles: MinimalSkinStyles = {
|
||||
MediaContainer: cn(
|
||||
'vjs', // scope preflight
|
||||
'vjs', // Scope preflight
|
||||
'vjs:relative vjs:isolate vjs:@container/root vjs:group/root vjs:overflow-clip vjs:bg-black',
|
||||
// Base typography
|
||||
'vjs:text-[0.8125rem]', // 13px
|
||||
@@ -63,20 +63,14 @@ const styles: MinimalSkinStyles = {
|
||||
ButtonGroup: cn('vjs:flex vjs:items-center vjs:gap-1.5'),
|
||||
IconButton: cn(
|
||||
'vjs:grid vjs:[&_.icon]:[grid-area:1/1]',
|
||||
'vjs:[&_.icon]:shrink-0 vjs:[&_.icon]:transition vjs:[&_.icon]:duration-300 vjs:[&_.icon]:ease-out vjs:[&_.icon]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] vjs:[&_.icon]:shadow-black/40',
|
||||
'vjs:[&_.icon]:shrink-0 vjs:[&_.icon]:transition-[opacity,filter] vjs:[&_.icon]:duration-300 vjs:[&_.icon]:linear vjs:[&_.icon]:drop-shadow-[0_1px_0_var(--tw-shadow-color)] vjs:[&_.icon]:shadow-black/40',
|
||||
),
|
||||
PlayButton: cn(
|
||||
'vjs:[&_.pause-icon]:opacity-100 vjs:[&[data-paused]_.pause-icon]:opacity-0',
|
||||
'vjs:[&_.play-icon]:opacity-0 vjs:[&[data-paused]_.play-icon]:opacity-100',
|
||||
'vjs:[&_.pause-icon]:opacity-100 vjs:[&_.pause-icon]:blur-none vjs:[&[data-paused]_.pause-icon]:opacity-0 vjs:[&[data-paused]_.pause-icon]:blur-xs',
|
||||
'vjs:[&_.play-icon]:opacity-0 vjs:[&_.play-icon]:blur-xs vjs:[&[data-paused]_.play-icon]:opacity-100 vjs:[&[data-paused]_.play-icon]:blur-none',
|
||||
),
|
||||
PlayIcon: cn('play-icon'),
|
||||
PauseIcon: cn('pause-icon'),
|
||||
TooltipPopup: cn(
|
||||
'vjs:whitespace-nowrap vjs:flex vjs:origin-[var(--transform-origin)] vjs:flex-col vjs:rounded-md vjs:text-white vjs:text-xs vjs:@7xl/root:text-sm vjs:px-2 vjs:py-1',
|
||||
'vjs:bg-black/10 vjs:backdrop-blur-md',
|
||||
// Animation
|
||||
'vjs:transition-[transform,scale,opacity] vjs:data-[ending-style]:scale-90 vjs:data-[ending-style]:opacity-0 vjs:data-[instant]:duration-0 vjs:data-[starting-style]:scale-90 vjs:data-[starting-style]:opacity-0',
|
||||
),
|
||||
PlayTooltipPopup: cn(
|
||||
'vjs:[&_.pause-tooltip]:inline vjs:[&[data-paused]_.pause-tooltip]:hidden',
|
||||
'vjs:[&_.play-tooltip]:hidden vjs:[&[data-paused]_.play-tooltip]:inline',
|
||||
@@ -140,10 +134,18 @@ const styles: MinimalSkinStyles = {
|
||||
'vjs:[&[data-orientation="horizontal"]]:hover:cursor-ew-resize',
|
||||
'vjs:[&[data-orientation="vertical"]]:hover:cursor-ns-resize',
|
||||
),
|
||||
PopupAnimation: cn(
|
||||
// Animation
|
||||
// XXX: We can't use transforms since floating UI uses them for positioning.
|
||||
'vjs:transition-[transform,scale,opacity,filter] vjs:origin-bottom vjs:duration-200 vjs:data-[instant]:duration-0',
|
||||
'vjs:data-[starting-style]:scale-0 vjs:data-[starting-style]:opacity-0 vjs:data-[starting-style]:blur-sm',
|
||||
'vjs:data-[ending-style]:scale-0 vjs:data-[ending-style]:opacity-0 vjs:data-[ending-style]:blur-sm',
|
||||
),
|
||||
PopoverPopup: cn(
|
||||
'vjs:py-2',
|
||||
// Animation
|
||||
'vjs:transition-[transform,scale,opacity] vjs:data-[ending-style]:scale-90 vjs:data-[ending-style]:opacity-0 vjs:data-[instant]:duration-0 vjs:data-[starting-style]:scale-90 vjs:data-[starting-style]:opacity-0',
|
||||
),
|
||||
TooltipPopup: cn(
|
||||
'vjs:whitespace-nowrap vjs:flex vjs:flex-col vjs:rounded vjs:text-white vjs:text-xs vjs:@7xl/root:text-sm vjs:px-2 vjs:py-1 vjs:bg-white/20 vjs:backdrop-blur-3xl vjs:backdrop-saturate-150 vjs:backdrop-brightness-90 vjs:shadow-md vjs:shadow-black/5',
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ export interface MinimalSkinStyles {
|
||||
readonly PlayTooltipPopup: string;
|
||||
readonly PlayTooltip: string;
|
||||
readonly PauseTooltip: string;
|
||||
readonly TooltipPopup: string;
|
||||
readonly MuteButton: string;
|
||||
readonly VolumeHighIcon: string;
|
||||
readonly VolumeLowIcon: string;
|
||||
@@ -23,7 +22,6 @@ export interface MinimalSkinStyles {
|
||||
readonly FullscreenTooltipPopup: string;
|
||||
readonly FullscreenEnterTooltip: string;
|
||||
readonly FullscreenExitTooltip: string;
|
||||
readonly PopoverPopup: string;
|
||||
readonly TimeSliderRoot: string;
|
||||
readonly TimeDisplay: string;
|
||||
readonly DurationDisplay: string;
|
||||
@@ -32,4 +30,7 @@ export interface MinimalSkinStyles {
|
||||
readonly SliderProgress: string;
|
||||
readonly SliderPointer: string;
|
||||
readonly SliderThumb: string;
|
||||
readonly PopupAnimation: string;
|
||||
readonly PopoverPopup: string;
|
||||
readonly TooltipPopup: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user