href value of this link is used as the source for the enlarged media by default.download attribute, a CTA for downloading the source file is displayed in the overlay (only works with image files).data-zoom.data-zoom is defined, the preview is displayed in the zoom.Preview
Zoom
<!-- Default -->
<!-- Vue Component -->
<script type="text/x-template" id="mediaOverlay-overlay-template">
<div class="mediaOverlay" :data-visible="isVisible == true">
<nav class="mediaOverlay-nav">
<button class="mediaOverlay-close" data-action="close"><span>Schließen</span></button>
</nav>
<div class="mediaOverlay-header">
<div v-if="asset.description" class="mediaOverlay-description">
<span v-html="asset.description.caption" class="mediaOverlay-caption"></span> <span v-html="asset.description.copyright" class="mediaOverlay-copyright"></span>
</div>
<div v-if="asset.cta.url" class="mediaOverlay-cta">
<a :href="asset.cta.url" class="button" download=""><svg class="icon is-download-simple"><use xlink:href="/icons/icons-bold.svg#icon--download-simple"></use></svg><span class="button-label">Herunterladen</span></a>
</div>
</div>
<div class="mediaOverlay-content" :data-type="asset.type">
<!-- Image -->
<figure v-if="asset.image && asset.image.src" class="mediaOverlay-figure">
<img class="figure-image" :src="asset.image.src" :alt="asset.image.alt" :style="{ 'background-image': 'url(' + asset.image.lowres + ')' }" data-trigger="">
</figure>
<!-- Video -->
<div v-if="asset.video && asset.video.src" class="mediaOverlay-figure">
<figure class="videoPlayer">
<div class="videoPlayer-video">
<video :poster="asset.video.poster" class="video" controls>
<source :src="asset.video.src" :type="asset.video.type">
</video>
</div>
</figure>
</div>
<!-- YouTube video -->
<figure v-if="asset.video && asset.video.isYouTube" class="mediaOverlay-figure is-youtube">
<iframe :src="'https://www.youtube-nocookie.com/embed/' + asset.video.id + '?rel=0'" width="1280" height="720" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</figure>
<!-- Audio -->
<div v-if="asset.audio && asset.audio.src" class="mediaOverlay-figure">
<figure class="audioPlayer">
<div v-if="asset.audio.poster" class="audioPlayer-poster" aria-hidden="true">
<img :src="asset.audio.poster" alt=""/>
</div>
<div class="audioPlayer-audio">
<audio :src="asset.audio.src" class="audio" controls></audio>
</div>
</figure>
</div>
</div>
</div>
</script>
<div v-cloak id="mediaOverlay">
<overlay></overlay>
</div>
<!-- Blocked Video -->
<div id="mediaOverlay" data-v-app="" style="transition-duration: 150ms;">
<div class="mediaOverlay" data-visible="true" style="--aspect-ratio: 1504/846;">
<nav class="mediaOverlay-nav"><button class="mediaOverlay-close" data-action="close" data-active="true" data-trigger=""><span>Schließen</span></button></nav>
<div class="mediaOverlay-header">
<div class="mediaOverlay-description"><span class="mediaOverlay-caption">Ich bin der Titel des Mediums</span> <span class="mediaOverlay-copyright"></span></div>
</div>
<div class="mediaOverlay-content" data-type="video">
<figure class="mediaOverlay-figure is-youtube">
<iframe src="https://cloud.ccm19.de/x-content-blocked.html?apiKey=d25f730e279e287e431f83c2f4b78964c741fa1266190ca5&domain=664f6d5790e20920600adc62&gen=2&theme=664f6d5790e20920600adc71&lang=de_DE&v=1733243400&url=https%3A%2F%2Fwww.youtube-nocookie.com%2Fembed%2F2eRWscBHHAI%3Frel%3D0&embedding=674eb432b73797f4e20c9c97" width="1280" height="720" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<div class="ccm--consent-switch"><input id="ccm--consent-switch-e3ad1c8910caef19" type="checkbox"><label for="ccm--consent-switch-e3ad1c8910caef19">Inhalt für "YouTube Video" zulassen</label><a href="javascript:">(Details zum Anbieter)</a></div>
</figure>
</div>
</div>
</div>
<!-- Demo Only -->
<script type="text/x-template" id="mediaOverlay-overlay-template">
<div class="mediaOverlay" :data-visible="isVisible == true">
<nav class="mediaOverlay-nav">
<button class="mediaOverlay-close" data-action="close"><span>Schließen</span></button>
</nav>
<div class="mediaOverlay-header">
<div v-if="asset.description" class="mediaOverlay-description">
<span v-html="asset.description.caption" class="mediaOverlay-caption"></span> <span v-html="asset.description.copyright" class="mediaOverlay-copyright"></span>
</div>
<div v-if="asset.cta.url" class="mediaOverlay-cta">
<a :href="asset.cta.url" class="button" download=""><svg class="icon is-download-simple"><use xlink:href="/icons/icons-bold.svg#icon--download-simple"></use></svg><span class="button-label">Herunterladen</span></a>
</div>
</div>
<div class="mediaOverlay-content" :data-type="asset.type">
<!-- Image -->
<figure v-if="asset.image && asset.image.src" class="mediaOverlay-figure">
<img class="figure-image" :src="asset.image.src" :alt="asset.image.alt" :style="{ 'background-image': 'url(' + asset.image.lowres + ')' }" data-trigger="">
</figure>
<!-- Video -->
<div v-if="asset.video && asset.video.src" class="mediaOverlay-figure">
<figure class="videoPlayer">
<div class="videoPlayer-video">
<video :poster="asset.video.poster" class="video" controls>
<source :src="asset.video.src" :type="asset.video.type">
</video>
</div>
</figure>
</div>
<!-- YouTube video -->
<figure v-if="asset.video && asset.video.isYouTube" class="mediaOverlay-figure is-youtube">
<iframe :src="'https://www.youtube-nocookie.com/embed/' + asset.video.id + '?rel=0'" width="1280" height="720" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</figure>
<!-- Audio -->
<div v-if="asset.audio && asset.audio.src" class="mediaOverlay-figure">
<figure class="audioPlayer">
<div v-if="asset.audio.poster" class="audioPlayer-poster" aria-hidden="true">
<img :src="asset.audio.poster" alt=""/>
</div>
<div class="audioPlayer-audio">
<audio :src="asset.audio.src" class="audio" controls></audio>
</div>
</figure>
</div>
</div>
</div>
</script>
<div v-cloak id="mediaOverlay">
<overlay></overlay>
</div>
<div style="min-height: 100vh;"><!-- Force displaying scrollbar -->
<div class="doc-section">
<h2 class="doc-section-title">Galerie mit Bildern</h2>
<div class="doc-variant-samples">
<div class="mediaGallery">
<ul class="mediaGallery-items">
<li class="mediaGallery-item" data-type="image" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(../../images/placeholder/dummy-image-640w.jpg);">
<a class="mediaGallery-link" href="../../images/placeholder/dummy-image-full-size.jpg" download target="_blank">
<img src="../../images/placeholder/dummy-image-640w.jpg" data-zoom="../../images/placeholder/dummy-image.jpg" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span><small class="mediaGallery-copyright">Foto von <a class="link" href="https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Sergio Medina</a></small></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="image" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(../../images/placeholder/dummy-image-640w.jpg);">
<a class="mediaGallery-link" href="../../images/placeholder/dummy-image-full-size.jpg" target="_blank">
<img src="../../images/placeholder/dummy-image-640w.jpg" data-zoom="../../images/placeholder/dummy-image.jpg" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="mediaGallery-caption"><span>Overlay ohne Download</span><small class="mediaGallery-copyright">Foto von <a class="link" href="https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Sergio Medina</a></small></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="image" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(https://picsum.photos/id/18/480/720);">
<a class="mediaGallery-link" href="https://picsum.photos/id/18/1120/1680" download target="_blank">
<img src="https://picsum.photos/id/18/480/720" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="image" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(https://picsum.photos/id/193/720/360);">
<a class="mediaGallery-link" href="https://picsum.photos/id/193/1680/840" download target="_blank">
<img src="https://picsum.photos/id/193/720/360" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span></figcaption>
</figure>
</li>
</ul>
</div>
</div>
</div>
<div class="doc-section">
<h2 class="doc-section-title">Galerie mit Video- und Audiodateien</h2>
<div class="doc-variant-samples">
<div class="mediaGallery">
<ul class="mediaGallery-items">
<li class="mediaGallery-item" data-type="video" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(../../videos/placeholder/dummy-video.jpg);">
<a class="mediaGallery-link" href="../../videos/placeholder/dummy-video.mp4" target="_blank">
<img src="../../videos/placeholder/dummy-video.jpg" alt="Alternative Beschreibung des Videos" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span><small class="mediaGallery-copyright">Video von <a class="link" href="https://pixabay.com/de/videos/gelb-fl%C3%BCssig-farbe-raum-27803/">Pixabay</a></small></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="video" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(https://img.youtube.com/vi/kg2fwlFsZss/maxresdefault.jpg);">
<a class="mediaGallery-link" href="https://www.youtube-nocookie.com/watch?v=kg2fwlFsZss" target="_blank">
<img src="https://img.youtube.com/vi/kg2fwlFsZss/maxresdefault.jpg" alt="Alternative Beschreibung des Videos" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="audio" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview" style="background-image: url(../../audios/placeholder/dummy-audio-poster.jpg);">
<a class="mediaGallery-link" href="../../audios/placeholder/dummy-audio.mp3" target="_blank">
<img src="../../audios/placeholder/dummy-audio-poster.jpg" alt="Alternative Beschreibung des Tons" />
</a>
<figcaption class="mediaGallery-caption"><span>Ich bin der Titel des Mediums</span><small class="mediaGallery-copyright">Audio von <a class="link" href="https://pixabay.com/sound-effects/birds-chirping-ambiance-26052/">Pixabay</a></small></figcaption>
</figure>
</li>
<li class="mediaGallery-item" data-type="audio" data-media-overlay data-action="open" data-trigger-selector=".mediaGallery-link">
<figure class="mediaGallery-preview">
<a class="mediaGallery-link" href="../../audios/placeholder/dummy-audio.mp3" target="_blank">
</a>
<figcaption class="mediaGallery-caption"><span>Audiodatei ohne Vorschau</span></figcaption>
</figure>
</li>
</ul>
</div>
</div>
</div>
<div class="doc-section">
<h2 class="doc-section-title">Video</h2>
<div class="doc-variant-samples">
<div data-media-overlay data-action="open" data-trigger-selector=".figure a" data-type="video">
<figure class="figure" style="">
<a href="../../videos/placeholder/dummy-video.mp4" target="_blank">
<img class="figure-image" src="../../videos/placeholder/dummy-video.jpg" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="figure-caption"><span>Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.</span><small class="figure-copyright">Foto von <a class="link" href="https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Sergio Medina</a></small></figcaption>
</figure>
</div>
</div>
</div>
<div class="doc-section">
<h2 class="doc-section-title">Youtube Video</h2>
<div class="doc-variant-samples">
<div data-media-overlay data-action="open" data-trigger-selector=".figure a" data-type="video">
<figure class="figure" style="">
<a href="https://www.youtube-nocookie.com/watch?v=kg2fwlFsZss" target="_blank">
<img class="figure-image" src="https://img.youtube.com/vi/kg2fwlFsZss/maxresdefault.jpg" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="figure-caption"><span>Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.</span><small class="figure-copyright">Foto von <a class="link" href="https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Sergio Medina</a></small></figcaption>
</figure>
</div>
</div>
</div>
<div class="doc-section">
<h2 class="doc-section-title">Grafik</h2>
<div class="doc-variant-samples">
<div data-media-overlay data-action="open" data-trigger-selector=".figure a" data-type="image">
<figure class="figure" data-intrinsic-ratio="true" style="">
<a href="../../images/placeholder/flow-chart.svg" target="_blank">
<img class="figure-image" src="../../images/placeholder/flow-chart.svg" alt="Alternative Bildbeschreibung" />
</a>
<figcaption class="figure-caption"><span>Abbildung mit freiem Seitenverhältnis (nur für Grafiken)</span><small class="figure-copyright">Quelle: Unbekannt</small></figcaption>
</figure>
</div>
</div>
</div>
</div>
<!-- Default -->
<!-- Vue Component -->
<script type="text/x-template" id="mediaOverlay-overlay-template"></script>
<div v-cloak id="mediaOverlay">
<overlay></overlay>
</div>
<!-- Blocked Video -->
<div id="mediaOverlay" data-v-app="" style="transition-duration: 150ms;">
<div class="mediaOverlay" data-visible="true" style="--aspect-ratio: 1504/846;">
<nav class="mediaOverlay-nav"><button class="mediaOverlay-close" data-action="close" data-active="true" data-trigger=""><span>Schließen</span></button></nav>
<div class="mediaOverlay-header">
<div class="mediaOverlay-description"><span class="mediaOverlay-caption">Ich bin der Titel des Mediums</span> <span class="mediaOverlay-copyright"></span></div>
</div>
<div class="mediaOverlay-content" data-type="video">
<figure class="mediaOverlay-figure is-youtube">
<iframe src="https://cloud.ccm19.de/x-content-blocked.html?apiKey=d25f730e279e287e431f83c2f4b78964c741fa1266190ca5&domain=664f6d5790e20920600adc62&gen=2&theme=664f6d5790e20920600adc71&lang=de_DE&v=1733243400&url=https%3A%2F%2Fwww.youtube-nocookie.com%2Fembed%2F2eRWscBHHAI%3Frel%3D0&embedding=674eb432b73797f4e20c9c97" width="1280" height="720" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
<div class="ccm--consent-switch"><input id="ccm--consent-switch-e3ad1c8910caef19" type="checkbox"><label for="ccm--consent-switch-e3ad1c8910caef19">Inhalt für "YouTube Video" zulassen</label><a href="javascript:">(Details zum Anbieter)</a></div>
</figure>
</div>
</div>
</div>
<!-- Demo Only -->
{{> '@mediaoverlay--vue-component'}}
<div style="min-height: 100vh;"><!-- Force displaying scrollbar -->
{{#variants}}
<div class="doc-section">
{{#title}}
<h2 class="doc-section-title">{{{.}}}</h2>
{{/title}}
<div class="doc-variant-samples">
{{#unless mediagallery}}
<div data-media-overlay data-action="open"{{#trigger-selector}} data-trigger-selector="{{.}}"{{/trigger-selector}}{{#type}} data-type="{{.}}"{{/type}}>
{{#figure}}
{{render '@figure' (contextData '@mediaoverlay' this) merge=true}}
{{/figure}}
</div>
{{/unless}}
{{#mediagallery}}
{{render '@mediagallery' (contextData '@mediaoverlay' this) merge=true}}
{{/mediagallery}}
</div>
</div>
{{/variants}}
</div>
/* Default */
{
"is-visible": false,
"labels": {
"close": "Schließen"
},
"description": {
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"figure": {
"src": "/images/placeholder/dummy-image.jpg",
"alt": "Alternative Bildbeschreibung",
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"cta": {
"label": "Herunterladen",
"is-download": "download",
"icon": {
"id": "download-simple",
"weight": "bold"
}
}
}
/* Vue Component */
{
"is-visible": false,
"labels": {
"close": "Schließen"
},
"description": {
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"figure": {
"src": "/images/placeholder/dummy-image.jpg",
"alt": "Alternative Bildbeschreibung",
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"cta": {
"label": "Herunterladen",
"is-download": "download",
"icon": {
"id": "download-simple",
"weight": "bold"
}
}
}
/* Blocked Video */
{
"is-visible": false,
"labels": {
"close": "Schließen"
},
"description": {
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"figure": {
"src": "/images/placeholder/dummy-image.jpg",
"alt": "Alternative Bildbeschreibung",
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"cta": {
"label": "Herunterladen",
"is-download": "download",
"icon": {
"id": "download-simple",
"weight": "bold"
}
}
}
/* Demo Only */
{
"is-visible": false,
"labels": {
"close": "Schließen"
},
"description": {
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"figure": {
"src": "/images/placeholder/dummy-image.jpg",
"alt": "Alternative Bildbeschreibung",
"caption": "Selfies fixie next level trust fund jean shorts photo booth raw denim butcher mixtape ethical mustache.",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>"
},
"cta": {
"label": "Herunterladen",
"is-download": "download",
"icon": {
"id": "download-simple",
"weight": "bold"
}
},
"variants": [
{
"title": "Galerie mit Bildern",
"mediagallery": {
"title": null,
"items": [
{
"type": "image",
"caption": "Ich bin der Titel des Mediums",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>",
"preview": {
"src": "/images/placeholder/dummy-image-640w.jpg",
"alt": "Alternative Bildbeschreibung"
},
"link": {
"url": "/images/placeholder/dummy-image-full-size.jpg",
"download": true,
"target": "_blank"
},
"zoom": {
"src": "/images/placeholder/dummy-image.jpg"
}
},
{
"type": "image",
"caption": "Overlay ohne Download",
"copyright": "Foto von <a class=\"link\" href=\"https://unsplash.com/de/@ssergiomedinaa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText\">Sergio Medina</a>",
"preview": {
"src": "/images/placeholder/dummy-image-640w.jpg",
"alt": "Alternative Bildbeschreibung"
},
"link": {
"url": "/images/placeholder/dummy-image-full-size.jpg",
"download": false,
"target": "_blank"
},
"zoom": {
"src": "/images/placeholder/dummy-image.jpg"
}
},
{
"type": "image",
"caption": "Ich bin der Titel des Mediums",
"preview": {
"src": "https://picsum.photos/id/18/480/720",
"alt": "Alternative Bildbeschreibung"
},
"link": {
"url": "https://picsum.photos/id/18/1120/1680",
"download": true,
"target": "_blank"
}
},
{
"type": "image",
"caption": "Ich bin der Titel des Mediums",
"preview": {
"src": "https://picsum.photos/id/193/720/360",
"alt": "Alternative Bildbeschreibung"
},
"link": {
"url": "https://picsum.photos/id/193/1680/840",
"download": true,
"target": "_blank"
}
}
]
}
},
{
"title": "Galerie mit Video- und Audiodateien",
"mediagallery": {
"title": null,
"items": [
{
"type": "video",
"caption": "Ich bin der Titel des Mediums",
"copyright": "Video von <a class=\"link\" href=\"https://pixabay.com/de/videos/gelb-fl%C3%BCssig-farbe-raum-27803/\">Pixabay</a>",
"preview": {
"src": "/videos/placeholder/dummy-video.jpg",
"alt": "Alternative Beschreibung des Videos"
},
"link": {
"url": "/videos/placeholder/dummy-video.mp4"
}
},
{
"type": "video",
"link": {
"url": "https://www.youtube-nocookie.com/watch?v=kg2fwlFsZss"
},
"caption": "Ich bin der Titel des Mediums",
"preview": {
"src": "https://img.youtube.com/vi/kg2fwlFsZss/maxresdefault.jpg",
"alt": "Alternative Beschreibung des Videos"
}
},
{
"type": "audio",
"caption": "Ich bin der Titel des Mediums",
"copyright": "Audio von <a class=\"link\" href=\"https://pixabay.com/sound-effects/birds-chirping-ambiance-26052/\">Pixabay</a>",
"preview": {
"src": "/audios/placeholder/dummy-audio-poster.jpg",
"alt": "Alternative Beschreibung des Tons"
},
"link": {
"url": "/audios/placeholder/dummy-audio.mp3"
}
},
{
"type": "audio",
"caption": "Audiodatei ohne Vorschau",
"link": {
"url": "/audios/placeholder/dummy-audio.mp3"
}
}
]
}
},
{
"title": "Video",
"trigger-selector": ".figure a",
"type": "video",
"figure": {
"src": "/videos/placeholder/dummy-video.jpg",
"link": {
"url": "/videos/placeholder/dummy-video.mp4",
"target": "_blank"
}
}
},
{
"title": "Youtube Video",
"trigger-selector": ".figure a",
"type": "video",
"figure": {
"src": "https://img.youtube.com/vi/kg2fwlFsZss/maxresdefault.jpg",
"link": {
"url": "https://www.youtube-nocookie.com/watch?v=kg2fwlFsZss",
"target": "_blank"
}
}
},
{
"title": "Grafik",
"trigger-selector": ".figure a",
"type": "image",
"figure": {
"intrinsic-ratio": true,
"src": "/images/placeholder/flow-chart.svg",
"caption": "Abbildung mit freiem Seitenverhältnis (nur für Grafiken)",
"copyright": "Quelle: Unbekannt",
"link": {
"url": "/images/placeholder/flow-chart.svg",
"target": "_blank"
}
}
}
]
}
import mediaOverlay from "./_mediaOverlay.script.js";
mediaOverlay.init();
export default (function (){
const defaults = {
selectors: {
overlay: '#mediaOverlay',
trigger: '.mediaOverlay [data-action], [data-media-overlay][data-action]',
content: '.mediaOverlay-content',
image: '[data-type="image"] .mediaOverlay-figure img',
},
types: [
"image",
"video",
"audio",
"iframe",
],
durations: {
visibility: 150,
},
minWidth: 640,
showFocus: false,
};
function init(options) {
options = options || {};
const settings = Object.assign({}, defaults, options);
if (typeof settings.minWidth === "number" && window.innerWidth < settings.minWidth) {
return;
}
const overlay = document.querySelector(defaults.selectors.overlay);
if (overlay) {
new Overlay(overlay, options);
}
}
const Overlay = function(appContainer, options) {
this.settings = Object.assign({}, defaults, options);
if (typeof this.settings.durations.visibility === "number") {
appContainer.style.transitionDuration = this.settings.durations.visibility + "ms";
}
// Data prototype
this._data = {
isVisible: false,
asset: {
image: {
src: null,
alt: null,
lowres: null,
},
video: {
src: null,
type: "video/mp4", // FIXME: Should be customisable
poster: null,
id: null,
isYouTube: false,
},
audio: {
src: null,
poster: null,
id: null,
},
description: {
caption: null,
copyright: null,
},
cta: {
link: null,
}
},
};
// Vue component
this.data = Vue.reactive(Object.assign({}, this._data));
const data = this.data;
this.vue = Vue.createApp({
data() {
return data;
},
methods: {
},
}).component("overlay", {
template: "#mediaOverlay-overlay-template",
data() {
return data;
},
}).mount(appContainer);
this.container = this.vue.$el;
// Actions
const actions = {
open: (data) => this.open(data),
close: () => this.close(),
};
// Init triggers
const triggers = document.querySelectorAll(defaults.selectors.trigger);
this.currentTrigger = null;
triggers.forEach((element) => {
if (typeof element.dataset.action !== "string" || typeof actions[element.dataset.action] !== "function") {
console.warn(`mediaOverlay: Action "${element.dataset.action}" not defined!`);
return;
}
const action = element.dataset.action;
element.setAttribute("data-active", "true");
// Collect Data
let assetData = {};
if (action === "open") {
assetData = this.getAssetData(element);
}
// Trigger
let trigger = element;
if (typeof element.dataset.triggerSelector === "string" && element.dataset.triggerSelector !== "") {
const selector = element.dataset.triggerSelector;
let selectedElement = element.querySelector(selector);
if (selectedElement) {
trigger = selectedElement;
} else {
console.warn(`mediaOverlay: Trigger "${selector}" not found.`);
}
}
trigger.setAttribute("data-trigger", "");
trigger.addEventListener("click", (event) => {
event.preventDefault();
if (action === "open") {
this.currentTrigger = trigger;
}
actions[action](assetData);
});
});
// Add blocker
this.blocker = document.createElement("div");
this.blocker.classList.add("mediaOverlay-blocker");
this.blocker.style.display = "none";
const contentContainer = this.container.querySelector(this.settings.selectors.content);
if (! contentContainer) {
console.warn(`mediaOverlay: Container "${this.settings.selectors.content}" for blocker not found.`);
} else {
contentContainer.appendChild(this.blocker);
}
this.siblings = appContainer.parentNode.querySelectorAll(`:scope > *:not(${defaults.selectors.overlay})`);
// Add keyboard controls
document.addEventListener('keydown', (e) => {
if (! this.data.isVisible) {
return true;
}
let preventDefault = false;
if ([27, 32].indexOf(e.keyCode) !== -1) {
preventDefault = true;
this.close();
}
if (preventDefault) {
e.preventDefault();
}
});
}
Overlay.prototype.getAssetData = function(container) {
const data = deepClone(this._data.asset);
data.type = "image";
if (typeof container.dataset.type === "string" && container.dataset.type !== "" && this.settings.types.includes(container.dataset.type)) {
data.type = container.dataset.type;
}
if (container.querySelector("figure")) {
const source = container.querySelector("figure");
// Image or video URL
let previewUrl = "",
mediaUrl = "",
downloadUrl = "";
if (source.querySelector("img[src]")) {
const image = source.querySelector("img[src]");
previewUrl = image.src;
if (typeof image.dataset.zoom === "string" && image.dataset.zoom !== "") {
mediaUrl = image.dataset.zoom;
}
}
if (source.querySelector("a[href]")) {
if (mediaUrl === "") {
mediaUrl = source.querySelector("a[href]").href;
}
if (source.querySelector("a[href]").hasAttribute("download")) {
downloadUrl = source.querySelector("a[href]").href;
}
}
if (mediaUrl === "" && previewUrl) {
mediaUrl = previewUrl;
}
if (mediaUrl === "") {
console.warn("mediaOverlay: No source found!");
return data;
}
if (data.type === "video") {
data.video.src = mediaUrl;
data.video.poster = previewUrl;
data.video.id = this.getVideoId(mediaUrl);
data.video.isYouTube = typeof data.video.id === "string";
if (data.video.isYouTube) {
data.video.src = null;
}
} else if (data.type === "audio") {
data.audio.src = mediaUrl;
data.audio.poster = previewUrl;
} else if (data.type === "iframe") {
} else {
data.image.src = mediaUrl;
data.image.lowres = previewUrl;
}
// Image alt
if (source.querySelector("img[alt]")) {
data.image.alt = source.querySelector("img[alt]").alt;
}
// Description
if (source.querySelector("figcaption")) {
const caption = source.querySelector("figcaption");
if (caption.querySelector(":scope > span")) {
data.description.caption = caption.querySelector(":scope > span").innerHTML;
}
if (caption.querySelector('[class*="copyright"')) {
data.description.copyright = caption.querySelector('[class*="copyright"').innerHTML;
}
}
// CTA
if (downloadUrl !== "") {
data.cta.url = downloadUrl;
}
}
return data;
}
Overlay.prototype.getVideoId= function(url) {
let regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/,
matches = url.match(regExp);
if (! matches || ! matches[7].length === 11) {
return null;
}
return matches[7];
}
Overlay.prototype.open = function(asset) {
this.data.asset = asset;
this.vue.$nextTick(() => {
const image = this.container.querySelector(this.settings.selectors.image);
if (image) {
image.addEventListener('load', (e) => {
this.showBlocker(e.target);
});
}
const media = this.container.querySelector("iframe, .audioPlayer, .videoPlayer");
if (media) {
this.setMediaSize(media);
}
const closeButton = this.container.querySelector('[data-action="close"]');
if (closeButton) {
closeButton.focus({ focusVisible: this.settings.showFocus });
}
this.inertSiblings(true);
});
this.toggle(true);
}
Overlay.prototype.close = function() {
this.data.asset = this._data.asset;
this.container.style.removeProperty("--aspect-ratio");
this.inertSiblings(false);
if (this.currentTrigger) {
this.currentTrigger.focus({ focusVisible: this.settings.showFocus });
this.currentTrigger = null;
}
this.showBlocker();
this.toggle(false);
}
Overlay.prototype.toggle = function(isVisible) {
if (typeof isVisible !== "boolean") {
isVisible = ! this.data.isVisible;
}
this.data.isVisible = isVisible;
document.querySelector("html").dataset.mediaOverlayVisible = isVisible;
}
Overlay.prototype.updateVue = function() {
if (typeof this.vue === "object") {
this.vue.$forceUpdate();
}
}
Overlay.prototype.setMediaSize = function(element) {
this.container.style.setProperty("--aspect-ratio", element.clientWidth + "/" + element.clientHeight);
const contentContainer = this.container.querySelector(this.settings.selectors.content);
if (! contentContainer) {
return;
}
const paddingLeft = getPadding(contentContainer, "left"),
paddingRight = getPadding(contentContainer, "right"),
maxWidth = contentContainer.clientWidth - (paddingLeft + paddingRight);
if (element.offsetWidth > maxWidth) {
element.style.width = maxWidth + "px";
element.style.height = "auto";
}
}
Overlay.prototype.showBlocker = function(image) {
if (typeof image !== "object") {
this.blocker.style.display = "none";
return;
}
this.blocker.style.display = "block";
const dimension = getRenderedSize(image);
this.blocker.style.width = dimension.width + "px";
this.blocker.style.height = dimension.height + "px";
}
Overlay.prototype.inertSiblings = function(inert) {
this.siblings.forEach(sibling => {
if (inert) {
sibling.setAttribute("inert", "");
} else {
sibling.removeAttribute("inert");
}
});
}
function getRenderedSize(image, objectFit){
if (typeof objectFit !== "string") {
objectFit = "contain";
}
const imageWidth = image.naturalWidth,
imageHeight = image.naturalHeight,
containerWidth = image.width,
containerHeight = image.height;
const imageRatio = imageWidth / imageHeight,
containerRatio = containerWidth / containerHeight;
let width = containerWidth,
height = containerHeight;
if (objectFit == "contain") {
if (imageRatio > containerRatio) {
height = containerWidth / imageRatio;
} else {
width = containerHeight * imageRatio;
}
} else if (objectFit == "cover") {
if (imageRatio < containerRatio) {
height = containerWidth / imageRatio;
} else {
width = containerHeight * imageRatio;
}
}
return {
width: width,
height: height,
};
}
const getPadding = function(element, side) {
let padding = 0;
if (typeof element !== "object" || typeof side !== "string" || ! ["top", "bottom", "left", "right", "inline", "block"].includes(side)) {
return padding;
}
padding = window.getComputedStyle(element).getPropertyValue("padding-" + side);
return parseInt(padding);
}
const deepClone = function(object) {
return JSON.parse(JSON.stringify(object));
}
return {
init: init
}
})();
$mediaOverlay_background: rgba($_GRAY-900, .93) !default;
$mediaOverlay_header_background: $_gray-900 !default;
$mediaOverlay-close_stroke-width: 2 !default;
$mediaOverlay_icons: (
close: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="{{color}}" stroke-width="#{$mediaOverlay-close_stroke-width}" stroke-linecap="round" stroke-linejoin="round"><path d="M18.75 5.25L5.25 18.75"/><path d="M18.75 18.75L5.25 5.25"/></svg>',
zoom-in: '<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="{{color}}" stroke-width="#{$mediaOverlay-close_stroke-width}" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6H26V12"/><path d="M19 13L26 6"/><path d="M12 26H6V20"/><path d="M13 19L6 26"/><path d="M26 20V26H20"/><path d="M19 19L26 26"/><path d="M6 12V6H12"/><path d="M13 13L6 6"/></svg>',
) !default;
$mediaOverlay_zoom-in_styles: (
width: 48px,
height: 48px,
background: $_black center center no-repeat,
background-image: svg-url(map.get($mediaOverlay_icons, zoom-in), $white),
background-size: 32px,
color: $white,
border-radius: 3px,
transition-duration: $_transition-duration--in,
transition-timing-function: easing(),
transition-property: (background-size, transform),
) !default;
$mediaOverlay_zoom-in_states: (
default: $mediaOverlay_zoom-in_styles,
hover: (
background-size: 40px,
transform: scale(1.1),
transition-timing-function: easing(back, in),
),
focus: map.merge( $_focus_styles,
(
opacity: 1,
)
),
) !default;
@import "_mediaOverlay.settings";
@import "_mediaOverlay.styles";
%mediaOverlay {
backdrop-filter: blur(5px);
a.button {
@extend %button--small;
@extend %button--alt--secondary;
@include stack-spacing(0);
}
.ccm--consent-switch {
pointer-events: all;
user-select: none;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .5ch;
height: $mediaOverlay_padding;
position: absolute;
bottom: 0;
@include text-size(smallest);
color: $_minor-color;
a {
color: inherit;
&:hover {
text-decoration-color: $_action-color--hover;
}
}
}
}
$mediaOverlay_padding: var(--bp-large) !default;
$mediaOverlay_max-width: $_page_max-width !default;
$mediaOverlay_background: black !default;
$mediaOverlay-header_background: rgba(white, .2) !default;
$mediaOverlay-close_stroke-width: 2 !default;
$mediaOverlay_icons: (
close: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="{{color}}" stroke-width="#{$mediaOverlay-close_stroke-width}" stroke-linecap="round" stroke-linejoin="round"><path d="M18.75 5.25L5.25 18.75"/><path d="M18.75 18.75L5.25 5.25"/></svg>',
zoom-in: '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="{{color}}" stroke-width="#{$mediaOverlay-close_stroke-width}" stroke-linecap="round" stroke-linejoin="round"><path d="M15 4.5H19.5V9"/><path d="M14.25 9.75L19.5 4.5"/><path d="M9 19.5H4.5V15"/><path d="M9.75 14.25L4.5 19.5"/><path d="M19.5 15V19.5H15"/><path d="M14.25 14.25L19.5 19.5"/><path d="M4.5 9V4.5H9"/><path d="M9.75 9.75L4.5 4.5"/></svg>',
) !default;
$mediaOverlay_zoom-in_styles: (
width: 32px,
height: 32px,
background: $_black center center no-repeat,
background-image: svg-url(map.get($mediaOverlay_icons, zoom-in), $white),
color: $white,
opacity: .66,
transition-duration: $_transition-duration,
transition-property: opacity,
) !default;
$mediaOverlay_zoom-in_states: (
default: $mediaOverlay_zoom-in_styles,
hover: (
opacity: 1,
),
focus: map.merge( $_focus_styles,
(
opacity: 1,
)
),
) !default;
$mediaOverlay_close_styles: (
width: 32px,
height: 32px,
background: transparent center center no-repeat,
background-image: svg-url(map.get($mediaOverlay_icons, close), $white),
color: $white,
border-radius: 100vmax,
transition-duration: $_transition-duration,
transition-property: (background-color, color),
) !default;
$mediaOverlay_close_states: (
default: $mediaOverlay_close_styles,
hover: (
background-color: $_action-background-color--hover,
color: $_action-foreground-color--hover,
transition-duration: $_transition-duration--in,
),
focus: $_focus_styles,
active: (
background-color: $_action-background-color--active,
color: $_action-foreground-color--active,
transition-duration: $_transition-duration--in,
),
) !default;
%mediaOverlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
@include z-index(mediaOverlay);
display: grid;
grid-template-rows: 1fr max-content;
grid-template-areas:
"content"
"header";
align-items: stretch;
--aspect-ratio: 16 / 9;
@include apply-theme(dark);
background: $mediaOverlay_background;
overflow: hidden;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 150ms, pointer-events 0s, visibility 0s;
&[data-visible="true"] {
opacity: 1;
visibility: visible;
pointer-events: all;
}
&-nav {
position: absolute;
top: 0;
left: 0;
z-index: -1;
width: 100%;
height: 100%;
}
&-close {
display: block;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
cursor: default !important;
outline: none;
&:not([disabled]):not([aria-disabled="true"]):focus-visible > span {
@include state-styles($mediaOverlay_close_states, focus);
}
> span {
cursor: pointer;
position: absolute;
top: calc(.5 * #{$mediaOverlay_padding});
right: calc(.5 * #{$mediaOverlay_padding});
transform: translate(50%, -50%);
display: block;
text-indent: 120%;
white-space: nowrap;
overflow: hidden;
@include styles($mediaOverlay_close_styles);
@include action-states($mediaOverlay_close_states);
$mask-image: get-style($mediaOverlay_close_styles, background-image);
@if $mask-image {
@supports (mask-image: url()) {
background-image: none;
&::after {
content: "";
position: absolute;
inset: 0;
background: currentcolor;
mask-image: $mask-image;
mask-repeat: no-repeat;
mask-position: center center;
}
}
}
}
}
&-header {
grid-area: header;
padding: var(--bp-small) var(--bp-large);
background: $mediaOverlay_header_background;
display: flex;
flex-wrap: wrap;
align-items: center;
column-gap: var(--gg);
row-gap: var(--sp-large);
position: relative;
z-index: 1;
}
&-description {
flex: 1;
min-width: 320px;
}
&-description:not(:has(~ .mediaOverlay-cta)) {
text-align: center;
}
&-caption {
padding-right: 1ch;
}
&-copyright {
@extend %figure-copyright;
}
&-cta {
.button {
@include stack-spacing(0);
}
}
&-content {
grid-area: content;
padding: $mediaOverlay_padding;
max-width: $mediaOverlay_max-width;
margin-left: auto;
margin-right: auto;
display: flex;
justify-content: center;
overflow: hidden;
pointer-events: none;
position: relative;
&[data-type="audio"],
&[data-type="video"],
&[data-type="iframe"] {
align-items: center;
width: 100%;
height: 100%;
}
> * {
@include stack-spacing(0);
display: flex;
justify-content: center;
}
}
&-content[data-type="audio"] &-figure,
&-content[data-type="video"] &-figure,
&-content[data-type="iframe"] &-figure {
width: auto;
height: 100%;
align-items: center;
}
&-content[data-type="audio"] &-figure,
&-content[data-type="video"] &-figure {
aspect-ratio: var(--aspect-ratio);
}
&-figure {
width: 100%;
.figure-caption {
display: none;
}
.figure-image {
@include stack-spacing(0);
width: auto;
max-width: 100%;
object-fit: contain;
object-position: center;
&[style*="background-image"] {
background-size: 0px;
position: relative;
&::after {
content: "";
position: absolute;
inset: 0;
background-image: inherit;
background-size: contain;
filter: blur(3px);
}
}
}
.audioPlayer,
.videoPlayer {
@include stack-spacing(0);
}
iframe {
width: auto;
height: 100%;
aspect-ratio: var(--aspect-ratio, 16 / 9);
}
.videoPlayer {
width: 100%;
}
.audioPlayer {
width: 100%;
&-audio:only-child {
margin: auto;
max-width: min(100%, 640px);
}
}
audio,
iframe,
video {
@include stack-spacing(0);
pointer-events: all;
}
}
&-content[data-type="audio"] &-figure {
pointer-events: all;
}
&-content[data-type="iframe"] &-figure {
width: 100%;
max-height: 100%;
overflow-y: auto;
pointer-events: all;
iframe {
max-width: 100%;
}
&.is-youtube {
//FIXME: Height of video can be taller than parent container
// ("object-fit" does not work with iframes).
width: 100%;
padding-bottom: 56.25%;
overflow-y: hidden;
position: relative;
iframe {
position: absolute;
display: block;
height: 100%;
width: 100%;
pointer-events: all;
}
}
}
// Blocker for images
&-blocker {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 100;
display: none;
width: 100%;
height: 100%;
pointer-events: all;
}
}
//** Actions **//
%mediaOverlay_zoom-in {
pointer-events: all !important;
cursor: zoom-in;
position: relative;
z-index: 2;
&:focus {
outline: none;
}
@include action-states($mediaOverlay_zoom-in_states, $descendant: "::after");
&::after {
content: "";
display: block;
position: absolute;
right: var(--bp-small);
bottom: var(--bp-small);
z-index: 3;
@include styles($mediaOverlay_zoom-in_styles);
}
}
*[data-media-overlay][data-action="open"][data-active="true"] {
*[data-trigger] {
@extend %mediaOverlay_zoom-in;
}
.mediaGallery-preview img {
transform: none !important;
}
}
.mediaOverlay {
@extend %mediaOverlay;
&-nav {
@extend %mediaOverlay-nav;
}
&-close {
@extend %mediaOverlay-close;
}
&-header {
@extend %mediaOverlay-header;
}
&-content {
@extend %mediaOverlay-content;
}
&-figure {
@extend %mediaOverlay-figure;
}
&-description {
@extend %mediaOverlay-description;
}
&-caption {
@extend %mediaOverlay-caption;
}
&-copyright {
@extend %mediaOverlay-copyright;
}
&-cta {
@extend %mediaOverlay-cta;
}
&-blocker {
@extend %mediaOverlay-blocker;
}
}
html[data-media-overlay-visible="true"] {
overflow: hidden;
.grid-page {
overflow: hidden;
}
@include only-on-desktop(){
padding-right: var(--scrollbar-width) !important;
.mainHeader,
.mainNav {
padding-right: var(--scrollbar-width) !important;
}
}
}