→ לכל המאגר

B33 · גלריה עם לייטבוקס

התנהגויות JS · dialog תמונות וגלריותכרטיסים ממתין

גריד תמונות שנפתח למסך מלא בלחיצה, עם מעבר בין תמונות בחצים ובמקלדת, סגירה ב-Escape או בלחיצה על הרקע, וגלילה נעולה מאחור.

מתי משתמשים: מסעדות, קליניקות, נדל"ן, אולמות, סטודיו, חנויות. בכל אתר שיש בו תמונות שאנשים ירצו לראות גדול.

הדמו כאן עיצובי-ניטרלי בכוונה. כשהמהלך נכנס לפרויקט, מיובאת רק ההתנהגות: הצבעים, הרדיוסים, הפונטים והצללים יורשים את העיצוב של אותו פרויקט.

הדמו

לחץ על תמונה. במקלדת: חצים למעבר, Escape לסגירה.

שלושה דברים שהופכים לייטבוקס מצעצוע לרכיב אמיתי: נעילת גלילה שמחזירה את רוחב פס הגלילה כריפוד כדי שהעמוד לא יקפוץ, החזרת הפוקוס לתמונה שממנה נפתח אחרי הסגירה, וחצי מקלדת הפוכים בעברית. בפרודקשן מוסיפים loading="lazy" לתמונות הגריד ומחליפים לגרסה גדולה רק בפתיחה.

קוד להדבקה

הקוד כאן עומד בפני עצמו. כל טוקן עיצוב נושא ברירת מחדל, מחלקות העזר של המאגר מוטמעות בו, ותגי הסקריפט מסודרים לפי הסדר הנכון. פרויקט שמגדיר --accent או --line משלו יורש אותם אוטומטית. הרכיב הזה מסתמך על dir="rtl".

