<div id="gallery-74529-56938" class="gallery" role="figure" aria-labelledby="gallery-74529-56938-headline">
    <div class="gallery__headline" id="gallery-74529-56938-headline">

        <h1 class="headline" data-headline-size="6"><span class="headline__text">Gallery (4 images)</span></h1>
    </div>

    <div class="gallery__preview">
        <figure id="media-31108-14052" class="media" role="figure" aria-labelledby="media-31108-14052-caption">
            <div class="media__figure">
                <div class="media__image">
                    <picture class="image" style="background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAABaADAAQAAAABAAAABQAAAABcYj7LAAAAFUlEQVQIHWP8V8PJgASYkNggJql8AHi/AY1z9PnwAAAAAElFTkSuQmCC)">
                        <source srcset="https://bildermangel.de/1380x776/fe7c09/130f26.webp?text=+++M:+690x388@2x+++ 2x, https://bildermangel.de/690x388/fe7c09/130f26.webp?text=+++M:+690x388@1x+++ 1x" media="(min-width: 512px) and (max-width: 767px)">
                        <source srcset="https://bildermangel.de/1760x990/fe7c09/130f26.webp?text=+++L:+880x495@2x+++ 2x, https://bildermangel.de/880x495/fe7c09/130f26.webp?text=+++L:+880x495@1x+++ 1x" media="(min-width: 768px) and (max-width: 1023px)">
                        <source srcset="https://bildermangel.de/2048x1152/fe7c09/130f26.webp?text=+++XL:+1024x576@2x+++ 2x, https://bildermangel.de/1024x576/fe7c09/130f26.webp?text=+++XL:+1024x576@1x+++ 1x" media="(min-width: 1024px)">
                        <img class="image__img" src="https://bildermangel.de/470x264/fe7c09/130f26.webp?text=+++S:+470x264@1x+++" srcset="https://bildermangel.de/940x528/fe7c09/130f26.webp?text=+++S:+470x264@2x+++ 2x" width="470" height="264" alt="Das ist ein Platzhalter-Bild" loading="lazy" />
                    </picture>
                </div>

                <div class="media__button">

                    <button class="button u-overlay-link" type="button" data-gallery="gallery-74529-56938-gallery-template">
                        <span class="button__icon">
                            <svg class="icon icon--panorama" viewBox="0 0 200 200" aria-hidden="true">
                                <use xlink:href="/assets/icons/icons.dcaca1c147.svg#panorama"></use>
                            </svg> </span>
                        <span class="button__text u-underline">View gallery</span>
                    </button>
                </div>
            </div>

            <figcaption class="media__caption" id="media-31108-14052-caption">

                <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>
    </div>

    <template id="gallery-74529-56938-gallery-template">
        <div role="dialog" class="gallery__overlay" id="gallery-74529-56938-gallery" aria-label="Gallery">
            <div class="gallery__overlay-container" role="document">
                <div class="gallery__images">
                    <div class="gallery__image" tabindex="0" data-floating-root>
                        <picture class="image">
                            <img class="image__img" src="https://bildermangel.de/470x264/fe7c09/130f26" width="470" height="264" alt="Das ist ein Platzhalter-Bild" />
                        </picture>

                        <div class="gallery__copyright">
                            <div class="copyright">
                                <button class="copyright__button" 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">Quelle: example.com</div>
                            </div>
                        </div>

                        <div class="gallery__caption">Das ist die Bildbeschreibung</div>
                    </div>
                    <div class="gallery__image" tabindex="0" data-floating-root hidden>
                        <picture class="image">
                            <img class="image__img" src="https://bildermangel.de/264x470/fe7c09/130f26" width="264" height="470" alt="Das ist ein Platzhalter-Bild" />
                        </picture>

                        <div class="gallery__copyright">
                            <div class="copyright">
                                <button class="copyright__button" 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">Quelle: example.com</div>
                            </div>
                        </div>

                        <div class="gallery__caption">Das ist die Bildbeschreibung mit <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">einem Link</span></a></div>
                    </div>
                    <div class="gallery__image" tabindex="0" data-floating-root hidden>
                        <picture class="image">
                            <img class="image__img" src="https://bildermangel.de/470x264/fe7c09/130f26" width="470" height="264" alt="Das ist ein Platzhalter-Bild" />
                        </picture>

                        <div class="gallery__copyright">
                            <div class="copyright">
                                <button class="copyright__button" 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">Quelle: example.com</div>
                            </div>
                        </div>

                        <div class="gallery__caption">Das ist die Bildbeschreibung mit <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">einem Link</span></a></div>
                    </div>
                    <div class="gallery__image" tabindex="0" data-floating-root hidden>
                        <picture class="image">
                            <img class="image__img" src="https://bildermangel.de/264x470/fe7c09/130f26" width="264" height="470" alt="Das ist ein Platzhalter-Bild" />
                        </picture>

                        <div class="gallery__copyright">
                            <div class="copyright">
                                <button class="copyright__button" 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">Quelle: example.com</div>
                            </div>
                        </div>

                        <div class="gallery__caption">Das ist die Bildbeschreibung</div>
                    </div>
                </div>

                <div class="gallery__controls">
                    <div class="gallery__close">

                        <button class="button u-overlay-link" type="button" title="Close gallery" data-gallery-action="close">
                            <span class="button__icon">
                                <svg class="icon icon--xmark" viewBox="0 0 200 200" aria-hidden="true">
                                    <use xlink:href="/assets/icons/icons.dcaca1c147.svg#xmark"></use>
                                </svg> </span>
                        </button>
                    </div>

                    <div class="gallery__position">1/4</div>

                    <div class="gallery__navigation">

                        <button class="button" type="button" title="Previous image" data-gallery-action="prev">
                            <span class="button__icon">
                                <svg class="icon icon--arrow-left" viewBox="0 0 200 200" aria-hidden="true">
                                    <use xlink:href="/assets/icons/icons.dcaca1c147.svg#arrow-left"></use>
                                </svg> </span>
                        </button>

                        <button class="button" type="button" title="Next image" data-gallery-action="next">
                            <span class="button__icon">
                                <svg class="icon icon--arrow-right" viewBox="0 0 200 200" aria-hidden="true">
                                    <use xlink:href="/assets/icons/icons.dcaca1c147.svg#arrow-right"></use>
                                </svg> </span>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </template>
