.media-sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; white-space: nowrap; border: 0; clip: rect(0, 0, 0, 0); } /* ========================================================================== Reset ========================================================================== */ .media-default-skin *, .media-default-skin *::before, .media-default-skin *::after { box-sizing: border-box; } .media-default-skin img, .media-default-skin video, .media-default-skin svg { display: block; max-width: 100%; } .media-default-skin button { font: inherit; } .media-default-skin [hidden][hidden] { /* Keep authored templates hidden even when component classes set display. */ display: none; } @media (prefers-reduced-motion: no-preference) { .media-default-skin { interpolate-size: allow-keywords; } } /* ========================================================================== Root Container ========================================================================== */ .media-default-skin { /* Colors */ --media-current-shadow-color: oklch(from currentColor 0 0 0 / clamp(0, calc((l - 0.5) * 0.5), 0.15)); --media-current-shadow-color-subtle: oklch(from var(--media-current-shadow-color) l c h / calc(alpha * 0.4)); --scrollbar-thumb-color: oklch(from currentColor l c h / 0.3); /* Scaling (used in video fullscreen) */ --scale: 1; /* What value and unit to use - 1rem assumes the document font size remains at the default 16px */ --base-size: 1rem; --size: calc(var(--base-size) * var(--scale)); /* Create a Tailwind-like spacing unit */ --spacing: calc(var(--size) / 4); --font-size-medium: calc(0.9375 * var(--size)); /* 15px */ --font-size-base: calc(0.8125 * var(--size)); /* 13px */ --font-size-small: calc(0.6875 * var(--size)); /* 11px */ --font-size-tiny: calc(0.5625 * var(--size)); /* 9px */ --media-icon-size: calc(1.125 * var(--size)); /* 18px */ position: relative; display: block; width: 100%; height: 100%; container: media-root / inline-size; font-family: Inter Variable, Inter, ui-sans-serif, system-ui, sans-serif; font-size: var(--font-size-base); -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; line-height: 1.5; letter-spacing: normal; outline: 2px solid transparent; outline-offset: -4px; scrollbar-color: var(--scrollbar-thumb-color) transparent; scrollbar-width: thin; border-radius: var(--media-border-radius, 1.75rem); isolation: isolate; transition-timing-function: ease-out; transition-duration: 100ms; transition-property: outline-offset, outline-color; &:focus-visible { outline-color: currentColor; outline-offset: 2px; } &::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb-color); border-radius: 9999px; } @media (prefers-reduced-transparency: reduce) or (prefers-contrast: more) { --scrollbar-thumb-color: oklch(from currentColor l c h / 0.8); scrollbar-width: auto; } } /* ========================================================================== Surface (shared glass effect for tooltips, popovers, controls) ========================================================================== */ .media-default-skin .media-surface { background-color: var(--media-surface-background-color); box-shadow: 0 0 0 1px var(--media-surface-outer-border-color), 0 1px 3px 0 var(--media-surface-shadow-color), 0 1px 2px -1px var(--media-surface-shadow-color); backdrop-filter: var(--media-surface-backdrop-filter); /* Inner border ring */ &::after { position: absolute; inset: 0; z-index: 10; pointer-events: none; content: ""; border-radius: inherit; box-shadow: inset 0 1px 0 0 var(--media-surface-inner-border-color), inset 0 0 0 1px oklch(from var(--media-surface-inner-border-color) l c h / calc(alpha * 0.5)); } } /* ========================================================================== Media Element ========================================================================== */ .media-default-skin ::slotted(video), .media-default-skin video { display: block; width: 100%; height: 100%; object-fit: var(--media-object-fit, contain); object-position: var(--media-object-position, center); } .media-default-skin ::slotted(video) { border-radius: var(--media-video-border-radius); } .media-default-skin video { border-radius: inherit; } .media-default-skin:fullscreen ::slotted(video), .media-default-skin:fullscreen video { object-fit: contain; } /* ========================================================================== Overlay / Scrim ========================================================================== */ .media-default-skin .media-overlay { position: absolute; inset: 0; pointer-events: none; background-image: linear-gradient(to top, oklch(0 0 0 / 0.5), oklch(0 0 0 / 0.3) 25%, oklch(0 0 0 / 0)); border-radius: inherit; opacity: 0; backdrop-filter: blur(0) saturate(1); transition-timing-function: ease-out; transition-duration: var(--media-controls-transition-duration); transition-property: opacity, backdrop-filter; } .media-default-skin .media-error ~ .media-overlay { transition-delay: var(--media-error-dialog-transition-delay); transition-duration: var(--media-error-dialog-transition-duration); } .media-default-skin .media-controls[data-visible] ~ .media-overlay, .media-default-skin .media-error[data-open] ~ .media-overlay { opacity: 1; } .media-default-skin .media-buffering-indicator[data-visible] ~ .media-overlay { background: oklch(0 0 0 / 0.35); opacity: 1; backdrop-filter: blur(8px); } .media-default-skin .media-error[data-open] ~ .media-overlay { backdrop-filter: blur(16px) saturate(1.5); } /* ========================================================================== Buffering Indicator ========================================================================== */ .media-default-skin .media-buffering-indicator { position: absolute; inset: 0; z-index: 10; display: none; place-content: center; color: oklch(1 0 0); pointer-events: none; &:not([data-visible]) { --media-spinner-animation: none; } &[data-visible] { display: grid; } } /* ========================================================================== Error Dialog ========================================================================== */ .media-default-skin .media-error { outline: none; } .media-default-skin .media-error:not([data-open]) { display: none; } .media-default-skin .media-error__title { font-weight: 600; line-height: 1.25; } .media-default-skin .media-error__description { overflow-wrap: anywhere; opacity: 0.7; } .media-default-skin .media-error__actions { display: flex; gap: calc(var(--spacing) * 2); & > * { flex: 1; } } .media-default-skin .media-error[data-open] ~ .media-controls * { visibility: hidden; } /* ========================================================================== Controls ========================================================================== */ .media-default-skin .media-controls { --media-popover-side-offset: calc(var(--spacing) * (var(--base-side-offset, 2) + 1)); --media-tooltip-side-offset: var(--media-popover-side-offset); --media-popover-boundary-offset: calc(var(--spacing) * var(--base-boundary-offset, 2)); --media-tooltip-boundary-offset: var(--media-popover-boundary-offset); display: flex; align-items: center; padding: calc(var(--spacing) * 1); container: media-controls / inline-size; text-shadow: 0 1px 0 var(--media-current-shadow-color); border-radius: calc(Infinity * 1px); } /* ========================================================================== Time Display ========================================================================== */ .media-default-skin .media-time-controls { display: flex; flex: 1; gap: calc(var(--spacing) * 2.5); align-items: center; container: media-time-controls / inline-size; & .media-time:last-child { @container media-time-controls (width < 10rem) { display: none; } } } .media-default-skin .media-time { font-variant-numeric: tabular-nums; } .media-default-skin .media-time[role="button"] { cursor: pointer; outline: 2px solid transparent; outline-offset: -2px; border-radius: calc(var(--spacing) * 1); transition-timing-function: ease-out; transition-duration: 100ms; transition-property: outline-color, outline-offset; &:focus-visible { outline-color: currentColor; outline-offset: 2px; } } /* ========================================================================== Buttons ========================================================================== */ /* Base button */ .media-default-skin .media-button { display: flex; flex-shrink: 0; align-items: center; justify-content: center; height: calc(var(--spacing) * 9); min-height: 0; padding: calc(var(--spacing) * 2) calc(var(--spacing) * 4); text-align: center; touch-action: manipulation; cursor: pointer; user-select: none; outline: 2px solid transparent; outline-offset: -2px; border: none; border-radius: calc(Infinity * 1px); transition-timing-function: ease-out; transition-duration: 150ms; transition-property: background-color, outline-offset, scale; /* Fix weird jumping when clicking on the buttons in Safari. */ will-change: scale; &:focus-visible { outline-color: currentColor; outline-offset: 2px; } &:active:not([aria-disabled="true"]) { scale: 0.98; } &[aria-disabled="true"] { cursor: not-allowed; opacity: 0.5; filter: grayscale(1); } } /* Primary button variant */ .media-default-skin .media-button--primary { font-weight: 500; color: oklch(0 0 0); text-shadow: none; background: oklch(1 0 0); } /* Subtle button variant */ .media-default-skin .media-button--subtle { color: inherit; text-shadow: inherit; background: transparent; &:hover, &:focus-visible, &[aria-expanded="true"] { text-decoration: none; background-color: oklch(from currentColor l c h / 0.1); } } /* Icon button variant */ .media-default-skin .media-button--icon { display: grid; aspect-ratio: 1; padding: 0; &:active:not([aria-disabled="true"]) { scale: 0.9; } & .media-icon__container { display: grid; } & .media-icon { grid-area: 1 / 1; transition-behavior: allow-discrete; transition-property: display, opacity; transition-duration: 150ms; transition-timing-function: ease-out; filter: drop-shadow(0 1px 0 var(--media-current-shadow-color)); } } /* Seek button */ .media-default-skin .media-button--seek { & .media-icon__label { position: absolute; right: -1px; bottom: -3px; font-size: 0.715em; /* 10px */ font-weight: 500; font-variant-numeric: tabular-nums; letter-spacing: -0.05em; } &:has(.media-icon--flipped) .media-icon__label { right: unset; left: -1px; } } /* Playback rate button */ .media-default-skin .media-button--playback-rate { padding: 0; font-variant-numeric: tabular-nums; &::after { width: 4ch; content: attr(data-rate) "\00D7"; } &[data-inline-rate-label]::after { content: none; } } /* Settings button */ .media-default-skin .media-button--settings { & .media-icon--settings { transition: transform 150ms ease-in-out; @media (prefers-reduced-motion: reduce) { transition-duration: 0ms; } } &[aria-expanded="true"] .media-icon--settings { transform: rotate(90deg); } } /* Live button — wide pill button with a status dot (gray → red at the live edge) rendered via ::before, and "LIVE" text rendered as the button's own text content. */ .media-default-skin .media-button--live { display: inline-flex; gap: calc(var(--spacing) * 1.5); align-items: center; width: auto; aspect-ratio: auto; padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3); font-size: var(--font-size-small); font-weight: 600; line-height: 1; text-transform: uppercase; letter-spacing: 0.05em; &::before { display: inline-block; flex-shrink: 0; width: calc(var(--spacing) * 2); height: calc(var(--spacing) * 2); content: ""; background-color: oklch(from currentColor l c h / 0.4); border-radius: 50%; transition: background-color 150ms ease-out; } &[data-live-edge]::before { background-color: oklch(0.65 0.22 27); } } /* ========================================================================== Button Groups ========================================================================== */ .media-default-skin .media-button-group { display: flex; gap: 1px; align-items: center; } /* ========================================================================== Badge ========================================================================== */ .media-default-skin .media-badge { padding: calc(var(--spacing) * 0.5) calc(var(--spacing) * 1.5); font-size: var(--font-size-small); font-weight: 500; line-height: 1; color: oklch(from currentColor l c h / 0.85); white-space: nowrap; background-color: oklch(from currentColor l c h / 0.1); border-radius: calc(Infinity * 1px); } /* ========================================================================== Icons ========================================================================== */ .media-default-skin .media-icon__container { position: relative; } .media-default-skin .media-icon { flex-shrink: 0; width: var(--media-icon-size); height: var(--media-icon-size); } .media-default-skin .media-icon--flipped { scale: -1 1; } /* ========================================================================== Poster Image ========================================================================== */ .media-default-skin media-poster, .media-default-skin > img { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; transition: opacity 0.25s; } .media-default-skin media-poster:not([data-visible]), .media-default-skin > img:not([data-visible]), .media-default-skin > img[data-visible]:not([data-loaded]) { opacity: 0; } .media-default-skin media-poster ::slotted(img), .media-default-skin media-poster img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: var(--media-object-fit, contain); object-position: var(--media-object-position, center); border-radius: var(--media-video-border-radius); } .media-default-skin > img { object-fit: var(--media-object-fit, contain); object-position: var(--media-object-position, center); border-radius: inherit; } /* Blurred placeholder: HTML path targets media-poster::before, React path targets the skin container::before */ .media-default-skin media-poster::before, .media-default-skin::before { position: absolute; inset: 0; pointer-events: none; content: ""; background-image: var(--media-poster-placeholder, none); background-repeat: no-repeat; background-position: var(--media-object-position, center); background-size: var(--media-object-fit, contain); filter: blur(var(--media-poster-placeholder-blur, 20px)); } /* React path: hidden by default, transitions in while poster loads */ .media-default-skin::before { opacity: 0; transition: opacity 0.25s; } /* Show placeholder while the poster is visible but not yet loaded */ .media-default-skin:has(img[data-visible]:not([data-loaded]))::before { opacity: 1; } .media-default-skin:fullscreen media-poster ::slotted(img), .media-default-skin:fullscreen media-poster img, .media-default-skin:fullscreen > img { object-fit: contain; } /* ========================================================================== Media thumbnail ========================================================================== */ .media-default-skin .media-thumbnail { pointer-events: none; background-color: oklch(0 0 0 / 0.9); border-radius: calc(var(--spacing) * 3); & .media-thumbnail__image { position: relative; display: block; overflow: clip; border-radius: inherit; &::after { position: absolute; inset: 0; content: ""; background-image: linear-gradient(to top, oklch(0 0 0 / 0.5), oklch(0 0 0 / 0.1), oklch(0 0 0 / 0)); border-radius: inherit; } } & .media-thumbnail__time { position: absolute; inset-inline: 0; bottom: calc(var(--spacing) * 2); text-align: center; } & .media-thumbnail__spinner { position: absolute; top: 50%; left: 50%; opacity: 0; translate: -50% -50%; } & .media-thumbnail__image, & .media-thumbnail__spinner { transition: opacity 150ms ease-out; } &:not(:has(.media-thumbnail__image[data-loading])) { & .media-thumbnail__spinner { --media-spinner-animation: none; } } &:has(.media-thumbnail__image[data-loading]) { & .media-thumbnail__image { opacity: 0; } & .media-thumbnail__spinner { opacity: 1; } } } /* ========================================================================== Slider ========================================================================== */ .media-default-skin .media-slider { position: relative; display: flex; flex: 1; align-items: center; justify-content: center; cursor: pointer; outline: none; border-radius: calc(infinity * 1px); &[data-orientation="horizontal"] { width: var(--slider-width, 100%); min-width: calc(var(--spacing) * 20); height: var(--slider-height, calc(var(--spacing) * 8)); } &[data-orientation="vertical"] { width: var(--slider-width, calc(var(--spacing) * 8)); height: var(--slider-height, calc(var(--spacing) * 20)); } /* Track */ & .media-slider__track { position: relative; overflow: hidden; user-select: none; border-radius: inherit; isolation: isolate; &[data-orientation="horizontal"] { width: 100%; height: calc(var(--spacing) * 1); } &[data-orientation="vertical"] { width: calc(var(--spacing) * 1); height: 100%; } } /* Thumb */ & .media-slider__thumb { position: absolute; z-index: 10; width: calc(var(--spacing) * 2.5); height: calc(var(--spacing) * 2.5); user-select: none; outline: 4px solid transparent; outline-offset: -4px; background-color: currentColor; border-radius: calc(Infinity * 1px); box-shadow: 0 0 0 1px var(--media-current-shadow-color, oklch(0 0 0 / 0.1)), 0 1px 3px 0 oklch(0 0 0 / 0.35), 0 1px 2px -1px oklch(0 0 0 / 0.35); opacity: 0; translate: -50% -50%; transition-timing-function: ease-out; transition-duration: 150ms; transition-property: opacity, height, width, outline-offset; &[data-orientation="horizontal"] { top: 50%; left: var(--media-slider-fill); } &[data-orientation="vertical"] { top: calc(100% - var(--media-slider-fill)); left: 50%; } &:hover, &:focus { outline-color: oklch(from currentColor l c h / 0.15); outline-offset: 0; } &::after { position: absolute; inset: -4px; content: ""; border-radius: inherit; box-shadow: 0 0 0 2px currentColor; transition-timing-function: ease-out; transition-duration: 150ms; transition-property: opacity, scale; } &:not(:focus-visible)::after { opacity: 0; scale: 0.5; } } &:active .media-slider__thumb, &:focus-within .media-slider__thumb, & .media-slider__thumb--persistent { width: calc(var(--spacing) * 3); height: calc(var(--spacing) * 3); } &:hover .media-slider__thumb, & .media-slider__thumb:focus-visible, & .media-slider__thumb--persistent { opacity: 1; } /* Preview */ & .media-slider__preview { & .media-slider__value, &::before { opacity: 0; scale: 0.5; transition-timing-function: ease-out; transition-duration: 200ms; transition-property: opacity, scale; } & .media-slider__value { position: absolute; bottom: calc(var(--spacing) * 9); text-shadow: 0 1px 0 var(--media-current-shadow-color); filter: blur(8px); translate: -50% calc(var(--spacing) * 2); transition-property: filter, opacity, scale, translate; } &::before { display: block; min-width: calc(var(--spacing) * 1); height: calc(var(--spacing) * 1); content: ""; background-color: currentColor; border-radius: 100%; box-shadow: 0 0 0 1px var(--media-current-shadow-color, oklch(0 0 0 / 0.15)), 0 1px 2px 0 oklch(0 0 0 / 0.35); } &[data-pointing] .media-slider__value, &[data-pointing]:not([data-dragging])::before { opacity: 1; scale: 1; } &[data-pointing] .media-slider__value { filter: blur(0); translate: -50% 0; } } /* Shared track fills */ & .media-slider__buffer, & .media-slider__fill { position: absolute; pointer-events: none; border-radius: inherit; &[data-orientation="horizontal"], &[data-orientation="horizontal"] { inset-block: 0; left: 0; } &[data-orientation="vertical"], &[data-orientation="vertical"] { inset-inline: 0; bottom: 0; } } /* Buffer */ & .media-slider__buffer { background-color: oklch(from currentColor l c h / 0.2); transition-timing-function: ease-out; transition-duration: 0.25s; &[data-orientation="horizontal"] { width: var(--media-slider-buffer); transition-property: width; } &[data-orientation="vertical"] { height: var(--media-slider-buffer); transition-property: height; } } /* Fill */ & .media-slider__fill { background-color: currentColor; &[data-orientation="horizontal"] { width: var(--media-slider-fill); } &[data-orientation="vertical"] { height: var(--media-slider-fill); } } /* Dragging — thumb and fill follow the pointer position */ &[data-dragging] { & .media-slider__thumb[data-orientation="horizontal"] { left: var(--media-slider-pointer); } & .media-slider__thumb[data-orientation="vertical"] { top: calc(100% - var(--media-slider-pointer)); } & .media-slider__fill[data-orientation="horizontal"] { width: var(--media-slider-pointer); } & .media-slider__fill[data-orientation="vertical"] { height: var(--media-slider-pointer); } } } /* ========================================================================== Popups & Tooltips ========================================================================== */ .media-default-skin { --media-popup-transition: opacity var(--media-popup-transition-timing-function) var(--media-popup-transition-duration), filter var(--media-popup-transition-timing-function) var(--media-popup-transition-duration), transform var(--media-popup-transition-timing-function) var(--media-popup-transition-duration), scale var(--media-popup-transition-timing-function) var(--media-popup-transition-duration); } .media-default-skin .media-popover, .media-default-skin .media-tooltip { --popup-translate-distance: calc(0.5 * var(--base-size)); margin: 0; overflow: visible; color: inherit; border: 0; transition: var(--media-popup-transition); &[data-starting-style], &[data-ending-style] { opacity: 0; filter: blur(4px); /* We have to use transform here for translate as the translate property is used for positioning by core. */ transform: translate(var(--popup-translate-x-distance, 0), var(--popup-translate-y-distance, 0)); scale: 0.95; } &[data-ending-style] { transform: none; /* Speed up the exit transition. */ transition-duration: max(0ms, calc(var(--media-popup-transition-duration) - 50ms)); } &[data-side="top"] { --popup-translate-y-distance: var(--popup-translate-distance); transform-origin: bottom; } &[data-side="bottom"] { --popup-translate-y-distance: calc(var(--popup-translate-distance) * -1); transform-origin: top; } &[data-side="left"] { --popup-translate-x-distance: var(--popup-translate-distance); transform-origin: right; } &[data-side="right"] { --popup-translate-x-distance: calc(var(--popup-translate-distance) * -1); transform-origin: left; } /* Safe area between trigger and popup */ &::before { position: absolute; pointer-events: inherit; content: ""; } &[data-side="top"]::before, &[data-side="bottom"]::before { inset-inline: 0; width: 100%; } &[data-side="top"]::before { top: 100%; } &[data-side="bottom"]::before { bottom: 100%; } &[data-side="left"]::before, &[data-side="right"]::before { inset-block: 0; height: 100%; } &[data-side="left"]::before { left: 100%; } &[data-side="right"]::before { right: 100%; } } .media-default-skin .media-popover { &[data-side="top"]::before, &[data-side="bottom"]::before { height: var(--media-popover-side-offset); } &[data-side="left"]::before, &[data-side="right"]::before { width: var(--media-popover-side-offset); } } .media-default-skin .media-popover--volume { padding: calc(var(--spacing) * 3) 0; border-radius: calc(Infinity * 1px); &:has(media-volume-slider[data-availability="unsupported"]) { display: none; } } .media-default-skin .media-tooltip { padding: calc(var(--spacing) * 1) calc(var(--spacing) * 2.5); font-size: var(--font-size-base); white-space: nowrap; border-radius: calc(Infinity * 1px); /* `display: flex` must not apply while closed — it overrides UA `[popover]` hiding. */ &[data-open] { display: flex; column-gap: calc(var(--spacing) * 1); align-items: center; } &[data-side="top"]::before, &[data-side="bottom"]::before { height: var(--media-tooltip-side-offset); } &[data-side="left"]::before, &[data-side="right"]::before { width: var(--media-tooltip-side-offset); } & .media-tooltip__kbd { min-width: 1.5em; padding: 0.1em; font-family: inherit; font-size: var(--font-size-small); font-weight: 600; line-height: 1.25; text-align: center; background-color: oklch(from currentColor l c h / 0.3); border-radius: calc(var(--spacing) * 1); } } /* ========================================================================== Menus Note: Menus use `.media-popover` styles for positioning and transitions. ========================================================================== */ .media-default-skin .media-menu { --menu-transition-duration: 250ms; --menu-max-height: calc(var(--spacing) * 56); --menu-padding: calc(var(--spacing) * 1); --menu-border-radius: calc(var(--spacing) * 3); --menu-item-border-radius: calc(var(--menu-border-radius) - var(--menu-padding)); box-sizing: border-box; min-width: max-content; max-width: var(--media-popover-available-width, none); max-height: min(var(--media-popover-available-height, var(--menu-max-height)), var(--menu-max-height)); padding: var(--menu-padding); overflow: auto; overscroll-behavior: none; border-radius: var(--menu-border-radius); @media (prefers-reduced-motion: reduce) { --menu-transition-duration: 0ms; } & .media-menu__panel { position: absolute; inset: 0; padding: var(--menu-padding); overflow: auto; overscroll-behavior: none; outline: none; translate: 0 0; transition-timing-function: ease-in-out; transition-duration: var(--menu-transition-duration); transition-property: translate, filter; will-change: translate, filter; &[data-starting-style], &[data-ending-style] { overflow: hidden; } /* Root settings view — slides out when a submenu is active */ &[data-menu-root-view][data-menu-view-state="inactive"] { filter: blur(8px); translate: -100% 0; /* Hide the indicator while transitioning */ & .media-menu__group::before { display: none; } } /* Submenu panels — slide in/out alongside the root view */ &[data-submenu] { z-index: 10; &:not([data-open], [data-ending-style]) { translate: -100% 0; transition-property: none; } &[data-starting-style], &[data-ending-style] { pointer-events: none; filter: blur(8px); } &[data-starting-style][data-direction="forward"], &[data-ending-style][data-direction="back"] { translate: 100% 0; } &[data-ending-style][data-direction="forward"], &[data-starting-style][data-direction="back"] { translate: -100% 0; } } } & .media-menu__separator { margin-block: calc(var(--spacing) * 1); border-bottom: 1px solid oklch(0 0 0 / 0.1); box-shadow: 0 1px 0 0 oklch(1 0 0 / 0.075); } & .media-menu__group { anchor-scope: --media-menu-item-highlight-anchor; display: flex; flex-direction: column; gap: calc(var(--spacing) * 0.5); @supports (top: anchor(top)) { &::before { position: absolute; position-anchor: --media-menu-item-highlight-anchor; inset: anchor(inside); pointer-events: none; content: ""; background-color: oklch(from currentColor l c h / 0.1); border-radius: var(--menu-item-border-radius); transition: inset ease-in-out 100ms; } } } & .media-menu__item, & .media-menu__back { display: flex; gap: calc(var(--spacing) * 1.5); align-items: center; padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 2); text-align: left; text-shadow: 0 1px 0 var(--media-current-shadow-color); cursor: pointer; user-select: none; outline: 2px solid transparent; outline-offset: -2px; border-radius: var(--menu-item-border-radius); transition: background-color, color; transition-timing-function: ease-out; transition-duration: 100ms; &:focus-visible { outline-color: currentColor; outline-offset: 2px; } &:hover, &[data-highlighted] { background-color: oklch(from currentColor l c h / 0.1); } & .media-icon { flex-shrink: 0; color: oklch(from currentColor l c h / 0.65); filter: drop-shadow(0 1px 0 var(--media-current-shadow-color)); } } & .media-menu__indicator { flex-shrink: 0; margin-right: calc(var(--spacing) * -1); margin-left: auto; opacity: 0; & .media-icon { filter: drop-shadow(0 1px 0 var(--media-current-shadow-color)); } } & .media-menu__item { justify-content: space-between; font-variant-numeric: tabular-nums; color: inherit; &[aria-disabled="true"] { pointer-events: none; cursor: not-allowed; opacity: 0.5; } &[aria-checked="true"] .media-menu__indicator { opacity: 1; } &[data-availability="unavailable"], &[data-availability="unsupported"] { display: none; } &:hover, &[data-highlighted] { @supports (top: anchor(top)) { anchor-name: --media-menu-item-highlight-anchor; background-color: transparent; } } } & .media-menu__tier { padding-top: 1px; padding-left: calc(var(--spacing) * 0.5); font-size: var(--font-size-tiny); font-weight: 600; line-height: 1; color: oklch(from currentColor l c h / 0.7); } & .media-menu__back { width: 100%; margin-bottom: calc(var(--spacing) * 0.5); } & .media-menu__hint { display: inline-flex; gap: calc(var(--spacing) * 1); align-items: center; min-width: 0; padding-left: calc(var(--spacing) * 2); margin-left: auto; color: oklch(from currentColor l c h / 0.65); } & .media-menu__hint-label { max-width: calc(var(--spacing) * 24); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } & .media-menu__chevron { width: calc(var(--spacing) * 3.5); height: calc(var(--spacing) * 3.5); } /* Settings menu */ &.media-menu--settings { width: var(--media-menu-width); min-width: calc(var(--spacing) * 48); height: var(--media-menu-height); overflow: hidden; /* Add height and width transitions. */ transition: var(--media-popup-transition), height var(--media-popup-transition-timing-function) var(--menu-transition-duration), width var(--media-popup-transition-timing-function) var(--menu-transition-duration); /* Don't transition width and height on open/close. */ &[data-starting-style], &[data-ending-style] { transition: var(--media-popup-transition); } } } /* ========================================================================== Native Caption Track ========================================================================== */ .media-default-skin { --media-caption-track-duration: var(--media-controls-transition-duration); --media-caption-track-delay: 25ms; --media-caption-track-y: calc(var(--spacing) * -2); &:has(.media-controls[data-visible]) { --media-caption-track-y: calc(var(--spacing) * -22); } @container media-root (width > 42rem) { &:has(.media-controls[data-visible]) > * { --media-caption-track-y: calc(var(--spacing) * -14); } } } .media-default-skin video::-webkit-media-text-track-container { z-index: 1; font-family: inherit; scale: 0.98; translate: 0 var(--media-caption-track-y); transition: translate var(--media-caption-track-duration) ease-out; transition-delay: var(--media-caption-track-delay); } /* ========================================================================== Input Feedback ========================================================================== */ .media-default-skin .media-input-feedback { position: absolute; inset: 0; display: grid; grid-template-columns: 1fr 1fr 1fr; align-items: center; justify-items: center; color: var(--media-color-primary, oklch(1 0 0)); pointer-events: none; } /* --- Feedback islands ------------------------------------------------------- */ .media-default-skin .media-input-feedback-island { --media-surface-background-color: oklch(0 0 0 / 0.25); position: absolute; top: calc(var(--spacing) * 3); font-weight: 500; color: inherit; pointer-events: none; border-radius: calc(Infinity * 1px); transform-origin: top center; transition-timing-function: ease-out; transition-duration: 100ms; .media-input-feedback-island__content { display: flex; gap: calc(var(--spacing) * 2); align-items: center; justify-content: space-between; width: 100%; padding: calc(var(--spacing) * 1) calc(var(--spacing) * 2.5); /* Increase contrast of the content */ * { mix-blend-mode: difference; } } .media-icon { display: none; flex-shrink: 0; } .media-input-feedback-island__value { margin-left: auto; } @media (pointer: coarse) { transition-property: scale, translate, opacity; will-change: scale, translate, opacity; } @media (pointer: fine) and (prefers-reduced-motion: no-preference) { transition-property: scale, translate, filter, opacity; will-change: scale, translate, filter, opacity; } @media (prefers-reduced-transparency: reduce) or (prefers-contrast: more) { --media-surface-background-color: oklch(0 0 0); } /* Default hidden state */ &[data-starting-style], &[data-ending-style] { opacity: 0; transition-timing-function: ease-in; transition-duration: 250ms; @media (pointer: fine) and (prefers-reduced-motion: no-preference) { filter: blur(8px); scale: 0.9; } @media (prefers-reduced-motion: no-preference) { &[data-ending-style] { translate: 0 -25%; } } } } .media-default-skin .media-input-feedback-island--volume { width: min(80%, calc(var(--spacing) * 48)); .media-input-feedback-island__content { --media-progress-fill: var(--media-volume-fill); background-image: linear-gradient( to right, currentColor 0%, currentColor var(--media-progress-fill), transparent var(--media-progress-fill), transparent 100% ); border-radius: inherit; transition: --media-progress-fill 200ms linear; } &[data-level="high"] .media-icon--volume-high, &[data-level="low"] .media-icon--volume-low, &[data-level="off"] .media-icon--volume-off { display: block; } @media (prefers-reduced-motion: no-preference) { &[data-min], &[data-max] { animation: media-shake 300ms ease-in-out; } } } .media-default-skin .media-input-feedback-island--status { &[data-status="captions-on"] .media-icon--captions-on, &[data-status="captions-off"] .media-icon--captions-off, &[data-status="fullscreen"] .media-icon--fullscreen-enter, &[data-status="exit-fullscreen"] .media-icon--fullscreen-exit, &[data-status="pip"] .media-icon--pip-enter, &[data-status="exit-pip"] .media-icon--pip-exit { display: block; } } /* --- Bubble ---------------------------------------------------------------- */ .media-default-skin .media-input-feedback-bubble { display: grid; grid-row: 1; grid-column: 2; place-content: center; padding: calc(var(--spacing) * 4); text-align: center; /* Central bubble (play, pause) */ &:not([data-direction]) { background: oklch(0 0 0 / 0.35); border-radius: 100%; backdrop-filter: blur(8px); transition-timing-function: ease-out; transition-duration: 200ms; transition-property: opacity, scale; &[data-starting-style], &[data-ending-style] { opacity: 0; scale: 0.85; } &[data-ending-style] { transition-timing-function: ease-in; transition-duration: 100ms; } @media (prefers-reduced-motion: reduce) { transition-duration: 50ms; transition-property: opacity; } } /* Directional bubbles (seek) */ &[data-direction] { gap: calc(var(--spacing) * 1); @container media-root (width > 24rem) { padding: calc(var(--spacing) * 6); } } &[data-direction="backward"] { grid-column: 1; justify-self: left; } &[data-direction="forward"] { grid-column: 3; justify-self: right; } /* Icons */ .media-icon { display: none; width: calc(var(--media-icon-size) * 1.5); height: calc(var(--media-icon-size) * 1.5); &.media-icon--play { translate: 1px 0; } } &[data-direction] .media-icon--seek, &[data-status="pause"] .media-icon--pause, &[data-status="play"] .media-icon--play { display: block; } &[data-direction="backward"] .media-icon--seek { scale: -1 1; } @media (prefers-reduced-motion: no-preference) { &[data-direction] .media-icon--seek { transition-timing-function: ease-in-out; transition-duration: 200ms; transition-property: translate, opacity; } &[data-starting-style] .media-icon--seek, &[data-ending-style] .media-icon--seek { opacity: 0; } &[data-direction="forward"][data-starting-style] .media-icon--seek { translate: -60% 0; } &[data-direction="backward"][data-starting-style] .media-icon--seek { translate: 60% 0; } } } /* ========================================================================== Icon State Visibility for Video Skins Data-attribute-driven visibility rules for multi-state icon buttons. Uses :is() with both element selectors (for HTML custom element wrappers) and class selectors (for React rendered SVG elements). ========================================================================== */ /* --- All icons hidden by default --- */ .media-button--play .media-icon--restart, .media-button--play .media-icon--play, .media-button--play .media-icon--pause, .media-button--mute .media-icon--volume-off, .media-button--mute .media-icon--volume-low, .media-button--mute .media-icon--volume-high, .media-button--fullscreen .media-icon--fullscreen-enter, .media-button--fullscreen .media-icon--fullscreen-exit, .media-button--pip .media-icon--pip-enter, .media-button--pip .media-icon--pip-exit, .media-button--cast .media-icon--cast-enter, .media-button--cast .media-icon--cast-exit, .media-button--airplay .media-icon--airplay-enter, .media-button--airplay .media-icon--airplay-exit, .media-button--captions .media-icon--captions-off, .media-button--captions .media-icon--captions-on { display: none; opacity: 0; } /* --- Active icon per state --- */ /* Play: ended → restart */ .media-button--play[data-ended] .media-icon--restart, /* Play: paused or not yet started (not ended) → play */ .media-button--play:not([data-ended])[data-paused] .media-icon--play, .media-button--play:not([data-ended]):not([data-started]) .media-icon--play, /* Play: started and not paused/ended → pause */ .media-button--play[data-started]:not([data-paused]):not([data-ended]) .media-icon--pause, /* Mute: muted → volume off */ .media-button--mute[data-muted] .media-icon--volume-off, /* Mute: volume low (not muted) → volume low */ .media-button--mute:not([data-muted])[data-volume-level="low"] .media-icon--volume-low, /* Mute: volume high (not muted, not low) → volume high */ .media-button--mute:not([data-muted]):not([data-volume-level="low"]) .media-icon--volume-high, /* Fullscreen: not fullscreen → enter */ .media-button--fullscreen:not([data-fullscreen]) .media-icon--fullscreen-enter, /* Fullscreen: fullscreen → exit */ .media-button--fullscreen[data-fullscreen] .media-icon--fullscreen-exit, /* Picture-in-Picture: not active → enter */ .media-button--pip:not([data-pip]) .media-icon--pip-enter, /* Picture-in-Picture: active → exit */ .media-button--pip[data-pip] .media-icon--pip-exit, /* Cast: not connected → enter */ .media-button--cast:not([data-cast-state="connected"]) .media-icon--cast-enter, /* Cast: connected → exit */ .media-button--cast[data-cast-state="connected"] .media-icon--cast-exit, /* AirPlay: not connected → enter */ .media-button--airplay:not([data-airplay-state="connected"]) .media-icon--airplay-enter, /* AirPlay: connected → exit */ .media-button--airplay[data-airplay-state="connected"] .media-icon--airplay-exit, /* Captions: not active → captions off */ .media-button--captions:not([data-active]) .media-icon--captions-off, /* Captions: active → captions on */ .media-button--captions[data-active] .media-icon--captions-on { display: block; opacity: 1; } /* --- Pause keyframe animations on inactive icons --- */ /* The airplay-exit SVG defines its keyframes against CSS variables (mirroring the spinner pattern). When the AirPlay session isn't active the SVG is still in the DOM — just `display: none` — so its animations would keep running. Set the variables to `none` to short-circuit the keyframes. */ .media-button--airplay:not([data-airplay-state="connected"]) { --media-icon--airplay__fill-animation: none; --media-icon--airplay__triangle-animation: none; } /* -------------------------------------------------------------------------- */ /* Global @keyframes for all video skins (CSS & Tailwind) */ /* -------------------------------------------------------------------------- */ @keyframes media-shake { 0%, 100% { translate: 0 0; } 20% { translate: -6px 0; } 40% { translate: 4px 0; } 60% { translate: -2px 0; } 80% { translate: 1px 0; } } /* -------------------------------------------------------------------------- */ /* Global @properties for all video skins (CSS & Tailwind) */ /* -------------------------------------------------------------------------- */ @property --media-progress-fill { syntax: ""; inherits: true; initial-value: 0%; } /* ========================================================================== Root ========================================================================== */ .media-default-skin--video { --media-spring-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 ); --media-border-color: oklch(0 0 0 / 0.1); --media-surface-background-color: oklch(1 0 0 / 0.1); --media-surface-inner-border-color: oklch(1 0 0 / 0.1); --media-surface-outer-border-color: oklch(0 0 0 / 0.1); --media-surface-shadow-color: oklch(0 0 0 / 0.15); --media-surface-backdrop-filter: blur(16px) saturate(1.5); /* We have to create an alias so that the shadow DOM can inherit the custom property value */ --media-video-border-radius: var(--media-border-radius, 1.75rem); --media-controls-transition-duration: 100ms; --media-controls-transition-timing-function: ease-out; --media-error-dialog-transition-duration: 350ms; --media-error-dialog-transition-delay: 100ms; --media-error-dialog-transition-timing-function: var(--media-spring-timing-function); --media-popup-transition-duration: 100ms; --media-popup-transition-timing-function: ease-out; overflow: clip; background: oklch(0 0 0); @media (prefers-reduced-motion: reduce) { --media-error-dialog-transition-duration: 50ms; --media-error-dialog-transition-delay: 0ms; --media-error-dialog-transition-timing-function: ease-out; --media-popup-transition-duration: 0ms; } @media (prefers-color-scheme: dark) { --media-border-color: oklch(1 0 0 / 0.15); } @media (prefers-reduced-transparency: reduce) or (prefers-contrast: more) { --media-surface-background-color: oklch(0 0 0); --media-surface-inner-border-color: oklch(1 0 0 / 0.25); --media-surface-outer-border-color: transparent; } &:has(.media-controls--root:not([data-visible])) { /* Slight delay to hide controls on non-touch devices after interaction */ @media (pointer: fine) { --media-controls-transition-duration: 300ms; } @media (pointer: coarse) { --media-controls-transition-duration: 150ms; } @media (prefers-reduced-motion: reduce) { --media-controls-transition-duration: 50ms; } } /* Inner border ring */ &::after { position: absolute; inset: 0; z-index: 10; pointer-events: none; content: ""; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--media-border-color); } &:fullscreen { --media-border-radius: 0; &::after { display: none; } @media (width >= 1280px) { --scale: 1.25; } @media (width >= 1536px) { --scale: 1.5; } @media (width >= 1920px) { --scale: 1.75; } } } /* ========================================================================== 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: calc(var(--spacing) * 3); width: 100%; max-width: calc(var(--spacing) * 72); padding: calc(var(--spacing) * 3); color: oklch(1 0 0); text-shadow: 0 1px 0 oklch(0 0 0 / 0.25); border-radius: calc(var(--spacing) * 7); transition-delay: var(--media-error-dialog-transition-delay); transition-timing-function: var(--media-error-dialog-transition-timing-function); transition-duration: var(--media-error-dialog-transition-duration); transition-property: opacity, scale; } .media-default-skin--video .media-error[data-starting-style] .media-error__dialog, .media-default-skin--video .media-error[data-ending-style] .media-error__dialog { opacity: 0; scale: 0.5; } .media-default-skin--video .media-error[data-ending-style] .media-error__dialog { transition-delay: 0ms; } .media-default-skin--video .media-error__content { display: flex; flex-direction: column; gap: calc(var(--spacing) * 2); padding: calc(var(--spacing) * 2) calc(var(--spacing) * 2) calc(var(--spacing) * 1.5); text-shadow: inherit; } .media-default-skin--video .media-error__title { font-size: var(--font-size-medium); } /* ========================================================================== Controls (hide/show behavior) ========================================================================== */ .media-default-skin--video .media-controls--root { --inset-factor: 2; --inset: calc(var(--spacing) * var(--inset-factor)); --base-boundary-offset: var(--inset-factor); z-index: 10; display: contents; color: var(--media-color-primary, oklch(1 0 0)); transition-timing-function: var(--media-controls-transition-timing-function); /* Speed up the entry transition */ transition-duration: calc(var(--media-controls-transition-duration) / 2); & .media-controls--primary, & .media-controls--secondary { position: absolute; z-index: 10; } & .media-controls--primary { inset-inline: var(--inset); bottom: var(--inset); transform-origin: bottom; } & .media-controls--secondary { top: var(--inset); right: var(--inset); container-type: normal; transform-origin: top; } @container media-root (width < 32rem) { & .media-controls--primary, & .media-controls--secondary { transition-timing-function: inherit; transition-duration: inherit; @media (pointer: fine) { transition-property: filter, opacity, scale, translate; will-change: filter, opacity, scale, translate; } @media (pointer: coarse) { transition-property: opacity, scale, translate; will-change: opacity, scale, translate; } } &:after { display: none; } &:not([data-visible]) { & .media-controls--primary, & .media-controls--secondary { pointer-events: none; opacity: 0; scale: 0.95; transition-duration: var(--media-controls-transition-duration); @media (pointer: fine) and (prefers-reduced-motion: no-preference) { filter: blur(8px); } @media (prefers-reduced-motion: reduce) { scale: 1; } } & .media-controls--primary { @media (prefers-reduced-motion: no-preference) { translate: 0 4px; } } & .media-controls--secondary { @media (prefers-reduced-motion: no-preference) { translate: 0 -4px; } } } } @container media-root (width >= 32rem) { position: absolute; inset-inline: var(--inset); bottom: var(--inset); display: flex; transform-origin: bottom; & .media-controls--primary, & .media-controls--secondary { display: contents; &::after { display: none; } } &:not([data-visible]) { pointer-events: none; opacity: 0; scale: 0.95; transition-duration: var(--media-controls-transition-duration); @media (pointer: fine) and (prefers-reduced-motion: no-preference) { filter: blur(8px); } @media (prefers-reduced-motion: reduce) { scale: 1; } @media (prefers-reduced-motion: no-preference) { translate: 0 4px; } } } & .media-time-controls { flex: 1; padding-inline: calc(var(--spacing) * 3); } @media (pointer: fine) { transition-property: filter, opacity, scale, translate; will-change: filter, opacity, scale, translate; } @media (pointer: coarse) { transition-property: opacity, scale, translate; will-change: opacity, scale, translate; } @container media-root (width > 42rem) { --inset-factor: 3; } } .media-default-skin--video .media-error[data-open] ~ .media-controls { display: none; } /* Hide cursor when controls are hidden */ .media-default-skin--video:has(.media-controls--root:not([data-visible])) { cursor: none; } /* ========================================================================== Sliders ========================================================================== */ .media-default-skin--video .media-slider__track { background-color: oklch(1 0 0 / 0.2); box-shadow: 0 0 0 1px oklch(0 0 0 / 0.05); } .media-default-skin--video .media-slider__thumbnail { --max-width: calc(var(--spacing) * 44); --max-height: calc(var(--spacing) * 32); --padding: calc(var(--spacing) * -4.5); /** Inset is the difference between the container width and the slider (100%) width. Divided by 2 as we render the time on both sides. */ --inset: calc((100cqi - 100%) / 2); position: absolute; bottom: calc(100% + calc(var(--spacing) * 4.8)); left: clamp( calc(var(--max-width) / 2 + var(--padding) - var(--inset)), var(--media-slider-pointer), calc(100% - var(--max-width) / 2 - var(--padding) + var(--inset)) ); pointer-events: none; opacity: 0; filter: blur(8px); transform-origin: bottom; scale: 0.8; translate: -50%; transition-timing-function: ease-out; transition-duration: 150ms; transition-property: scale, opacity, filter; & .media-thumbnail__image { max-width: var(--max-width); max-height: var(--max-height); } } .media-default-skin--video .media-slider[data-pointing] .media-slider__thumbnail:has([role="img"]:not([data-hidden])) { opacity: 1; filter: blur(0); scale: 1; } .media-default-skin--video .media-slider__thumbnail:has([role="img"]:not([data-hidden])) + .media-slider__preview .media-slider__value { display: none; }