Skip to main content

New HTML Gallery (IN PROGESS) - Tabs for keyboard accessibility, but has two tabs it shouldnt. 

See it Here: https://izzysworld.specialdistrict.org/amplify-homepage-preview

How to use: 

add it to an HTML element. Add photos as normal images to another page not linked anywhere. Take the URL and replace the bolded URL below. 

HTML: 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Accessible Image Gallery with Keyboard Support</title>
  <style>
    /* Visually hidden text for screen readers */
    .visually-hidden {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      padding: 0;
      border: 0;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
    }
    .gallery-container * { box-sizing: border-box; margin: 0; padding: 0; }
    .gallery-container { background: #fff; padding: 20px; font-family: Arial, sans-serif; }
    .gallery-container .gallery { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }
    .gallery-container .gallery-item { position: relative; width: calc(25% - 10px); height: 250px; overflow: hidden; cursor: pointer; }
    .gallery-container .gallery-item img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s ease; }
    .gallery-container .gallery-item:hover img { transform: scale(1.1); }
    .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0, 0, 0, 0.8); }
    #modalContent { margin: 5% auto; text-align: center; }
    #modalContent img { max-width: 90%; max-height: 80%; display: block; margin: auto; outline: none; }
    .close-button, .nav-button { position: absolute; background: transparent; border: none; color: #fff; cursor: pointer; }
    .close-button { top: 20px; right: 30px; font-size: 30px; }
    .nav-button { top: 50%; transform: translateY(-50%); font-size: 40px; padding: 10px; }
    .nav-left { left: 20px; }
    .nav-right { right: 20px; }
    .close-button:focus, .nav-button:focus, #modalContent img:focus { outline: 2px solid #fff; }
  </style>
</head>
<body>
  <div class="gallery-container">
    <div class="gallery" id="gallery"></div>
  </div>
  <div id="modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
    <h2 id="modalTitle" class="visually-hidden">Image preview</h2>
    <button class="close-button" id="closeModal" aria-label="Close modal">&times;</button>
    <button class="nav-button nav-left" id="prevBtn" aria-label="Previous image">&#10094;</button>
    <button class="nav-button nav-right" id="nextBtn" aria-label="Next image">&#10095;</button>
    <div id="modalContent" tabindex="0"></div>
  </div>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
      let lastFocused = null;
      let images = [];
      let currentIndex = 0;
      const modal = document.getElementById("modal");
      const content = document.getElementById("modalContent");

      function trapFocus(e, first, last) {
        if (e.key === 'Tab') {
          if (e.shiftKey && document.activeElement === first) {
            e.preventDefault(); last.focus();
          } else if (!e.shiftKey && document.activeElement === last) {
            e.preventDefault(); first.focus();
          }
        }
      }

      function openModal(idx) {
        lastFocused = document.activeElement;
        currentIndex = idx;
        updateModal();
        modal.style.display = 'block';

        const focusable = [
          document.getElementById('closeModal'),
          document.getElementById('prevBtn'),
          document.getElementById('nextBtn'),
          content.querySelector('img')
        ].filter(Boolean);
        const first = focusable[0];
        const last = focusable[focusable.length - 1];

        first.focus();
        modal.addEventListener('keydown', e => trapFocus(e, first, last));
        modal.addEventListener('keydown', modalKeyHandler);
      }

      function modalKeyHandler(e) {
        if (e.key === 'Escape') closeModal();
        if (e.key === 'ArrowLeft') showPrev();
        if (e.key === 'ArrowRight') showNext();
      }

      function updateModal() {
        const item = images[currentIndex];
        content.innerHTML = `<img src="${item.src}" alt="${item.alt}" tabindex="0">`;
      }

      function closeModal() {
        modal.style.display = 'none';
        modal.removeEventListener('keydown', modalKeyHandler);
        lastFocused && lastFocused.focus();
      }

      function showPrev() {
        currentIndex = (currentIndex - 1 + images.length) % images.length;
        updateModal();
      }

      function showNext() {
        currentIndex = (currentIndex + 1) % images.length;
        updateModal();
      }

      document.getElementById('closeModal').addEventListener('click', closeModal);
      modal.addEventListener('click', e => { if (e.target === modal) closeModal(); });
      document.getElementById('prevBtn').addEventListener('click', showPrev);
      document.getElementById('nextBtn').addEventListener('click', showNext);

      function loadGallery() {
        fetch('https://izzysworld.specialdistrict.org/bucketlist-gallery-attempth')
          .then(r => r.text())
          .then(html => {
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            const figs = doc.querySelectorAll('figure');
            images = Array.from(figs).map(f => {
              const img = f.querySelector('img');
              return img ? { src: img.src, alt: img.alt || (f.querySelector('figcaption')?.innerText) || 'Image' } : null;
            }).filter(Boolean);

            const gallery = document.getElementById('gallery');
            gallery.innerHTML = '';
            images.forEach((imgData, i) => {
              const div = document.createElement('div');
              div.className = 'gallery-item';
              div.tabIndex = 0;
              const img = document.createElement('img');
              img.src = imgData.src;
              img.alt = imgData.alt;
              div.appendChild(img);
              div.addEventListener('click', () => openModal(i));
              div.addEventListener('keydown', e => { if (e.key === 'Enter') openModal(i); });
              gallery.appendChild(div);
            });
          })
          .catch(err => console.error('Gallery load error:', err));
      }

      loadGallery();
    });
  </script>
</body>
</html>