→ לכל המאגר

G91 · טקסט שנוסע במנהרה לעומק

GSAP GSAP · ScrollTrigger · 3D טקסט וכותרותהירו ופתיחה ממתין

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

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

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

הדמו

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

קוד להדבקה

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

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

CSS

/* G91 · טקסט שנוסע במנהרה לעומק */
.tn{position:relative;height:320vh}
.tn-pin{position:sticky;top:0;height:100vh;overflow:hidden;perspective:900px;background:var(--ink, #16182b);color:var(--bg, #f7f7fa);display:grid;place-items:center}
.tn-space{position:absolute;inset:0;transform-style:preserve-3d;display:grid;place-items:center}
.tn-line{position:absolute;font-size:clamp(28px,5vw,72px);font-weight:800;white-space:nowrap;will-change:transform,opacity;text-align:center;padding-inline:20px}
.tn-line small{display:block;font-size:.4em;font-weight:500;opacity:.7;margin-top:.3em}
.tn-glow{position:absolute;inset:30%;border-radius:50%;background:radial-gradient(closest-side,color-mix(in srgb,var(--accent, #4a3aff) 45%,transparent),transparent);filter:blur(40px)}
@media(max-width:767px){.tn{height:260vh}.tn-pin{perspective:600px}}

HTML

<div class="tn">
  <div class="tn-pin"><div class="tn-glow"></div><div class="tn-space">
    <div class="tn-line">אנחנו לא בונים אתרים.<small>שורה ראשונה</small></div>
    <div class="tn-line">אנחנו בונים דרך להגיע אליכם.</div>
    <div class="tn-line">בלי רעש. בלי קישוטים.</div>
    <div class="tn-line">רק מה שמזיז לקוח לפעולה.</div>
    <div class="tn-line">וזה נמדד. כל יום.</div>
  </div></div>
</div>

JS

gsap.registerPlugin(ScrollTrigger);

(function(){
  const lines=gsap.utils.toArray(".tn-line"),STEP=matchMedia("(max-width:767px)").matches?700:1000;
  if(matchMedia("(prefers-reduced-motion: reduce)").matches){lines.forEach((l,i)=>gsap.set(l,{position:"static",marginBottom:20}));return;}
  lines.forEach((l,i)=>gsap.set(l,{z:-i*STEP,opacity:i===0?1:0}));
  const total=(lines.length-1)*STEP+STEP*.6;
  const tl=gsap.timeline({scrollTrigger:{trigger:".tn",start:"top top",end:"bottom bottom",scrub:.7}});
  tl.to(lines,{z:"+="+total,ease:"none",duration:1},0);
  // השקיפות יושבת באותו טיימליין ולא ב-onUpdate נפרד, כך שהיא נכונה גם בסקראב לאחור וגם בקפיצה.
  // לכל שורה: מופיעה כשהיא במרחק 1.2 צעדים, מלאה מ-0.4 צעד, ונעלמת כשהיא חולפת על פני המצלמה.
  const at=(i,z)=>Math.max(0,(z+i*STEP)/total);   // הזמן (0..1) שבו השורה i מגיעה לעומק z
  lines.forEach((l,i)=>{
    if(i>0)tl.fromTo(l,{opacity:0},{opacity:1,duration:Math.max(.001,at(i,-STEP*.4)-at(i,-STEP*1.2)),ease:"none"},at(i,-STEP*1.2));
    tl.to(l,{opacity:0,duration:Math.max(.001,at(i,300)-at(i,100)),ease:"none"},at(i,100));
  });
})();