@motion-proto/live-tokens 0.85.0 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +118 -0
- package/src/editor/component-editor/VideoFrameEditor.svelte +73 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +108 -0
- package/src/editor/component-editor/registry.ts +40 -1
- package/src/editor/core/sketch/sketchLayer.ts +7 -0
- package/src/editor/overlay/LiveEditorOverlay.svelte +45 -52
- package/src/editor/pages/ComponentEditorPage.svelte +1 -1
- package/src/editor/ui/ThemePanel.svelte +9 -53
- package/src/live-tokens/data/themes/autumn.json +97 -0
- package/src/live-tokens/data/themes/halloween.json +97 -0
- package/src/live-tokens/data/themes/midnight-study.json +97 -0
- package/src/live-tokens/data/themes/ocean.json +97 -0
- package/src/live-tokens/data/themes/royal-velvet.json +97 -0
- package/src/live-tokens/data/themes/sketchy.json +97 -0
- package/src/live-tokens/data/themes/spring-meadow.json +97 -0
- package/src/live-tokens/data/themes/sunset.json +97 -0
- package/src/system/assets/slide-04.webp +0 -0
- package/src/system/assets/slide-05.webp +0 -0
- package/src/system/assets/slide-06.webp +0 -0
- package/src/system/assets/watch-navigation-poster.webp +0 -0
- package/src/system/assets/watch-navigation.mp4 +0 -0
- package/src/system/components/ImageLightbox.svelte +20 -21
- package/src/system/components/SlidePager.svelte +516 -0
- package/src/system/components/VideoFrame.svelte +284 -0
- package/src/system/components/VideoLightbox.svelte +454 -0
- package/src/testing-js/{chunk-RDHCBQ6I.js → chunk-FTLASW2R.js} +366 -2
- package/src/testing-js/chunk-FTLASW2R.js.map +1 -0
- package/src/testing-js/{chunk-XV5CYADO.js → chunk-NXRFTTVL.js} +10 -6
- package/src/testing-js/chunk-NXRFTTVL.js.map +1 -0
- package/src/testing-js/component-behavior.contract.js +1 -1
- package/src/testing-js/component-editor.contract.js +1 -1
- package/src/testing-js/component-render.contract.js +1 -1
- package/src/testing-js/index.d.ts +2 -2
- package/src/testing-js/index.js +2 -2
- package/src/testing-js/page-compliance.contract.js +1 -1
- package/src/testing-js/{vitest-BMLIbDs2.d.ts → vitest-5vWtqwWe.d.ts} +2 -2
- package/src/testing-js/vitest.d.ts +1 -1
- package/src/testing-js/vitest.js +2 -2
- package/src/testing-js/chunk-RDHCBQ6I.js.map +0 -1
- package/src/testing-js/chunk-XV5CYADO.js.map +0 -1
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { CatalogueEntry } from '../../editor/component-editor/scaffolding/types';
|
|
3
|
+
|
|
4
|
+
export const catalogue = {
|
|
5
|
+
description:
|
|
6
|
+
'A small still that fades the page away and plays its clip in the middle of the screen when clicked.',
|
|
7
|
+
whenToUse:
|
|
8
|
+
'a short clip on a plain ground, such as a product shot, that the reader opens from a thumbnail beside the copy.',
|
|
9
|
+
whenNotToUse: [
|
|
10
|
+
{ when: 'a picture whose detail is the point.', use: 'imagelightbox' },
|
|
11
|
+
{ when: 'footage that plays in the layout at full size, with no thumbnail to open.', use: 'videoframe' }
|
|
12
|
+
],
|
|
13
|
+
props: {
|
|
14
|
+
chrome:
|
|
15
|
+
'`badge` lays a play badge over the thumbnail, so a still reads as a clip. `none` shows the still alone.',
|
|
16
|
+
inline: 'true renders the open lightbox in flow, without the thumbnail. The editor preview uses it.',
|
|
17
|
+
loop: 'true repeats the clip.',
|
|
18
|
+
hoverPlay:
|
|
19
|
+
'true plays the thumbnail muted under the pointer, and rewinds it on the way out. A reader who asked for less motion keeps the still.',
|
|
20
|
+
stretch:
|
|
21
|
+
'true spreads the trigger over the nearest positioned ancestor, so a whole card opens the clip. The consumer positions that ancestor.',
|
|
22
|
+
thumbAspect:
|
|
23
|
+
'the ratio the thumbnail box holds, when it should not be the clip\'s own. The clip still opens at its full frame.',
|
|
24
|
+
thumbFit:
|
|
25
|
+
'`cover` fills a thumbnail box the clip does not share the shape of, and crops what will not fit. `contain` keeps the whole frame and letterboxes it.',
|
|
26
|
+
previewStart:
|
|
27
|
+
'seconds into the clip the hover preview starts, for footage that opens on a title card. The clip itself still opens at the beginning.'
|
|
28
|
+
}
|
|
29
|
+
} satisfies CatalogueEntry;
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { tick } from 'svelte';
|
|
34
|
+
import { portal } from '../internal/portal';
|
|
35
|
+
|
|
36
|
+
interface Props {
|
|
37
|
+
src: string;
|
|
38
|
+
/** The thumbnail, and the frame shown before the clip starts. */
|
|
39
|
+
poster: string;
|
|
40
|
+
alt?: string;
|
|
41
|
+
/** Natural size of the clip. Sets the thumbnail's box and the open size. */
|
|
42
|
+
width?: number;
|
|
43
|
+
height?: number;
|
|
44
|
+
loop?: boolean;
|
|
45
|
+
/** "badge" lays a play badge over the thumbnail. */
|
|
46
|
+
chrome?: 'badge' | 'none';
|
|
47
|
+
/** Renders the open lightbox in flow, without the thumbnail. Used by the editor preview. */
|
|
48
|
+
inline?: boolean;
|
|
49
|
+
/** Plays the thumbnail muted under the pointer. Still for reduced motion. */
|
|
50
|
+
hoverPlay?: boolean;
|
|
51
|
+
/** Spreads the hit area over the nearest positioned ancestor. */
|
|
52
|
+
stretch?: boolean;
|
|
53
|
+
/** The thumbnail box's ratio, when it differs from the clip's own. */
|
|
54
|
+
thumbAspect?: number;
|
|
55
|
+
/** How the still meets a box it does not share the shape of. */
|
|
56
|
+
thumbFit?: 'contain' | 'cover';
|
|
57
|
+
/** Seconds in that the hover preview starts. The open clip still plays from 0. */
|
|
58
|
+
previewStart?: number;
|
|
59
|
+
/** Editor preview hook: `force-hover` paints the hover of the close button on an `inline` lightbox, and of the badge on a tile. */
|
|
60
|
+
class?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let {
|
|
64
|
+
src,
|
|
65
|
+
poster,
|
|
66
|
+
alt = '',
|
|
67
|
+
width = undefined,
|
|
68
|
+
height = undefined,
|
|
69
|
+
loop = false,
|
|
70
|
+
chrome = 'none',
|
|
71
|
+
inline = false,
|
|
72
|
+
hoverPlay = false,
|
|
73
|
+
stretch = false,
|
|
74
|
+
thumbAspect = undefined,
|
|
75
|
+
thumbFit = 'contain',
|
|
76
|
+
previewStart = 0,
|
|
77
|
+
class: className = ''
|
|
78
|
+
}: Props = $props();
|
|
79
|
+
|
|
80
|
+
let thumbEl: HTMLVideoElement | undefined = $state();
|
|
81
|
+
|
|
82
|
+
function previewIn() {
|
|
83
|
+
if (!thumbEl || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
|
84
|
+
if (thumbEl.currentTime < previewStart) thumbEl.currentTime = previewStart;
|
|
85
|
+
thumbEl.play().catch(() => {});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function previewOut() {
|
|
89
|
+
if (!thumbEl) return;
|
|
90
|
+
thumbEl.pause();
|
|
91
|
+
thumbEl.currentTime = previewStart;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let triggerEl: HTMLButtonElement | undefined = $state();
|
|
95
|
+
let modalEl: HTMLDivElement | undefined = $state();
|
|
96
|
+
let videoEl: HTMLVideoElement | undefined = $state();
|
|
97
|
+
let closeBtnEl: HTMLButtonElement | undefined = $state();
|
|
98
|
+
// `mounted` keeps the lightbox in the DOM through the fade out, `shown`
|
|
99
|
+
// drives the fade, and `open` is what the trigger reports.
|
|
100
|
+
let mounted = $state(false);
|
|
101
|
+
let shown = $state(false);
|
|
102
|
+
let open = $state(false);
|
|
103
|
+
|
|
104
|
+
const ratio = $derived(width && height ? width / height : undefined);
|
|
105
|
+
// The box the still sits in; the clip itself always opens at `ratio`.
|
|
106
|
+
const tileRatio = $derived(thumbAspect ?? ratio);
|
|
107
|
+
|
|
108
|
+
async function openLightbox() {
|
|
109
|
+
if (open) return;
|
|
110
|
+
open = true;
|
|
111
|
+
mounted = true;
|
|
112
|
+
await tick();
|
|
113
|
+
if (!modalEl) return;
|
|
114
|
+
// Commit the transparent start so the class change below fades in.
|
|
115
|
+
modalEl.getBoundingClientRect();
|
|
116
|
+
shown = true;
|
|
117
|
+
modalEl.focus();
|
|
118
|
+
videoEl?.play();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function closeLightbox() {
|
|
122
|
+
if (!open || !modalEl) return;
|
|
123
|
+
open = false;
|
|
124
|
+
shown = false;
|
|
125
|
+
videoEl?.pause();
|
|
126
|
+
await tick();
|
|
127
|
+
await Promise.all(modalEl.getAnimations().map((a) => a.finished)).catch(() => {});
|
|
128
|
+
if (open) return;
|
|
129
|
+
mounted = false;
|
|
130
|
+
triggerEl?.focus();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// A page may act on keys it hears at the window, such as a deck turning
|
|
134
|
+
// slides, so the lightbox keeps every key to itself while it is open.
|
|
135
|
+
function onModalKey(e: KeyboardEvent) {
|
|
136
|
+
e.stopPropagation();
|
|
137
|
+
if (e.key === 'Escape') {
|
|
138
|
+
closeLightbox();
|
|
139
|
+
} else if (e.key === ' ' && e.target === modalEl) {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
if (videoEl?.paused) videoEl.play();
|
|
142
|
+
else videoEl?.pause();
|
|
143
|
+
} else if (e.key === 'Tab') {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
(document.activeElement === closeBtnEl ? modalEl : closeBtnEl)?.focus();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
</script>
|
|
149
|
+
|
|
150
|
+
{#if inline}
|
|
151
|
+
<div class="videolightbox-modal inline shown {className}" style:--videolightbox-ratio={ratio}>
|
|
152
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
153
|
+
<video class="videolightbox-video" {src} {poster} preload="metadata" playsinline></video>
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
class="videolightbox-close"
|
|
157
|
+
aria-label="Close" tabindex="-1"
|
|
158
|
+
>
|
|
159
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
160
|
+
<path d="M18 6L6 18" />
|
|
161
|
+
<path d="M6 6l12 12" />
|
|
162
|
+
</svg>
|
|
163
|
+
</button>
|
|
164
|
+
</div>
|
|
165
|
+
{:else}
|
|
166
|
+
<div
|
|
167
|
+
class="videolightbox {className}"
|
|
168
|
+
style:aspect-ratio={tileRatio}
|
|
169
|
+
style:--videolightbox-thumb-fit={thumbFit}
|
|
170
|
+
>
|
|
171
|
+
<button
|
|
172
|
+
bind:this={triggerEl}
|
|
173
|
+
type="button"
|
|
174
|
+
class="videolightbox-trigger"
|
|
175
|
+
class:stretch
|
|
176
|
+
aria-label={alt ? `Play video: ${alt}` : 'Play video'}
|
|
177
|
+
aria-haspopup="dialog"
|
|
178
|
+
aria-expanded={open}
|
|
179
|
+
onclick={openLightbox}
|
|
180
|
+
onpointerenter={hoverPlay ? previewIn : undefined}
|
|
181
|
+
onpointerleave={hoverPlay ? previewOut : undefined}
|
|
182
|
+
onfocus={hoverPlay ? previewIn : undefined}
|
|
183
|
+
onblur={hoverPlay ? previewOut : undefined}
|
|
184
|
+
>
|
|
185
|
+
{#if hoverPlay}
|
|
186
|
+
<!-- Muted and silent by design; the sound belongs to the open clip. -->
|
|
187
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
188
|
+
<video
|
|
189
|
+
bind:this={thumbEl}
|
|
190
|
+
class="videolightbox-thumb"
|
|
191
|
+
{src}
|
|
192
|
+
{poster}
|
|
193
|
+
{width}
|
|
194
|
+
{height}
|
|
195
|
+
muted
|
|
196
|
+
loop
|
|
197
|
+
playsinline
|
|
198
|
+
preload="metadata"
|
|
199
|
+
></video>
|
|
200
|
+
{:else}
|
|
201
|
+
<img src={poster} alt="" {width} {height} draggable="false" />
|
|
202
|
+
{/if}
|
|
203
|
+
{#if chrome === 'badge'}
|
|
204
|
+
<span class="videolightbox-badge sketch-chip" aria-hidden="true">
|
|
205
|
+
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5.5v13l11-6.5z" /></svg>
|
|
206
|
+
</span>
|
|
207
|
+
{/if}
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
{#if mounted}
|
|
212
|
+
<!-- Clicking anywhere closes it, the clip included; the close button and
|
|
213
|
+
Escape do the same. -->
|
|
214
|
+
<div
|
|
215
|
+
bind:this={modalEl}
|
|
216
|
+
class="videolightbox-modal"
|
|
217
|
+
class:shown
|
|
218
|
+
role="dialog"
|
|
219
|
+
aria-modal="true"
|
|
220
|
+
aria-label={alt}
|
|
221
|
+
tabindex="-1"
|
|
222
|
+
style:--videolightbox-ratio={ratio}
|
|
223
|
+
style:--videolightbox-natural-width={width ? `${width}px` : undefined}
|
|
224
|
+
use:portal
|
|
225
|
+
onclick={closeLightbox}
|
|
226
|
+
onkeydown={onModalKey}
|
|
227
|
+
>
|
|
228
|
+
<!-- No caption track ships with the source footage. -->
|
|
229
|
+
<!-- svelte-ignore a11y_media_has_caption -->
|
|
230
|
+
<video bind:this={videoEl} class="videolightbox-video" {src} {poster} {loop} playsinline></video>
|
|
231
|
+
<button
|
|
232
|
+
bind:this={closeBtnEl}
|
|
233
|
+
type="button"
|
|
234
|
+
class="videolightbox-close"
|
|
235
|
+
aria-label="Close"
|
|
236
|
+
onclick={closeLightbox}
|
|
237
|
+
>
|
|
238
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
239
|
+
<path d="M18 6L6 18" />
|
|
240
|
+
<path d="M6 6l12 12" />
|
|
241
|
+
</svg>
|
|
242
|
+
</button>
|
|
243
|
+
</div>
|
|
244
|
+
{/if}
|
|
245
|
+
{/if}
|
|
246
|
+
|
|
247
|
+
<style>
|
|
248
|
+
:global(:root) {
|
|
249
|
+
--videolightbox-tile-surface: var(--color-transparent);
|
|
250
|
+
--videolightbox-tile-border: var(--color-transparent);
|
|
251
|
+
--videolightbox-tile-border-width: var(--border-width-0);
|
|
252
|
+
--videolightbox-tile-radius: var(--radius-2xl);
|
|
253
|
+
--videolightbox-tile-shadow: var(--shadow-md);
|
|
254
|
+
|
|
255
|
+
--videolightbox-scrim-surface: var(--scrim-high);
|
|
256
|
+
--videolightbox-overlay-padding: var(--space-64);
|
|
257
|
+
--videolightbox-overlay-duration: var(--duration-500);
|
|
258
|
+
--videolightbox-overlay-easing: var(--ease-out-quad);
|
|
259
|
+
|
|
260
|
+
--videolightbox-chrome-surface: var(--surface-neutral-low);
|
|
261
|
+
--videolightbox-chrome-border: var(--border-brand);
|
|
262
|
+
--videolightbox-chrome-border-width: var(--border-width-1);
|
|
263
|
+
--videolightbox-chrome-radius: var(--radius-full);
|
|
264
|
+
--videolightbox-chrome-icon: var(--text-primary);
|
|
265
|
+
--videolightbox-chrome-hover-surface: var(--surface-brand-high);
|
|
266
|
+
|
|
267
|
+
--videolightbox-badge-default-surface: var(--surface-brand-lower);
|
|
268
|
+
--videolightbox-badge-default-border: var(--border-brand-medium);
|
|
269
|
+
--videolightbox-badge-default-border-width: var(--border-width-2);
|
|
270
|
+
--videolightbox-badge-default-icon: var(--text-primary);
|
|
271
|
+
--videolightbox-badge-default-size: var(--space-64);
|
|
272
|
+
--videolightbox-badge-default-icon-size: var(--icon-size-4xl);
|
|
273
|
+
--videolightbox-badge-duration: var(--duration-200);
|
|
274
|
+
--videolightbox-badge-easing: var(--ease-out-quad);
|
|
275
|
+
|
|
276
|
+
--videolightbox-badge-hover-surface: var(--surface-brand);
|
|
277
|
+
--videolightbox-badge-hover-border: var(--border-brand-strong);
|
|
278
|
+
--videolightbox-badge-hover-icon: var(--text-primary);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.videolightbox {
|
|
282
|
+
width: 100%;
|
|
283
|
+
background: var(--videolightbox-tile-surface);
|
|
284
|
+
border: var(--videolightbox-tile-border-width) solid var(--videolightbox-tile-border);
|
|
285
|
+
border-radius: var(--videolightbox-tile-radius);
|
|
286
|
+
box-shadow: var(--videolightbox-tile-shadow);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* The clip lives one level in: the sketch layer forces the drawn part's
|
|
290
|
+
overflow visible, which would let the picture's square corners escape. */
|
|
291
|
+
/* One grid cell stacks the badge on the still without positioning the
|
|
292
|
+
trigger, which would capture a `stretch` pad meant for the ancestor. */
|
|
293
|
+
.videolightbox-trigger {
|
|
294
|
+
display: grid;
|
|
295
|
+
grid-template: minmax(0, 1fr) / minmax(0, 1fr);
|
|
296
|
+
width: 100%;
|
|
297
|
+
height: 100%;
|
|
298
|
+
padding: 0;
|
|
299
|
+
border: none;
|
|
300
|
+
background: none;
|
|
301
|
+
overflow: hidden;
|
|
302
|
+
border-radius: var(--sketch-radius, var(--videolightbox-tile-radius));
|
|
303
|
+
cursor: pointer;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* The pad is laid over the ancestor the consumer positioned, so the one
|
|
307
|
+
button keeps the label and the whole card answers the pointer. It rides
|
|
308
|
+
above the consumer's own content, or copy beside the still would swallow
|
|
309
|
+
the pointer before the trigger heard it.
|
|
310
|
+
A transform on anything between here and that ancestor would make itself
|
|
311
|
+
the containing block and shrink the pad back to the still. */
|
|
312
|
+
.videolightbox-trigger.stretch::after {
|
|
313
|
+
content: '';
|
|
314
|
+
position: absolute;
|
|
315
|
+
inset: 0;
|
|
316
|
+
z-index: 1;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.videolightbox-trigger:focus-visible {
|
|
320
|
+
outline: none;
|
|
321
|
+
box-shadow: var(--ring-focus-md);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.videolightbox-trigger img,
|
|
325
|
+
.videolightbox-trigger .videolightbox-thumb {
|
|
326
|
+
grid-area: 1 / 1;
|
|
327
|
+
display: block;
|
|
328
|
+
width: 100%;
|
|
329
|
+
height: 100%;
|
|
330
|
+
object-fit: var(--videolightbox-thumb-fit, contain);
|
|
331
|
+
user-select: none;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.videolightbox-badge {
|
|
335
|
+
grid-area: 1 / 1;
|
|
336
|
+
place-self: center;
|
|
337
|
+
box-sizing: border-box;
|
|
338
|
+
display: flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: center;
|
|
341
|
+
width: var(--videolightbox-badge-default-size);
|
|
342
|
+
height: var(--videolightbox-badge-default-size);
|
|
343
|
+
color: var(--videolightbox-badge-default-icon);
|
|
344
|
+
background: var(--videolightbox-badge-default-surface);
|
|
345
|
+
border: var(--videolightbox-badge-default-border-width) solid
|
|
346
|
+
var(--videolightbox-badge-default-border);
|
|
347
|
+
border-radius: var(--radius-full);
|
|
348
|
+
transition:
|
|
349
|
+
background var(--videolightbox-badge-duration) var(--videolightbox-badge-easing),
|
|
350
|
+
border-color var(--videolightbox-badge-duration) var(--videolightbox-badge-easing),
|
|
351
|
+
color var(--videolightbox-badge-duration) var(--videolightbox-badge-easing);
|
|
352
|
+
|
|
353
|
+
--sketch-fill: var(--videolightbox-badge-default-surface);
|
|
354
|
+
--sketch-stroke: var(--videolightbox-badge-default-border);
|
|
355
|
+
--sketch-hatch-color: var(--videolightbox-badge-default-border);
|
|
356
|
+
--sketch-radius: var(--radius-full);
|
|
357
|
+
--sketch-shadow: none;
|
|
358
|
+
/* Travel is stated in px, and the play triangle is small enough to tear. */
|
|
359
|
+
--sketch-icon-off: var(--sketch-icon-soft);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.videolightbox-badge svg {
|
|
363
|
+
width: var(--videolightbox-badge-default-icon-size);
|
|
364
|
+
height: var(--videolightbox-badge-default-icon-size);
|
|
365
|
+
/* The triangle's mass sits left of centre; nudge it back onto the axis. */
|
|
366
|
+
margin-inline-start: 6%;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.videolightbox-trigger:hover .videolightbox-badge,
|
|
370
|
+
.videolightbox:global(.force-hover) .videolightbox-badge {
|
|
371
|
+
color: var(--videolightbox-badge-hover-icon);
|
|
372
|
+
background: var(--videolightbox-badge-hover-surface);
|
|
373
|
+
border-color: var(--videolightbox-badge-hover-border);
|
|
374
|
+
|
|
375
|
+
--sketch-fill: var(--videolightbox-badge-hover-surface);
|
|
376
|
+
--sketch-stroke: var(--videolightbox-badge-hover-border);
|
|
377
|
+
--sketch-hatch-color: var(--videolightbox-badge-hover-border);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.videolightbox-modal {
|
|
381
|
+
position: fixed;
|
|
382
|
+
inset: 0;
|
|
383
|
+
z-index: var(--z-modal);
|
|
384
|
+
box-sizing: border-box;
|
|
385
|
+
display: grid;
|
|
386
|
+
place-items: center;
|
|
387
|
+
padding: var(--videolightbox-overlay-padding);
|
|
388
|
+
background: var(--videolightbox-scrim-surface);
|
|
389
|
+
container-type: size;
|
|
390
|
+
opacity: 0;
|
|
391
|
+
transition: opacity var(--videolightbox-overlay-duration) var(--videolightbox-overlay-easing);
|
|
392
|
+
cursor: pointer;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.videolightbox-modal.shown {
|
|
396
|
+
opacity: 1;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.videolightbox-modal:focus {
|
|
400
|
+
outline: none;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.videolightbox-modal.inline {
|
|
404
|
+
position: relative;
|
|
405
|
+
inset: auto;
|
|
406
|
+
z-index: auto;
|
|
407
|
+
width: 100%;
|
|
408
|
+
aspect-ratio: 16 / 9;
|
|
409
|
+
cursor: default;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.videolightbox-close {
|
|
413
|
+
position: absolute;
|
|
414
|
+
top: var(--space-24);
|
|
415
|
+
right: var(--space-24);
|
|
416
|
+
/* 2.75rem = 44px, the min touch-target floor. */
|
|
417
|
+
width: 2.75rem;
|
|
418
|
+
height: 2.75rem;
|
|
419
|
+
display: flex;
|
|
420
|
+
align-items: center;
|
|
421
|
+
justify-content: center;
|
|
422
|
+
padding: 0;
|
|
423
|
+
background: var(--videolightbox-chrome-surface);
|
|
424
|
+
border: var(--videolightbox-chrome-border-width) solid var(--videolightbox-chrome-border);
|
|
425
|
+
border-radius: var(--videolightbox-chrome-radius);
|
|
426
|
+
color: var(--videolightbox-chrome-icon);
|
|
427
|
+
backdrop-filter: blur(var(--blur-md));
|
|
428
|
+
cursor: pointer;
|
|
429
|
+
transition: background var(--duration-200) ease;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.videolightbox-close:hover,
|
|
433
|
+
.videolightbox-modal:global(.force-hover) .videolightbox-close {
|
|
434
|
+
background: var(--videolightbox-chrome-hover-surface);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.videolightbox-close:focus-visible {
|
|
438
|
+
outline: none;
|
|
439
|
+
box-shadow: var(--ring-focus-md);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
@media (prefers-reduced-motion: reduce) {
|
|
443
|
+
.videolightbox-modal {
|
|
444
|
+
transition: none;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/* The clip plays at its own pixel size and shrinks only to fit the screen. */
|
|
449
|
+
.videolightbox-video {
|
|
450
|
+
display: block;
|
|
451
|
+
width: min(var(--videolightbox-natural-width, 100cqw), 100cqw, 100cqh * var(--videolightbox-ratio, 16 / 9));
|
|
452
|
+
aspect-ratio: var(--videolightbox-ratio, 16 / 9);
|
|
453
|
+
}
|
|
454
|
+
</style>
|