<svg style="display:none">
  <symbol id="arrow-left" viewBox="0 0 10 10">
    <path fill="currentColor" d="m9 4h-4v-2l-4 3 4 3v-2h4z"/>
  </symbol>
  <symbol id="arrow-right" viewBox="0 0 10 10">
    <path fill="currentColor" d="m1 4h4v-2l4 3-4 3v-2h-4z"/>
  </symbol>
</svg>
<h1>Photomontages by Hanna Höch</h1>
<div class="gallery">
  <div role="region" tabindex="0" aria-describedby="instructions" role="region" aria-label="gallery">
    <ul>
      <li>
        <figure>
          <img data-src="https://i.pinimg.com/originals/fe/61/b8/fe61b828c9490313681cda5257bae997.jpg" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' /%3E" alt="two male figures in bathing costumes stand behind a wall on a cryptically doodled background">
          <noscript>
            <img src="https://i.pinimg.com/originals/fe/61/b8/fe61b828c9490313681cda5257bae997.jpg" alt="two male figures in bathing costumes stand behind a wall on a cryptically doodled background">
          </noscript>
          <figcaption>Heads of State</figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <img data-src="http://www.lypophrenia.com/wp-content/uploads/2011/01/Hannah-H%C3%B6ch_Astronomy-and-Movement-Dada-250%C3%97190-mm-drawing-and-collage-1922.jpg" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' /%3E" alt="stark photomontage with jagged edges and red, yellow, and blue shades">
          <noscript>
            <img src="http://www.lypophrenia.com/wp-content/uploads/2011/01/Hannah-H%C3%B6ch_Astronomy-and-Movement-Dada-250%C3%97190-mm-drawing-and-collage-1922.jpg" alt="stark photomontage with jagged edges and red, yellow, and blue shades">
          </noscript>
          <figcaption>Astronomy and Movement Dada</figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <img data-src="http://www.lypophrenia.com/wp-content/uploads/2011/01/Hannah-H%C3%B6ch_-Mother-watercolour-and-photograph-collage-on-grey-paper-41o%C3%9735o-mm-1925%E2%80%936.jpg" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' /%3E" alt="Strange portrait-like montage. The subject is rotund and wears a torturous looking mask.">
          <noscript>
            <img src="http://www.lypophrenia.com/wp-content/uploads/2011/01/Hannah-H%C3%B6ch_-Mother-watercolour-and-photograph-collage-on-grey-paper-41o%C3%9735o-mm-1925%E2%80%936.jpg" alt="stark photomontage with jagged edges and red, yellow, and blue shades">
          </noscript>
          <figcaption>Mother</figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <img data-src="https://i.pinimg.com/originals/53/d9/e2/53d9e2cf92b05d3f7ddd903e5c91538a.jpg" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' /%3E" alt="Reclining nude on a colourful bed, wearing a large, monster-like mask and spectacles">
          <noscript>
            <img src="https://i.pinimg.com/originals/53/d9/e2/53d9e2cf92b05d3f7ddd903e5c91538a.jpg" alt="stark photomontage with jagged edges and red, yellow, and blue shades">
          </noscript>
          <figcaption>Untitled</figcaption>
        </figure>
      </li>
      <li>
        <figure>
          <img data-src="https://d32dm0rphc51dk.cloudfront.net/WGpoh18Y3iHXoI9g7rHSBw/large.jpg" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' /%3E" alt="Photomontoge of female figures and automobile parts, featuing several instances of the BMW logo">
          <noscript>
            <img src="https://d32dm0rphc51dk.cloudfront.net/WGpoh18Y3iHXoI9g7rHSBw/large.jpg" alt="stark photomontage with jagged edges and red, yellow, and blue shades">
          </noscript>
          <figcaption>The Beautiful Girl</figcaption>
        </figure>
      </li>
    </ul>
  </div>
  <div id="instructions">
    <p id="touch">
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-left"></use></svg>
      swipe for more
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-right"/></svg>
    </p>
    <p id="hover">
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-left"></use></svg>
      scroll for more
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-right"/></svg>
    </p>
    <p id="focus">
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-left"></use></svg>
      use your arrow keys for more
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-right"/></svg>
    </p>
    <p id="hover-and-focus">
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-left"></use></svg>
      scroll or use arrow keys for more
      <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-right"/></svg>
    </p>
  </div>
</div>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: sans-serif;
  margin: 3rem auto;
  max-width: 40rem;
  padding: 1rem;
  color: #111;
}

h1 {
  text-align: center;
  margin-bottom: 1rem;
}

[aria-label="gallery"] {
  border: 2px solid;
  overflow-x: scroll;
}