פתח קובץ עצמאי ↗
הצג את הקוד כאן

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.stage{padding:var(--sec, clamp(120px, 12vw, 260px)) var(--gutter, clamp(24px, 5vw, 96px));position:relative}
.stage.tight{padding-block:clamp(48px,5vw,90px)}
.stage.full{padding-inline:0}
.ph{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;border-radius:var(--r, 14px)}
.ph-a{background:linear-gradient(160deg,#3b5bdb,#748ffc)}
.ph-b{background:linear-gradient(160deg,#5f3dc4,#9775fa)}
.ph-c{background:linear-gradient(160deg,#0b7285,#3bc9db)}
.ph-d{background:linear-gradient(160deg,#2b8a3e,#69db7c)}
.ph-e{background:linear-gradient(160deg,#e8590c,#ffa94d)}
.ph-f{background:linear-gradient(160deg,#c92a2a,#ff8787)}
.ph-l{background:rgba(16,18,43,.72);color:#fff;padding:.18em .7em;border-radius:999px;line-height:1.3;font-size:max(.78em,12px)}
.center{text-align:center}

/* B33 · גלריה עם לייטבוקס */
.gl{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding-inline:var(--gutter, clamp(24px, 5vw, 96px))}
.gl button{border:0;padding:0;background:none;cursor:zoom-in;border-radius:10px;overflow:hidden;aspect-ratio:1;position:relative}
.gl .ph{position:absolute;inset:0;border-radius:0;font-size:22px;transition:transform .5s cubic-bezier(.2,.6,.2,1)}
.gl button:hover .ph{transform:scale(1.06)}
.lb{position:fixed;inset:0;z-index:80;background:rgba(10,10,16,.92);display:none;place-items:center;padding:clamp(16px,4vw,48px)}
.lb.on{display:grid}
.lb-stage{position:relative;width:min(1000px,92vw);aspect-ratio:3/2;border-radius:14px;overflow:hidden}
.lb-stage .ph{position:absolute;inset:0;border-radius:0;font-size:64px;opacity:0;transition:opacity .35s}
.lb-stage .ph.on{opacity:1}
.lb-close,.lb-arrow{position:absolute;z-index:2;width:48px;height:48px;border-radius:50%;border:0;cursor:pointer;
  background:rgba(255,255,255,.14);color:#fff;font-size:20px;display:grid;place-items:center;backdrop-filter:blur(8px);transition:background .25s}
.lb-close:hover,.lb-arrow:hover{background:rgba(255,255,255,.28)}
.lb-close{top:clamp(14px,3vw,28px);inset-inline-end:clamp(14px,3vw,28px)}
.lb-arrow{top:50%;transform:translateY(-50%)}
/* בעברית "הקודמת" יושבת בצד ההתחלה (ימין) וחצה מצביע ימינה, ו"הבאה" בצד הסיום
   (שמאל) וחצה מצביע שמאלה. הפוך מזה ושני החצים מצביעים אחד לשני. */
.lb-prev{inset-inline-start:clamp(10px,2vw,22px)}
.lb-next{inset-inline-end:clamp(10px,2vw,22px)}
.lb-count{position:absolute;bottom:clamp(14px,3vw,26px);inset-inline:0;text-align:center;color:#fff;font-size:13px;letter-spacing:.1em;z-index:2;direction:ltr}
@media(max-width:767px){.gl{grid-template-columns:repeat(2,1fr)}}

HTML

<div class="stage tight">
<div class="gl">
  <button aria-label="פתח תמונה 1"><span class="ph ph-a"><span class="ph-l">1</span></span></button>
  <button aria-label="פתח תמונה 2"><span class="ph ph-b"><span class="ph-l">2</span></span></button>
  <button aria-label="פתח תמונה 3"><span class="ph ph-c"><span class="ph-l">3</span></span></button>
  <button aria-label="פתח תמונה 4"><span class="ph ph-d"><span class="ph-l">4</span></span></button>
  <button aria-label="פתח תמונה 5"><span class="ph ph-e"><span class="ph-l">5</span></span></button>
  <button aria-label="פתח תמונה 6"><span class="ph ph-f"><span class="ph-l">6</span></span></button>
  <button aria-label="פתח תמונה 7"><span class="ph ph-a"><span class="ph-l">7</span></span></button>
  <button aria-label="פתח תמונה 8"><span class="ph ph-c"><span class="ph-l">8</span></span></button>
</div>
<p class="center" style="color:var(--muted);font-size:14px;margin-top:18px">לחץ על תמונה. במקלדת: חצים למעבר, Escape לסגירה.</p>
</div>
<div class="lb" role="dialog" aria-modal="true" aria-label="תצוגת תמונה">
  <div class="lb-stage">
    <span class="ph ph-a"><span class="ph-l">1</span></span><span class="ph ph-b"><span class="ph-l">2</span></span><span class="ph ph-c"><span class="ph-l">3</span></span><span class="ph ph-d"><span class="ph-l">4</span></span>
    <span class="ph ph-e"><span class="ph-l">5</span></span><span class="ph ph-f"><span class="ph-l">6</span></span><span class="ph ph-a"><span class="ph-l">7</span></span><span class="ph ph-c"><span class="ph-l">8</span></span>
  </div>
  <button class="lb-close" aria-label="סגירה">✕</button>
  <button class="lb-arrow lb-prev" aria-label="הקודמת">→</button>
  <button class="lb-arrow lb-next" aria-label="הבאה">←</button>
  <span class="lb-count"></span>
</div>

JS

(function(){
  const thumbs=[...document.querySelectorAll(".gl button")];
  const lb=document.querySelector(".lb"),shots=[...lb.querySelectorAll(".lb-stage .ph")];
  const count=lb.querySelector(".lb-count"),closeBtn=lb.querySelector(".lb-close");
  let i=0,opener=null;
  function show(n){
    i=(n+shots.length)%shots.length;
    shots.forEach((s,k)=>s.classList.toggle("on",k===i));
    count.textContent=(i+1)+" / "+shots.length;
  }
  function open(n,from){
    opener=from;show(n);lb.classList.add("on");
    // נעילת גלילה בלי קפיצה: מחזירים את רוחב פס הגלילה כריפוד
    document.body.style.paddingInlineEnd=(innerWidth-document.documentElement.clientWidth)+"px";
    document.body.style.overflow="hidden";
    closeBtn.focus();
  }
  function close(){
    lb.classList.remove("on");
    document.body.style.overflow="";document.body.style.paddingInlineEnd="";
    if(opener)opener.focus();
  }
  thumbs.forEach((b,n)=>b.addEventListener("click",()=>open(n,b)));
  closeBtn.addEventListener("click",close);
  lb.querySelector(".lb-prev").addEventListener("click",()=>show(i-1));
  lb.querySelector(".lb-next").addEventListener("click",()=>show(i+1));
  lb.addEventListener("click",e=>{if(e.target===lb)close();});
  addEventListener("keydown",e=>{
    if(!lb.classList.contains("on"))return;
    if(e.key==="Escape")close();
    // בעברית חץ שמאלה מתקדם לתמונה הבאה
    if(e.key==="ArrowLeft")show(i+1);
    if(e.key==="ArrowRight")show(i-1);
  });
})();