</div>
{% set id = id ?? html_id('gallery') -%}

<div {{ html_attributes({
  id: id,
  class: 'gallery',
  role: 'figure',
  'aria-labelledby': 'headline' | namespaceInputId(id),
}, attrs ?? {}) }}>
  <div class="gallery__headline" id="{{ 'headline' | namespaceInputId(id) }}">
    {% include '@headline' with {
      text: '{text} ({count, plural, one {One image} other {# images}})' | t('site', {
        text: headline,
        count: images | length,
      }),
      size: 7,
      level: level ?? 1,
    } only %}
  </div>

  <div class="gallery__preview">
    {% include '@media' with preview | merge({
      caption: false,
      link: {
        text: 'View gallery' | t('site'),
        icon: 'panorama',
        attrs: {
          'data-gallery': 'gallery-template' | namespaceInputId(id),
        },
      },
    }) only %}
  </div>

  <template id="{{ 'gallery-template' | namespaceInputId(id) }}">
    <div role="dialog" class="gallery__overlay" id="{{ 'gallery' | namespaceInputId(id) }}" aria-label="{{ 'Gallery' | t('site') }}">
      <div class="gallery__overlay-container" role="document">
        <div class="gallery__images">
          {% for image in images %}
            <div class="gallery__image" tabindex="0" data-floating-root{{ not loop.first ? ' hidden' }}>
              {% include '@image' with image | merge({
                lazy: false,
                placeholder: false,
              }) only %}

              {% if image.copyright|default %}
                <div class="gallery__copyright">
                  {% include '@copyright' with {
                    text: image.copyright,
                  } only %}
                </div>
              {% endif %}

              {% if image.caption|default %}
                <div class="gallery__caption">
                  {{- image.caption | componentize(inline_only=true) -}}
                </div>
              {% endif %}
            </div>
          {% endfor %}
        </div>

        <div class="gallery__controls">
          <div class="gallery__close">
            {% include '@button' with {
              icon: 'xmark',
              title: 'Close gallery' | t('site'),
              overlay: true,
              attrs: {
                'data-gallery-action': 'close',
              },
            } only %}
          </div>

          <div class="gallery__position">
            {{- "1/#{images|length}" -}}
          </div>

          <div class="gallery__navigation">
            {% include '@button' with {
              title: 'Previous image' | t('site'),
              icon: 'arrow-left',
              attrs: {
                'data-gallery-action': 'prev',
              },
            } only %}

            {% include '@button' with {
              title: 'Next image' | t('site'),
              icon: 'arrow-right',
              attrs: {
                'data-gallery-action': 'next',
              },
            } only %}
          </div>
        </div>
      </div>
    </div>
  </template>
