<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="{&quot;Current time&quot;:&quot;Current time&quot;,&quot;Disable captions&quot;:&quot;Disable captions&quot;,&quot;Enable captions&quot;:&quot;Enable captions&quot;,&quot;Enter Fullscreen&quot;:&quot;Enter Fullscreen&quot;,&quot;Enter PiP&quot;:&quot;Enter PiP&quot;,&quot;Exit Fullscreen&quot;:&quot;Exit Fullscreen&quot;,&quot;Exit PiP&quot;:&quot;Exit PiP&quot;,&quot;Go back to previous menu&quot;:&quot;Go back to previous menu&quot;,&quot;Ad&quot;:&quot;Ad&quot;,&quot;AirPlay&quot;:&quot;AirPlay&quot;,&quot;All&quot;:&quot;All&quot;,&quot;Audio&quot;:&quot;Audio&quot;,&quot;Auto&quot;:&quot;Auto&quot;,&quot;Buffered&quot;:&quot;Buffered&quot;,&quot;Captions&quot;:&quot;Captions&quot;,&quot;Default&quot;:&quot;Default&quot;,&quot;Disabled&quot;:&quot;Disabled&quot;,&quot;Download&quot;:&quot;Download&quot;,&quot;Duration&quot;:&quot;Duration&quot;,&quot;Enabled&quot;:&quot;Enabled&quot;,&quot;End&quot;:&quot;End&quot;,&quot;Forward&quot;:&quot;Forward&quot;,&quot;LIVE&quot;:&quot;LIVE&quot;,&quot;Loop&quot;:&quot;Loop&quot;,&quot;Mute&quot;:&quot;Mute&quot;,&quot;Normal&quot;:&quot;Normal&quot;,&quot;Pause&quot;:&quot;Pause&quot;,&quot;Play&quot;:&quot;Play&quot;,&quot;Played&quot;:&quot;Played&quot;,&quot;Quality&quot;:&quot;Quality&quot;,&quot;Reset&quot;:&quot;Reset&quot;,&quot;Restart&quot;:&quot;Restart&quot;,&quot;Rewind&quot;:&quot;Rewind&quot;,&quot;Seek&quot;:&quot;Seek&quot;,&quot;Settings&quot;:&quot;Settings&quot;,&quot;Speed&quot;:&quot;Speed&quot;,&quot;Start&quot;:&quot;Start&quot;,&quot;Unmute&quot;:&quot;Unmute&quot;,&quot;Volume&quot;:&quot;Volume&quot;}">
                    <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>&#65279;</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"
      }
    ]
  }
}
  • Content:
    @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;
    }
    
  • URL: /components/raw/media/media.scss
  • Filesystem Path: src/components/3-molecules/media/media.scss
  • Size: 3 KB
  • Content:
    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();
    });
    
  • URL: /components/raw/media/media.ts
  • Filesystem Path: src/components/3-molecules/media/media.ts
  • Size: 1.7 KB

No notes defined.