<figure id="media-74422-15638" class="media" role="figure" aria-labelledby="media-74422-15638-caption">
<div class="media__figure">
<div class="media__video">
<div class="video">
<video class="video__video" controls width="1280" height="720" playsinline poster="https://files.vidstack.io/sprite-fight/poster.webp" preload="none" data-video-translations="{"Current time":"Current time","Disable captions":"Disable captions","Enable captions":"Enable captions","Enter Fullscreen":"Enter Fullscreen","Enter PiP":"Enter PiP","Exit Fullscreen":"Exit Fullscreen","Exit PiP":"Exit PiP","Go back to previous menu":"Go back to previous menu","Ad":"Ad","AirPlay":"AirPlay","All":"All","Audio":"Audio","Auto":"Auto","Buffered":"Buffered","Captions":"Captions","Default":"Default","Disabled":"Disabled","Download":"Download","Duration":"Duration","Enabled":"Enabled","End":"End","Forward":"Forward","LIVE":"LIVE","Loop":"Loop","Mute":"Mute","Normal":"Normal","Pause":"Pause","Play":"Play","Played":"Played","Quality":"Quality","Reset":"Reset","Restart":"Restart","Rewind":"Rewind","Seek":"Seek","Settings":"Settings","Speed":"Speed","Start":"Start","Unmute":"Unmute","Volume":"Volume"}">
<source src="https://files.vidstack.io/sprite-fight/720p.mp4" type="video/mp4">
</video>
</div>
</div>
</div>
<figcaption class="media__caption" id="media-74422-15638-caption">
<div class="media__text">This is the caption <a class="link" data-link-type="external" href="https://example.com" rel="noopener noreferrer" target="_blank"><span class="link__icon"><svg class="icon icon--arrow-up-right-from-square" viewBox="0 0 200 200" aria-hidden="true">
<use xlink:href="/assets/icons/icons.dcaca1c147.svg#arrow-up-right-from-square"></use>
</svg></span><span class="link__text u-underline">with a link</span></a></div>
<div class="media__copyright">
<div class="copyright">
<button class="copyright__button u-overlay-link" type="button" title="Image attribution" aria-pressed="false">
<svg class="icon icon--copyright" viewBox="0 0 200 200" aria-hidden="true">
<use xlink:href="/assets/icons/icons.dcaca1c147.svg#copyright"></use>
</svg> </button>
<div class="copyright__floating" tabindex="0" role="floating" hidden data-floating-placements="bottom,top,left,right">Source: example.com</div>
</div>
</div>
</figcaption>
</figure>
{% set id = id ?? html_id('media') %}
{% set caption = caption ?? image.caption ?? false %}
{% set copyright = copyright ?? image.copyright ?? false %}
{% set captionId = 'caption' | namespaceInputId(id) -%}
<figure {{ html_attributes({
id: id,
class: 'media',
role: 'figure',
'aria-labelledby': caption or copyright ? captionId,
}, attrs ?? {}) }}>
<div class="media__figure">
{% if image|default %}
<div class="media__image">
{% include '@image' with image only %}
</div>
{% if lightbox|default %}
<div class="media__button">
{% if lightboxText|default %}
{% include '@button' with {
overlay: true,
text: lightboxText,
icon: 'magnifying-glass-plus',
attrs: {
'data-media-lightbox': 'lightbox' | namespaceInputId(id),
},
} only %}
{% else %}
{% include '@button' with {
overlay: true,
title: 'Enlarge image' | t('site'),
icon: 'magnifying-glass-plus',
attrs: {
'data-media-lightbox': 'lightbox' | namespaceInputId(id),
},
} only %}
{% endif %}
</div>
{% elseif link|default %}
<div class="media__button">
{% include '@button' with link | merge({
overlay: true,
}) only %}
</div>
{% endif %}
{% elseif video|default %}
<div class="media__video">
{% include '@video' with video only %}
</div>
{% endif %}
</div>
{% if caption or copyright %}
<figcaption class="media__caption" id="{{ captionId }}">
{% if caption %}
<div class="media__text">
{{- caption | componentize(inline_only=true) -}}
</div>
{% endif %}
{% if copyright %}
<div class="media__copyright">
{% include '@copyright' with {
text: copyright,
overlay: true,
} only %}
</div>
{% endif %}
</figcaption>
{% endif %}
{% if image|default and lightbox|default %}
<template id="{{ 'lightbox' | namespaceInputId(id) }}">
<div class="media__lightbox" aria-hidden="true">
<div class="media__lightbox-container">
<div class="media__lightbox-image">
{% include '@image' with lightbox only %}
</div>
<div class="media__lightbox-close">
{% include '@button' with {
icon: 'xmark',
title: 'Close' | t('site'),
attrs: {
'data-media-lightbox-action': 'close',
},
} only %}
</div>
</div>
</div>
</template>
{% endif %}
</figure>
{
"caption": "This is the caption <a href=\"https://example.com\">with a link</a>",
"copyright": "Source: example.com",
"video": {
"width": 1280,
"height": 720,
"poster": "https://files.vidstack.io/sprite-fight/poster.webp",
"sources": [
{
"src": "https://files.vidstack.io/sprite-fight/720p.mp4",
"type": "video/mp4"
}
]
}
}
@use 'layers';
.media {
--_media-figure-inset-inline: 1.6rem;
--_media-figure-inset-block: 1.6rem;
--_media-copyright-size: 2.4rem;
display: block;
position: relative;
}
.media__figure {
isolation: isolate;
overflow: clip;
position: relative;
z-index: 1;
}
.media__image {
border-radius: var(--border-radius-medium);
overflow: clip;
position: relative;
z-index: 1;
}
.media:has(.media__copyright) :is(.plyr__controls) {
padding-inline-end: calc(var(--_media-figure-inset-inline) * 2 + var(--_media-copyright-size) + 0.8rem);
}
.media__button {
--overlay-link-inset: -100vw;
display: flex;
inset-block-end: 0;
inset-inline-start: 0;
padding-block: var(--_media-figure-inset-block);
padding-inline: var(--_media-figure-inset-inline);
position: absolute;
z-index: 3;
}
.media__caption {
position: relative;
text-align: center;
z-index: 2;
}
.media__text {
margin-inline: auto;
max-inline-size: 75%;
min-inline-size: 50rem;
}
.media__copyright {
--copyright-size: var(--_media-copyright-size);
--overlay-link-inset: calc(var(--_media-figure-inset-block) * -1) calc(var(--_media-figure-inset-inline) * -1);
inset-block-start: 0;
inset-inline-end: 0;
line-height: 0;
padding-block: var(--_media-figure-inset-block);
padding-inline: var(--_media-figure-inset-inline);
position: absolute;
translate: 0 -100%;
}
.media__text {
color: var(--media-caption-color, var(--secondary-text-color));
font-size: var(--media-caption-font-size, var(--font-size-caption));
line-height: var(--media-caption-line-height, var(--line-height-regular));
padding-block-start: var(--media-caption-gap, 1.2rem);
padding-inline: var(--media-caption-padding-inline, 0);
}
.media__lightbox {
--icon-button-size: 4rem;
--_media-lightbox-padding: clamp(2rem, 10vw, 4rem);
animation-duration: var(--duration-fast);
animation-name: opacity;
backdrop-filter: blur(10px);
background-color: var(--media-lightbox-background-color, rgba(0, 0, 0, 0.8));
color: var(--media-lightbox-color, var(--color-white));
display: flex;
flex-direction: column;
inset: 0;
overflow: hidden;
overscroll-behavior: contain;
padding-block-end: var(--_media-lightbox-padding);
padding-block-start: calc(var(--_media-lightbox-padding) + 2rem + var(--icon-button-size));
padding-inline: var(--_media-lightbox-padding);
position: fixed;
z-index: #{layers.position('overlay')};
&:is([aria-hidden='true']) {
display: none;
}
}
.media__lightbox-container {
align-items: center;
block-size: 100%;
display: flex;
flex-direction: column;
flex-grow: 1;
inline-size: 100%;
justify-content: center;
}
.media__lightbox-image {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
inline-size: 100%;
position: relative;
}
.media__lightbox-close {
inset-block-start: var(--_media-lightbox-padding);
inset-inline-end: var(--_media-lightbox-padding);
line-height: 0;
pointer-events: all;
position: absolute;
}
import { on } from 'delegated-events';
import A11yDialog from 'a11y-dialog';
import abort from '../../../javascripts/utils/abort';
import fitImageInsideContainer from '../../../javascripts/utils/fitImageInsideContainer';
on('click', '[data-media-lightbox]', (event) => {
const { currentTarget: $trigger } = event;
const { mediaLightbox: templateId } = $trigger.dataset;
event.preventDefault();
if (!templateId) {
return;
}
const $media = $trigger.closest<HTMLElement>('.media') ?? abort();
const $template = document.querySelector<HTMLTemplateElement>(`#${templateId}`) ?? abort();
const $fragment = $template.content.cloneNode(true) as DocumentFragment;
const $lightbox = $fragment.querySelector<HTMLElement>('.media__lightbox') ?? abort();
const $container = $lightbox.querySelector<HTMLElement>('.media__lightbox-container') ?? abort();
const $image = $container.querySelector<HTMLElement>('.media__lightbox-image') ?? abort();
const dialog = new A11yDialog($lightbox);
const resizeOberserver = new ResizeObserver(() => {
fitImageInsideContainer($image, $container);
});
$lightbox.addEventListener('show', () => {
fitImageInsideContainer($image, $container);
resizeOberserver.observe($lightbox);
});
$lightbox.addEventListener('hide', () => {
resizeOberserver.disconnect();
});
$lightbox.addEventListener('destroy', () => {
$lightbox.remove();
});
$lightbox.addEventListener('click', (clickEvent) => {
const { target: $target } = clickEvent;
if ($target instanceof Element && $target.closest('[data-media-lightbox-action="close"]')) {
clickEvent.preventDefault();
dialog.destroy();
}
});
$media.appendChild($lightbox);
dialog.show();
});
No notes defined.