কম্পিউটার টিউটোরিয়াল

CSS এবং জাভাস্ক্রিপ্ট দিয়ে ট্যাবড ইমেজ গ্যালারি (Tabbed Image Gallery) কীভাবে তৈরি করবেন?

থাম্বনেইল ক্লিক করলে মডালে বড় ইমেজ দেখানো (Tabbed/Modal Image Gallery)

ছোট থাম্বনেইল ইমেজের গ্রিড, যেগুলো ক্লিক করলে একটি মডাল (Modal) খুলে বড় আকারে ইমেজ দেখায়। এটি লাইটবক্স (Lightbox) স্টাইল গ্যালারির সিম্পল ভার্সন।

পূর্ণ কোড উদাহরণ

<!DOCTYPE html>
<html lang="bn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ট্যাবড ইমেজ গ্যালারি</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 20px; background: #f5f5f5; }
    
    h1 { text-align: center; margin-bottom: 30px; color: #333; }
    
    /* থাম্বনেইল গ্রিড */
    .gallery {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
      gap: 16px;
      max-width: 900px;
      margin: 0 auto;
    }
    
    .thumb {
      width: 100%;
      aspect-ratio: 4/3;
      object-fit: cover;
      border-radius: 8px;
      cursor: zoom-in;
      transition: transform 0.2s, box-shadow 0.2s;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    .thumb:hover { 
      transform: translateY(-4px); 
      box-shadow: 0 8px 24px rgba(0,0,0,0.15); 
    }
    .thumb:focus { outline: 3px solid #4f46e5; outline-offset: 2px; }
    
    /* মডাল (লুকানো) */
    .modal {
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.9);
      backdrop-filter: blur(4px);
      z-index: 10000;
      display: flex;
      align-items: center;
      justify-content: center;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.3s ease, visibility 0.3s ease;
      padding: 20px;
    }
    .modal.open {
      opacity: 1;
      visibility: visible;
    }
    
    /* মডাল ইমেজ */
    .modal-img {
      max-width: 100%;
      max-height: 90vh;
      object-fit: contain;
      border-radius: 4px;
      box-shadow: 0 0 40px rgba(0,0,0,0.5);
      transform: scale(0.9);
      transition: transform 0.3s ease;
    }
    .modal.open .modal-img { transform: scale(1); }
    
    /* ক্যাপশন */
    .modal-caption {
      position: absolute;
      bottom: -50px;
      left: 50%;
      transform: translateX(-50%);
      color: white;
      font-size: 16px;
      text-align: center;
      max-width: 90%;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    
    /* ক্লোজ বাটন */
    .modal-close {
      position: absolute;
      top: 20px;
      right: 30px;
      width: 48px;
      height: 48px;
      background: rgba(255,255,255,0.1);
      border: none;
      border-radius: 50%;
      color: white;
      font-size: 28px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background 0.2s, transform 0.2s;
      line-height: 1;
    }
    .modal-close:hover { background: rgba(255,255,255,0.2); transform: rotate(90deg); }
    .modal-close:focus { outline: 3px solid #4f46e5; outline-offset: 3px; }
    
    /* নেভিগেশন অ্যারো (অপশনাল) */
    .modal-nav {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 56px;
      height: 56px;
      background: rgba(255,255,255,0.1);
      border: none;
      border-radius: 50%;
      color: white;
      font-size: 24px;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: background 0.2s;
      opacity: 0.7;
    }
    .modal-nav:hover { opacity: 1; background: rgba(255,255,255,0.2); }
    .modal-prev { left: 20px; }
    .modal-next { right: 20px; }
    @media (max-width: 600px) {
      .modal-nav { width: 44px; height: 44px; font-size: 20px; }
      .modal-prev { left: 10px; }
      .modal-next { right: 10px; }
    }
  </style>
</head>
<body>
  <h1>ট্যাবড ইমেজ গ্যালারি</h1>
  
  <div class="gallery" id="gallery">
    <img class="thumb" src="https://picsum.photos/id/1015/400/300.jpg" alt="পাহাড় এবং নদী" data-full="https://picsum.photos/id/1015/1200/900.jpg">
    <img class="thumb" src="https://picsum.photos/id/1016/400/300.jpg" alt="জঙ্গল পথ" data-full="https://picsum.photos/id/1016/1200/900.jpg">
    <img class="thumb" src="https://picsum.photos/id/1018/400/300.jpg" alt="সানসেট বীচ" data-full="https://picsum.photos/id/1018/1200/900.jpg">
    <img class="thumb" src="https://picsum.photos/id/1020/400/300.jpg" alt="শহরের স্কাইলাইন" data-full="https://picsum.photos/id/1020/1200/900.jpg">
    <img class="thumb" src="https://picsum.photos/id/1021/400/300.jpg" alt="বনের ভিতর" data-full="https://picsum.photos/id/1021/1200/900.jpg">
    <img class="thumb" src="https://picsum.photos/id/1022/400/300.jpg" alt="পাহাড়ি ঝরনা" data-full="https://picsum.photos/id/1022/1200/900.jpg">
  </div>
  
  <!-- মডাল -->
  <div class="modal" id="modal" role="dialog" aria-modal="true" aria-label="ইমেজ ভিউয়ার">
    <button class="modal-close" id="modalClose" aria-label="বন্ধ করুন">×</button>
    <button class="modal-nav modal-prev" id="modalPrev" aria-label="পূর্বের ছবি">‹</button>
    <img class="modal-img" id="modalImg" src="" alt="">
    <div class="modal-caption" id="modalCaption"></div>
    <button class="modal-nav modal-next" id="modalNext" aria-label="পরবর্তী ছবি">›</button>
  </div>
  
  <script>
    const gallery = document.getElementById('gallery');
    const modal = document.getElementById('modal');
    const modalImg = document.getElementById('modalImg');
    const modalCaption = document.getElementById('modalCaption');
    const closeBtn = document.getElementById('modalClose');
    const prevBtn = document.getElementById('modalPrev');
    const nextBtn = document.getElementById('modalNext');
    
    const thumbs = [...gallery.querySelectorAll('.thumb')];
    let currentIndex = 0;
    
    // মডাল খুলুন
    function openModal(index) {
      currentIndex = index;
      const thumb = thumbs[currentIndex];
      modalImg.src = thumb.dataset.full || thumb.src;
      modalImg.alt = thumb.alt;
      modalCaption.textContent = thumb.alt;
      modal.classList.add('open');
      document.body.style.overflow = 'hidden';
      updateNavButtons();
    }
    
    // মডাল বন্ধ
    function closeModal() {
      modal.classList.remove('open');
      document.body.style.overflow = '';
      // ছবি আনলোড করুন মেমরি বাঁচাতে (ঐচ্ছিক)
      // modalImg.src = '';
    }
    
    // নেভিগেশন আপডেট
    function updateNavButtons() {
      prevBtn.style.display = currentIndex === 0 ? 'none' : 'flex';
      nextBtn.style.display = currentIndex === thumbs.length - 1 ? 'none' : 'flex';
    }
    
    // পরবর্তী/পূর্বের ছবি
    function navigate(dir) {
      const newIndex = currentIndex + dir;
      if (newIndex >= 0 && newIndex < thumbs.length) {
        openModal(newIndex);
      }
    }
    
    // ইভেন্ট লিসেনার
    thumbs.forEach((thumb, index) => {
      thumb.addEventListener('click', () => openModal(index));
      thumb.addEventListener('keydown', (e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          openModal(index);
        }
      });
    });
    
    closeBtn.addEventListener('click', closeModal);
    prevBtn.addEventListener('click', () => navigate(-1));
    nextBtn.addEventListener('click', () => navigate(1));
    
    // ব্যাকড্রপ ক্লিক
    modal.addEventListener('click', (e) => {
      if (e.target === modal) closeModal();
    });
    
    // কীবোর্ড নেভিগেশন
    document.addEventListener('keydown', (e) => {
      if (!modal.classList.contains('open')) return;
      if (e.key === 'Escape') closeModal();
      if (e.key === 'ArrowLeft') navigate(-1);
      if (e.key === 'ArrowRight') navigate(1);
    });
    
    // টাচ സ്വাইপ (মোবাইল)
    let touchStartX = 0;
    modal.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; }, { passive: true });
    modal.addEventListener('touchend', (e) => {
      const touchEndX = e.changedTouches[0].clientX;
      const diff = touchStartX - touchEndX;
      if (Math.abs(diff) > 50) {
        navigate(diff > 0 ? 1 : -1);
      }
    });
  </script>
</body>
</html>

মূল ফিচারসমূহ

  • রেসপন্সিভ গ্রিড: `grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))` — স্ক্রিন সাইজ অনুযায়ী কলাম অটো-অ্যাডজাস্ট।
  • হাই-রেজ ইমেজ: থাম্বনেইলে `data-full` অ্যাট্রিবিউটে বড় ইমেজের URL — মডালে লোড হয়।
  • মডাল অ্যানিমেশন: ফেড-ইন + স্কেল অ্যানিমেশন।
  • কীবোর্ড নেভিগেশন: Arrow Left/Right, ESC, Enter/Space।
  • টাচ স্বাইপ: মোবাইলে বাম/ডান স্বাইপ করলে ছবি পরিবর্তন।
  • অ্যাক্সেসিবিলিটি: `role="dialog"`, `aria-modal`, `aria-label`, ফোকাস ম্যানেজমেন্ট।
  • প্রিভ/নেক্স্ট বাটন: প্রথম/শেষ ছবিতে অটোমেটিক লুকানো।

উন্নত করুন (প্রো টিপস)

ফিচারকীভাবে যোগ করবেন
লেজি লোডিংথাম্বনেইলে `loading="lazy"`, মডালে শুধু খোলার সময় লোড
প্রিলোড নেক্স্ট ইমেজ`new Image().src = thumbs[currentIndex+1]?.dataset.full`
ক্যাপশন আন্ডার ইমেজ`.modal-caption` কে `bottom: 10px` দিয়ে ইমেজের নিচে আনুন
ফুলস্ক্রিন API`modalImg.requestFullscreen()` ডাবল ক্লিকে
URL হ্যাশ রাউটিং`location.hash = 'img-' + index` দিয়ে ডায়রেক্ট লিংক শেয়ার
অনিমেশন লাইব্রেরিFramer Motion, GSAP ব্যবহার করে উন্নত অ্যানিমেশন

এই গ্যালারিটি ই-কমার্স, পোর্টফোলিও, ব্লগ — যেকোনো জায়গায় ব্যবহার করা যায়।

  1. সিএসএস এবং জাভাস্ক্রিপ্ট দিয়ে কোল্যাপসিবল সেকশন তৈরি করা

  2. সিএসএস এবং জাভাস্ক্রিপ্ট দিয়ে স্ন্যাকবার/টোস্ট তৈরি করা

  3. CSS এবং JavaScript দিয়ে অ্যাকর্ডিয়ন (Accordion) তৈরি করা

  4. CSS এবং JavaScript দিয়ে রেসপন্সিভ স্লাইডশো গ্যালারি তৈরি