[aria-label="gallery"]:focus, [aria-label="gallery controls"] button:focus {
  outline: 4px solid DodgerBlue;
  outline-offset: -6px;
}

[aria-label="gallery controls"] button:focus {
  outline-offset: -4px;
}

[aria-label="gallery"] ul {
  display: flex;
}


[aria-label="gallery"] li {
  list-style: none;
  flex: 0 0 100%;
  padding: 2rem;
  height: 60vh;
}

[aria-label="gallery"] figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

[aria-label="gallery"] figcaption {
  padding: 0.5rem;
  font-style: italic;
  text-align: center;
}

[aria-label="gallery"] img {
  max-height: calc(100% - 2rem);
  margin-top: 2rem;
  max-width: 100%;
}

#instructions {
  position: relative;
}

#instructions p {
  padding: 1rem;
  text-align: center;
  color: #fefefe;
  background-color: #111;
}

#focus, #hover, #hover-and-focus, #touch {
  display: none;
}

[aria-label="gallery"]:focus + #instructions #focus,
[aria-label="gallery"]:hover + #instructions #hover {
  display: block;
}

[aria-label="gallery"]:hover + #instructions #hover + #focus {
  display: none;
}

[aria-label="gallery"]:hover:focus + #instructions #hover, 
[aria-label="gallery"]:hover:focus + #instructions #focus {
  display: none;
}

[aria-label="gallery"]:hover:focus + #instructions #hover-and-focus {
  display: block;
}

#instructions svg {
  height: 1.5rem;
  width: 1.5rem;
  fill: #fff;
  vertical-align: -0.5rem;
}

.touch #instructions p {
  display: none !important;
}

.touch #instructions #touch {
  display: block !important;
}

.gallery {
  position: relative;
}

[aria-label="gallery controls"] li {
  list-style: none;
}

[aria-label="gallery controls"] button {
  position: absolute;
  top: 0;
  background: #111;
  color: #fff;
  border: 2px solid #111;
  border-radius: 0;
  width: 3rem;
  height: calc(60vh + 4px);
}

#previous {
  left: 0;
}

#next {
  right: 0;
}

button svg {
  width: 2rem;
  height: 2rem;
}
(function() {
  /* touch detection */
  window.addEventListener('touchstart', function touched() {
    document.body.classList.add('touch')
    window.removeEventListener('touchstart', touched, false)
  }, false)

  /* lazy loading and button controls */
  const gallery = document.querySelector('[aria-label="gallery"]')
  const slides = gallery.querySelectorAll('li')
  const instructions = document.getElementById('instructions')

  const observerSettings = {
    root: gallery,
    rootMargin: '-10px'
  }

  if ('IntersectionObserver' in window) {
    const callback = (slides, observer) => {
      Array.prototype.forEach.call(slides, function(entry) {
        entry.target.classList.remove('visible')
        if (!entry.intersectionRatio > 0) {
          return
        }
        let img = entry.target.querySelector('img')
        if (img.dataset.src)  {
          img.setAttribute('src', img.dataset.src)
          img.removeAttribute('data-src')
        }
        entry.target.classList.add('visible')
      })
    }

    const observer = new IntersectionObserver(callback, observerSettings)
    Array.prototype.forEach.call(slides, t => observer.observe(t))

    const controls = document.createElement('ul')
    controls.setAttribute('aria-label', 'gallery controls')
    controls.innerHTML = `
    <li>
      <button id="previous" aria-label="previous">
        <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-left"></use></svg>
      </button>
    </li>
    <li>
      <button id="next" aria-label="next">
        <svg aria-hidden="true" focusable="false"><use xlink:href="#arrow-right"/></svg>
      </button> 
    </li>
    `

    instructions.parentNode.insertBefore(controls, instructions.nextElementSibling)
    instructions.parentNode.style.padding = '0 3rem'

    function scrollIt (slideToShow) {
      let scrollPos = Array.prototype.indexOf.call(slides, slideToShow) * (gallery.scrollWidth / slides.length)
      gallery.scrollLeft = scrollPos
    }

    function showSlide (dir, slides) {
      let visible = document.querySelectorAll('[aria-label="gallery"] .visible')
      let i = dir === 'previous' ? 0 : 1

      if (visible.length > 1) {
        scrollIt(visible[i])
      } else {
        let newSlide = i === 0 ? visible[0].previousElementSibling : visible[0].nextElementSibling
        if (newSlide) {
          scrollIt(newSlide)
        }
      }
    }

    controls.addEventListener('click', function (e) {
      showSlide(e.target.closest('button').id, slides)
    })
    
  } else {
    Array.prototype.forEach.call(slides, function (s) {
      let img = s.querySelector('img')
      img.setAttribute('src', img.getAttribute('data-src'))
    })
  }
})()
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.