→ לכל המאגר

B52 · הירו שמתקפל לתפריט דביק

התנהגויות GSAP · ScrollTrigger · sticky הירו ופתיחהניווט ומבנהתמונות וגלריות ממתין

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

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

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

הדמו

גלול למטה, הדמו מגיע ↓

אתרים שמביאים פניות

מה קורה כאן

הירו לא נעלם ולא מוחלף בתפריט אחר. אותו אלמנט מתכווץ עד לגובה רצועה, והכותרת יוצאת ממנו תוך כדי. זה מה שגורם למעבר להרגיש כמו תנועה אחת ולא כמו שני מצבים.

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

גלול חזרה למעלה כדי לראות את זה נפתח בחזרה.

עוד מסלול גלילה. נסה גם חזרה למעלה ↑
הטריק הוא שאין שני אלמנטים: התפריט חי בתוך הירו מהרגע הראשון, והירו הוא זה שמתכווץ סביבו. `overflow:hidden` על המסכה הוא מה שגורם לתמונה להיחתך במקום להתכווץ, וזה מה שמייצר את תחושת הקיפול. **שתי הכרעות מבנה**: הדביקות היא `position:sticky` על מעטפת שעוטפת את כל תוכן העמוד, ולכן הרצועה נשארת למעלה עד הסוף בלי `position:fixed`; וכל מה שהמסכה מאבדת בגובה עובר למרווח משלים, אחרת העמוד היה מתקצר תוך כדי הגלילה והתנועה הייתה מקרטעת. ניסיון לעשות את זה עם `pin` נשבר: ScrollTrigger מניח טרנספורם על האלמנט המוצמד, וטרנספורם על אב שובר `position:fixed` של צאצא.

קוד להדבקה

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

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

CSS

/* מחלקות עזר של המאגר שהרכיב הזה משתמש בהן */
.ph{display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;border-radius:var(--r, 14px)}
.ph-b{background:linear-gradient(160deg,#5f3dc4,#9775fa)}

/* B52 · הירו שמתקפל לתפריט דביק */
.fu-scope{--fu-h:100vh}
.fu-mask{position:sticky;top:0;z-index:60;height:var(--fu-h);overflow:hidden;background:var(--ink, #16182b);
  border-radius:0 0 var(--fu-r,0px) var(--fu-r,0px)}
.fu-media{position:absolute;inset:0;border-radius:0;font-size:0}
.fu-inner{position:absolute;inset:0;display:grid;grid-template-rows:auto 1fr;padding:18px clamp(16px,3vw,34px)}
.fu-nav{display:flex;align-items:center;justify-content:space-between;gap:16px;color:#fff;height:56px}
.fu-logo{font-weight:800;font-size:19px}
.fu-links{display:flex;gap:20px;font-size:14px;opacity:.9}
.fu-links a{color:#fff;text-decoration:none}
.fu-title{align-self:center;color:#fff;margin:0;font-size:clamp(32px,7vw,90px);line-height:1.05;max-width:14ch}
/* המרווח המשלים: מה שהמסכה מאבדת בגובה הוא מקבל, ולכן העמוד לא קופץ תוך כדי הקיפול */
.fu-gap{height:calc(100vh - var(--fu-h))}
/* הרצועה המקופלת נשארת דבוקה מעל התוכן. בלי ריפוד עליון בגובה שלה הכותרת הראשונה
   נכנסת מתחתיה והטקסט נראה כאילו הוא חופף להירו. --fu-bar נמדד מהתפריט עצמו ב-JS. */
.fu-body{padding:calc(var(--fu-bar,92px) + clamp(30px,6vw,80px)) var(--gutter, clamp(24px, 5vw, 96px)) 20vh;
  max-width:min(760px,92vw);margin-inline:auto}
.fu-body h3{font-size:clamp(22px,3vw,36px);margin:0 0 12px}
.fu-body p{color:var(--muted, #6a6d85);font-size:17px;line-height:1.8;margin:0 0 22px}
@media(max-width:700px){.fu-links{display:none}}

HTML

<div class="fu-scope">
  <div class="fu-mask">
    <div class="ph fu-media ph-b"></div>
    <div class="fu-inner">
      <div class="fu-nav"><span class="fu-logo">סטודיו</span>
        <nav class="fu-links"><a href="#">עבודות</a><a href="#">שירותים</a><a href="#">אודות</a><a href="#">צור קשר</a></nav>
      </div>
      <h1 class="fu-title">אתרים שמביאים פניות</h1>
    </div>
  </div>
  <div class="fu-gap"></div>
  <div class="fu-body">
    <h3>מה קורה כאן</h3>
    <p>הירו לא נעלם ולא מוחלף בתפריט אחר. אותו אלמנט מתכווץ עד לגובה רצועה, והכותרת יוצאת ממנו תוך כדי. זה מה שגורם למעבר להרגיש כמו תנועה אחת ולא כמו שני מצבים.</p>
    <p>הגובה הסופי נמדד מהתפריט עצמו ולא מוזן כמספר קבוע, ולכן זה מחזיק גם כשמשנים לוגו, פונט או ריפוד.</p>
    <p>גלול חזרה למעלה כדי לראות את זה נפתח בחזרה.</p>
  </div>
</div>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const scope=document.querySelector(".fu-scope"),nav=scope.querySelector(".fu-nav"),title=scope.querySelector(".fu-title");
  let tl;
  function build(){
    if(tl){tl.scrollTrigger&&tl.scrollTrigger.kill();tl.kill();gsap.set([scope,title],{clearProps:"all"});}
    // הגובה הסופי נמדד מהתפריט האמיתי, כדי שלא יהיה מספר קסם בקוד
    const navH=nav.offsetHeight+36;
    scope.style.setProperty("--fu-bar",navH+"px");   // גם ה-CSS צריך את המידה הזאת
    gsap.set(scope,{"--fu-h":"100vh","--fu-r":"0px"});
    tl=gsap.timeline({scrollTrigger:{trigger:scope,start:"top top",end:"+=100%",scrub:.8}});
    tl.to(scope,{"--fu-h":navH+"px","--fu-r":"22px",ease:"none"},0)
      .to(title,{opacity:0,y:-30,ease:"none"},0);
  }
  build();
  let t;addEventListener("resize",()=>{clearTimeout(t);t=setTimeout(()=>{build();ScrollTrigger.refresh();},200);});
})();