</div>
{
  "preview": {
    "caption": "This is the caption <a href=\"https://example.com\">with a link</a>",
    "copyright": "Source: example.com",
    "image": {
      "src": "https://bildermangel.de/470x264/fe7c09/130f26.webp?text=+++S:+470x264@1x+++",
      "width": 470,
      "height": 264,
      "alt": "Das ist ein Platzhalter-Bild",
      "src2x": "https://bildermangel.de/940x528/fe7c09/130f26.webp?text=+++S:+470x264@2x+++",
      "placeholder": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAABaADAAQAAAABAAAABQAAAABcYj7LAAAAFUlEQVQIHWP8V8PJgASYkNggJql8AHi/AY1z9PnwAAAAAElFTkSuQmCC",
      "sources": [
        {
          "srcset": "https://bildermangel.de/690x388/fe7c09/130f26.webp?text=+++M:+690x388@1x+++",
          "srcset2x": "https://bildermangel.de/1380x776/fe7c09/130f26.webp?text=+++M:+690x388@2x+++",
          "media": "(min-width: 512px) and (max-width: 767px)"
        },
        {
          "srcset": "https://bildermangel.de/880x495/fe7c09/130f26.webp?text=+++L:+880x495@1x+++",
          "srcset2x": "https://bildermangel.de/1760x990/fe7c09/130f26.webp?text=+++L:+880x495@2x+++",
          "media": "(min-width: 768px) and (max-width: 1023px)"
        },
        {
          "srcset": "https://bildermangel.de/1024x576/fe7c09/130f26.webp?text=+++XL:+1024x576@1x+++",
          "srcset2x": "https://bildermangel.de/2048x1152/fe7c09/130f26.webp?text=+++XL:+1024x576@2x+++",
          "media": "(min-width: 1024px)"
        }
      ]
    }
  },
  "headline": "Gallery",
  "images": [
    {
      "src": "https://bildermangel.de/470x264/fe7c09/130f26",
      "width": 470,
      "height": 264,
      "alt": "Das ist ein Platzhalter-Bild",
      "caption": "Das ist die Bildbeschreibung",
      "copyright": "Quelle: example.com"
    },
    {
      "src": "https://bildermangel.de/264x470/fe7c09/130f26",
      "width": 264,
      "height": 470,
      "alt": "Das ist ein Platzhalter-Bild",
      "caption": "Das ist die Bildbeschreibung mit <a href=\"https://example.com\">einem Link</a>",
      "copyright": "Quelle: example.com"
    },
    {
      "src": "https://bildermangel.de/470x264/fe7c09/130f26",
      "width": 470,
      "height": 264,
      "alt": "Das ist ein Platzhalter-Bild",
      "caption": "Das ist die Bildbeschreibung mit <a href=\"https://example.com\">einem Link</a>",
      "copyright": "Quelle: example.com"
    },
    {
      "src": "https://bildermangel.de/264x470/fe7c09/130f26",
      "width": 264,
      "height": 470,
      "alt": "Das ist ein Platzhalter-Bild",
      "caption": "Das ist die Bildbeschreibung",
      "copyright": "Quelle: example.com"
    }
  ]
}
  • Content:
    @use 'layers';
    
    .gallery__headline {
      margin-block-end: 1.6rem;
    }
    
    .gallery__overlay {
      --_gallery-padding: clamp(2rem, 10vw, 3rem);
      --_gallery-image-padding-block: 2rem;
      --_gallery-close-button-size: 4rem;
      --_gallery-navigation-button-size: 4.4rem;
      --_gallery-copyright-size: 2.4rem;
      --icon-button-size: var(--_gallery-navigation-button-size);
    
      animation: opacity var(--duration-fast), background-blur var(--duration-x-long);
      backdrop-filter: blur(10px);
      background-color: color-mix(in srgb, var(--color-black), transparent 20%);
      color: var(--color-white);
      display: flex;
      flex-direction: column;
      inset: 0;
      overflow: hidden;
      padding-block-end: calc(var(--_gallery-padding) + var(--_gallery-navigation-button-size) + var(--_gallery-image-padding-block));
      padding-block-start: calc(var(--_gallery-padding) + var(--_gallery-close-button-size) + var(--_gallery-image-padding-block));
      padding-inline: var(--_gallery-padding);
      position: fixed;
      z-index: #{layers.position('overlay')};
    }
    
    .gallery__overlay-container {
      block-size: 100%;
      display: flex;
      flex-direction: column;
      flex-grow: 1;
      inline-size: 100%;
    }
    
    .gallery__images {
      display: flex;
      flex-grow: 1;
      inline-size: 100%;
      justify-content: center;
      position: relative;
      z-index: 1;
    }
    
    .gallery__image {
      animation-duration: var(--duration-x-long);
      animation-name: opacity;
      box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
      inline-size: 100%;
      margin-block: auto;
      position: relative;
    
      &:is([data-move-direction='left']) {
        animation-name: opacity, slide-from-left;
      }
    
      &:is([data-move-direction='right']) {
        animation-name: opacity, slide-from-right;
      }
    }
    
    .gallery__copyright {
      --copyright-size: var(--_gallery-copyright-size);
    
      inset-block-end: 1rem;
      inset-inline-end: 1rem;
      line-height: 0;
      position: absolute;
      z-index: 2;
    }
    
    .gallery__caption {
      --_gallery-caption-padding-inline: var(--gallery-caption-padding-inline, 1rem);
      --selection-foreground-color: var(--color-black);
      --selection-background-color: var(--color-white);
      --link-color: currentColor;
      --link-underline-color: currentColor;
      --link-color--enter: currentColor;
      --link-underline-color--enter: transparent;
    
      backdrop-filter: blur(10px);
      background-color: color-mix(in srgb, var(--color-black), transparent 40%);
      border-radius: var(--border-radius-x-small);
      font-size: var(--font-size-caption);
      inset-block-end: 1rem;
      inset-inline-start: 1rem;
      line-height: var(--line-height-narrow);
      max-inline-size: calc(100% - var(--_gallery-caption-padding-inline) * 2 - var(--_gallery-copyright-size) - 1rem);
      padding-block: var(--gallery-caption-padding-block, 0.5rem);
      padding-inline: var(--_gallery-caption-padding-inline);
      position: absolute;
      z-index: 2;
    }
    
    .gallery__controls {
      inset: 0;
      pointer-events: none;
      position: absolute;
      user-select: none;
      z-index: 2;
    }
    
    .gallery__close {
      inset-block-start: var(--_gallery-padding);
      inset-inline-end: var(--_gallery-padding);
      line-height: 0;
      pointer-events: all;
      position: absolute;
    }
    
    .gallery__position {
      font-size: var(--font-size-default);
      font-weight: var(--font-weight-bold);
      inset-block-start: var(--_gallery-padding);
      inset-inline-start: var(--_gallery-padding);
      line-height: var(--_gallery-close-button-size);
      position: absolute;
    }
    
    .gallery__navigation {
      display: flex;
      gap: 1rem;
      inset-block-end: var(--_gallery-padding);
      inset-inline-start: 50%;
      pointer-events: all;
      position: absolute;
      transform: translateX(-50%);
    }
    
  • URL: /components/raw/gallery/gallery.scss
  • Filesystem Path: src/components/4-organisms/gallery/gallery.scss
  • Size: 3.6 KB
  • Content:
    import { on } from 'delegated-events';
    import { createFocusTrap } from 'focus-trap';
    import elementIndex from '../../../javascripts/utils/elementIndex';
    import abort from '../../../javascripts/utils/abort';
    import moveFocus from '../../../javascripts/utils/moveFocus';
    
    const fitImageInsideContainer = ($image: HTMLElement, $container: HTMLElement) => {
      const { width: containerWidth, height: containerHeight } = $container.getBoundingClientRect();
      const $img = $image instanceof HTMLImageElement ? $image : $image.querySelector('img');
      const { width, height } = $img ?? {};
    
      if (width && height) {
        const imageRatio = width / height;
        const fittedRatio = containerHeight / (containerWidth / imageRatio);
        const maxWidth = Math.min(1, fittedRatio) * 100;
    
        $image.style.maxWidth = `${maxWidth}%`;
      }
    };
    
    on('click', '[data-gallery]', (triggerEvent) => {
      const { currentTarget: $trigger } = triggerEvent;
      const { gallery: templateId } = $trigger.dataset;
    
      if (!templateId) {
        return;
      }
    
      const $gallery = $trigger.closest<HTMLElement>('.gallery') ?? abort();
      const $template = document.querySelector<HTMLTemplateElement>(`#${templateId}`) ?? abort();
      const $fragment = $template.content.cloneNode(true) as DocumentFragment;
      const $overlay = $fragment.querySelector<HTMLElement>('.gallery__overlay') ?? abort();
      const $container = $overlay.querySelector<HTMLElement>('.gallery__overlay-container') ?? abort();
      const $position = $overlay.querySelector('.gallery__position') ?? abort();
    
      const currentImage = () => $overlay.querySelector<HTMLElement>('.gallery__image:not([hidden])') ?? abort();
      const moveImage = (by: number) => {
        const $$image = $overlay.querySelectorAll<HTMLElement>('.gallery__image');
        const $currentImage = currentImage();
        const currentPosition = elementIndex($currentImage) + 1;
    
        let newPosition = currentPosition + by;
    
        if (newPosition > $$image.length) {
          newPosition %= $$image.length;
        }
    
        if (newPosition <= 0) {
          newPosition = $$image.length + newPosition;
        }
    
        const $nextImage = $$image[newPosition - 1] ?? abort();
        $nextImage.dataset.moveDirection = by > 0 ? 'left' : 'right';
        $currentImage.toggleAttribute('hidden', true);
        $nextImage.toggleAttribute('hidden', false);
        $position.textContent = `${newPosition}/${$$image.length}`;
    
        fitImageInsideContainer($nextImage, $container);
        moveFocus($nextImage);
      };
    
      const resizeOberserver = new ResizeObserver(() => {
        fitImageInsideContainer(currentImage(), $container);
      });
    
      const focusTrap = createFocusTrap($overlay, {
        clickOutsideDeactivates: false,
        escapeDeactivates: true,
        onActivate() {
          fitImageInsideContainer(currentImage(), $container);
          resizeOberserver.observe($overlay);
        },
        onDeactivate() {
          resizeOberserver.disconnect();
        },
        onPostDeactivate() {
          $overlay.remove();
        },
      });
    
      $overlay.addEventListener('keydown', (event) => {
        if (event.code === 'ArrowRight') {
          event.preventDefault();
          moveImage(1);
        } else if (event.code === 'ArrowLeft') {
          event.preventDefault();
          moveImage(-1);
        }
      });
    
      $overlay.addEventListener('click', (event) => {
        const { target: $target } = event;
    
        if ($target instanceof Element) {
          const { galleryAction: action } = $target?.closest<HTMLElement>('[data-gallery-action]')?.dataset ?? {};
    
          if (action === 'prev' || action === 'next') {
            event.preventDefault();
            moveImage(action === 'prev' ? -1 : 1);
          }
    
          if (action === 'close') {
            event.preventDefault();
            focusTrap.deactivate();
          }
        }
      });
    
      $gallery.appendChild($overlay);
      focusTrap.activate();
    });
    
  • URL: /components/raw/gallery/gallery.ts
  • Filesystem Path: src/components/4-organisms/gallery/gallery.ts
  • Size: 3.7 KB

No notes defined.