7 שלב בניית היכולות

Scroll-Driven Animations — אנימציות גלילה ב-CSS טהור

עד 2024 כל אנימציית גלילה באתר — fade-in של כרטיס כשהוא נכנס למסך, progress bar למעלה, parallax של תמונת hero — דרשה JavaScript. GSAP ScrollTrigger, Framer Motion, IntersectionObserver, ספריות של עשרות KB. עכשיו יש CSS native לרוב המקרים האלה. בפרק הזה תלמדו את ההבדל בין scroll() timeline (האנימציה צמודה לגלילת הדף) ל-view() timeline (האנימציה מתחילה כשאלמנט נכנס ל-viewport), תבנו 5 דפוסים מוכנים — reveal, parallax, progress bar, sticky nav, image zoom — ותדעו בדיוק מתי CSS מספיק ומתי JavaScript עדיין עדיף. הכל בקוד שתעתיקו לכל פרויקט.

מה יהיה לך בסוף הפרק הזה
מה תוכלו לעשות בסוף הפרק הזה
דרישות קדם
הפרויקט שלך

לאורך הקורס אתם בונים את היכולת להפוך אתר גנרי שAI בנה לאתר שנראה כמו עבודת סטודיו — ה-capstone בפרק 13. עד כה בניתם צבעים (ch3), טיפוגרפיה (ch4), layout עם Grid/Flexbox/8px system (ch5), ובפרק 6 הוספתם את השכבה החמישית: Modern CSS Superpowers — Container Queries, :has(), @layer, subgrid, color-mix, anchor positioning. עכשיו בפרק 7 אתם מוסיפים את השכבה השישית — תנועה. עד 2024 תנועה באתר הייתה סימן ל-"אתר מקצועי" כי היא דרשה JavaScript ומפתח שיודע GSAP. בפרק הזה אתם לומדים שהרף ירד: animation-timeline: view() נותן reveal animation בקוד שמקביל ל-IntersectionObserver של 20 שורות JS. בפרק הבא (8) נעבור ל-View Transitions API — מעברים בין עמודים שמרגישים כמו אפליקציה native, גם הם ב-CSS (ברובם) ללא ספריות. ch7 + ch8 = מה שלקח פעם ספרייה של 40KB, עכשיו בקוד native של 30 שורות. בקפסטון (ch13) תשלבו reveal בגלילה, progress bar, ו-View Transition לכל ניווט — סגנון שעד לא מזמן היה שמור לסטודיו עם budget של רב-פיתוח.

מילון מונחים — 12 מושגים מרכזיים בפרק
מונח (English)תרגוםהגדרה בשורה אחת
Scroll-Driven Animationsאנימציות מונעות-גלילהאנימציות ש-progress שלהן (0%→100%) קשור ישירות לגלילה של המשתמש — אין time axis עצמאי
scroll() timelineציר-זמן של גלילת-דףבונה timeline מגלילת ה-scroller הקרוב/root — האנימציה מתקדמת מ-0 ל-100 כמו שהדף נגלל מהתחלה לסוף
view() timelineציר-זמן של נראות-אלמנטבונה timeline מכניסת/יציאת האלמנט ל-viewport — האנימציה רצה כשהאלמנט מתקרב, חוצה, ויוצא מהמסך
animation-timelineמחבר-ציר-זמןה-CSS property שמחבר @keyframes ל-timeline של גלילה במקום ל-wall clock time
animation-rangeטווח-אנימציהמגדיר מתי ב-timeline של view() האנימציה מתחילה ומסתיימת (entry 0%, cover 50%, exit 100% וכו׳)
Parallax Effectאפקט פרלקסשכבות שזזות במהירויות שונות במהלך גלילה — יוצר תחושת עומק תלת-ממדית באתר דו-ממדי
Reveal Animationאנימציית חשיפהאלמנט שמופיע (fade, slide, scale) ברגע שהוא נכנס ל-viewport — הפטרן הנפוץ ביותר של scroll animation
Progress Indicatorמחוון התקדמותפס (בדרך כלל עליון) שמראה כמה גללתם מהמסמך — נפוץ ב-blogs ארוכים ו-articles
scroll-snapהצמדת גלילהמערכת שמצמידה גלילה לנקודות עצירה ("סלייד" מושלם) — לא דורש JavaScript
IntersectionObserver Alternativeתחליף ל-IntersectionObserverה-view() timeline ברוב המקרים מחליף את ה-JavaScript API הישן שהיה הסטנדרט ל-reveal-on-scroll
Scroll-Triggered Animationאנימציה מופעלת-גלילה (Chrome 145)אנימציה time-based שנפתחת ברגע שהאלמנט מגיע לנקודה מסוימת, אבל רצה בזמן שלה (לא צמודה לגלילה)
W3C Scroll Animations Specמפרט W3C לאנימציות גלילההמסמך הרשמי (Candidate Recommendation 2024) שמגדיר את scroll(), view(), animation-timeline, animation-range
מתחיל 8 דקות חינם מושג

7.1 המהפכה — למה זה פרק שלם ולא סעיף

תחשבו על הדברים הבאים: פס התקדמות בראש עמוד blog שמראה כמה קראתם. כרטיסי features שמופיעים עם fade-up כשגוללים אליהם. תמונת hero ש-parallax נעה לאט ברקע בעוד הטקסט מעליה נע מהר. Nav bar שמחליף מ-shadow שקוף ל-shadow מלא ברגע שגללתם 80 פיקסלים. כולם patterns שרואים ב-כל אתר פרימיום ב-2026. וכולם, עד 2024, דרשו JavaScript.

התשתית היסטורית:

התובנה הגדולה

לפני 2024, אם רציתם reveal animation פשוט כשכרטיס נכנס ל-viewport, הייתם צריכים: (1) ספרייה כמו AOS/ScrollReveal או שילוב של IntersectionObserver+CSS, (2) לטעון את הספרייה, (3) לאתחל אותה, (4) להוסיף classes/data-attributes, (5) להתמודד עם bundle size ו-JS performance. היום זה 4 שורות CSS. זה לא שיפור קטן — זו קפיצה דומה במגניטודה למעבר מ-jQuery ל-vanilla JS.

מה המשמעות בפועל

כש-AI בונה לכם אתר ב-2026, ברירת המחדל שלו עדיין תהיה JavaScript — כי רוב ה-training data שלו הוא מ-2020-2023. אם תבקשו "make the cards fade in on scroll", הוא יחזיר לכם IntersectionObserver של 25 שורות. אם תבקשו "use CSS animation-timeline: view() with animation-range: entry 0% cover 40% to fade cards in on scroll" — הוא יחזיר 8 שורות CSS שעובדות טוב יותר, מהירות יותר, ונשברות בחינניות בדפדפנים ישנים.

התפקיד שלכם בפרק הזה: ללמוד את השפה החדשה של scroll animation — כדי שתוכלו לבקש מ-AI native CSS במקום ספריות ישנות.

3 המשפחות של scroll animations

לפני שנכנסים לקוד, חשוב להבין שיש שלוש משפחות שונות של scroll animation, והחוסר הבחנה ביניהן הוא מקור מספר 1 לבלבול:

משפחהמה זהדוגמהפתרון 2026
Scroll-DRIVENה-progress של האנימציה צמוד לגלילה (0%=למעלה, 100%=למטה). גוללים בחזרה = האנימציה חוזרת אחורה.Progress bar, parallax, horizontal scroll galleryCSS animation-timeline: scroll() / view()
Scroll-TRIGGEREDהאנימציה מתחילה כשמגיעים לנקודה מסוימת, אבל רצה בזמן שלה. גוללים בחזרה = האנימציה כבר רצה, לא משנה."fade in" של כרטיס כשהוא נכנס ל-viewportChrome 145+ CSS native. דפדפנים אחרים עדיין JS.
Smooth Scrolling / Scroll-Enhancedשינוי האופי של הגלילה עצמה (inertia, lerp, easing).אתרי Awwwards עם גלילה "חלקה" שונה מה-defaultJavaScript — Lenis. אין CSS native.

רוב הפרק יעסוק ב-scroll-driven (הוותיק יותר, תמיכה רחבה). נגע ב-scroll-triggered בסעיף 7.11 (חדש ב-Chrome 145, מרץ 2026). smooth scrolling — נסכם שעדיין צריך ספרייה.

עשו עכשיו 3 דקות

פתחו stripe.com. גללו לאט למטה. ספרו את האנימציות שרואים: כרטיסים שמופיעים, תמונות שזזות יחסית לטקסט, progress indicator, sticky nav שמשנה צבע. רשמו 5 אנימציות שזיהיתם. כעת פתחו DevTools → Network → צרפו filter JS → גללו שוב. האם הטעינה של JS חדש כשאתם גוללים? כנראה שלא — Stripe ב-2026 משתמש ב-scroll-driven CSS לרבים מהאפקטים האלה.

עשו עכשיו 2 דקות

פתחו scroll-driven-animations.style. זה הסנדבוקס הרשמי של Bramus Van Damme מ-Chrome DevRel — 40+ דמואים אינטראקטיביים. לחצו על "progress bar" בצד הימני. גללו למטה — ראו את הפס מתמלא ב-CSS טהור. לחצו "view source" — מינימום קוד. שמרו את האתר הזה — תחזרו אליו במהלך הפרק.

מתחיל 10 דקות חינם מעשי

scroll() timeline — אנימציה צמודה לגלילת הדף

scroll() הוא הסוג הראשון של timeline. המשמעות: ה-progress של האנימציה (0% → 100%) קשור ישירות לגלילה של ה-scroll container (בדרך כלל כל הדף). 0% = המשתמש בראש הדף, 100% = המשתמש בתחתית הדף.

זה בדיוק מה שצריך לשני use-cases קלאסיים: progress bar של הדף ו-parallax hero. נראה את שניהם.

Progress Bar — 12 שורות CSS

הפטרן הנפוץ ביותר. פס צבעוני בראש הדף שגדל מ-0% ל-100% ככל שגוללים. הנה הקוד המלא:

/* HTML: <div class="progress-bar"></div> בתוך body */

.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: var(--color-accent, #6366f1);
  transform-origin: left center;
  transform: scaleX(0);
  animation: grow-progress linear;
  animation-timeline: scroll(root block);
}

@keyframes grow-progress {
  to { transform: scaleX(1); }
}

זהו. 12 שורות CSS ויש לכם progress bar מקצועי. בואו נפרק איך זה עובד:

  1. animation: grow-progress linear — אנימציה רגילה, עם linear timing (כי הגלילה היא linear — אנחנו לא רוצים easing על התקדמות). שימו לב: אין duration כי ה-duration מגיע מהגלילה, לא מהשעון.
  2. animation-timeline: scroll(root block) — זו השורה הקריטית. אנחנו מחליפים את ה-"wall clock time" ב-timeline של גלילת root (ה-<html> root) על ציר ה-block (אנכי במסמכי LTR/RTL, horizontal ב-writing-mode אנכי).
  3. transform: scaleX(0)scaleX(1) — אנחנו משנים scale אופקי. עם transform-origin: left center זה גדל משמאל לימין. ב-RTL, שנו ל-right center או השתמשו ב-transform-origin: start center (חדש, logical).
  4. @keyframes רגיל — ה-@keyframes זהה לחלוטין לאנימציות רגילות. השינוי היחיד הוא ה-timeline שמחליף את ה-duration.

Anatomy של scroll(root block)

הפונקציה scroll() מקבלת שני ארגומנטים אופציונליים:

ארגומנטמה עושהערכים
Scrollerאיזה scroll container לעקוב אחריוnearest (default) / root / self
Axisציר הגלילהblock (default, אנכי) / inline (אופקי) / x / y

ה-3 סוגי scroller הם:

טעות נפוצה

אנשים כותבים animation-timeline: scroll() (בלי ארגומנטים) ומצפים שזה יעקוב אחרי ה-<body>. לא נכון — ה-default הוא nearest, וזה מוצא את הקונטיינר הקרוב. אם הקונטיינר הקרוב הוא ה-<body> (רוב המקרים) זה כן יעבוד, אבל ברגע שתעטפו את ה-progress bar ב-div עם overflow: hidden הכל נשבר. ל-progress bar של הדף — תמיד scroll(root block).

Use case שני: Parallax Hero

ה-use case הקלאסי השני של scroll() הוא parallax של hero section. תמונת רקע שנעה לאט יותר מהטקסט יוצרת תחושת עומק:

.hero-bg {
  position: absolute;
  inset: 0;
  background-image: url('hero.jpg');
  background-size: cover;
  animation: parallax-slow linear;
  animation-timeline: scroll(root block);
}

@keyframes parallax-slow {
  to { transform: translateY(40%); }
}

הרקע זז 40% מה-height של עצמו כשגוללים מראש הדף לסופו. הטקסט (שלא קיבל animation) נע במהירות רגילה של גלילה. ההבדל = parallax. נראה גרסה מלאה עם 3 שכבות בסעיף 7.7.

עשו עכשיו 4 דקות

פתחו CodePen חדש. הדביקו את קוד ה-progress bar (12 שורות CSS + div ב-HTML). הוסיפו 10-15 פסקאות lorem ipsum כדי שיהיה מה לגלול. גללו. עובד? עכשיו שנו את animation-timeline: scroll(root block) ל-scroll(nearest block). האם עדיין עובד? עטפו את כל התוכן ב-<div style="height: 300px; overflow: scroll">. עכשיו מה קורה עם nearest מול root? הבנתם את ההבדל.

בינוני 12 דקות חינם מעשי

view() timeline — אנימציה מופעלת-כניסה-ל-viewport

אם scroll() עוקב אחרי גלילת הדף כולו, אז view() עוקב אחרי כניסה ויציאה של אלמנט ספציפי מה-viewport. זה ה-timeline שרוב ה-use cases של reveal-on-scroll ישתמשו בו.

ה-progress של ה-timeline מוגדר כך:

Fade-Up על כניסה ל-viewport — 8 שורות CSS

.fade-up {
  animation: fade-up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}

מה שקורה: ברגע שהאלמנט מתחיל להיכנס ל-viewport (entry 0%), ה-opacity הוא 0 וה-translateY הוא 40px. כשהוא מכסה 40% מה-viewport (cover 40%), ה-opacity הוא 1 וה-translateY הוא 0. מעבר לזה — לא קורה כלום (בזכות animation-fill-mode: both).

כמה חשוב animation-range

השורה animation-range: entry 0% cover 40% היא ה-נקודה הקריטית של view(). בלי זה, ברירת המחדל היא cover 0% cover 100% — האנימציה תרוץ את כל הזמן שהאלמנט ב-viewport. זה נראה מוזר — reveal שקורה לאורך 100% מהמסך.

עם entry 0% cover 40%, האנימציה רצה רק ב-התחלה של הכניסה — מרגע שהאלמנט מתחיל להיכנס (entry 0%) עד שהוא מכסה 40% מה-viewport. אחרי זה הוא נשאר קבוע במצב "סיום".

נצלול עמוק ל-animation-range בסעיף הבא (7.4). קודם — הבדל קריטי בין view() ל-scroll().

scroll() vs view() — ההבדל המהותי

שאלהscroll()view()
מה הציר?גלילה של scroll container שלםנראות של אלמנט בודד ב-viewport
0% =ראש הדף/containerאלמנט בדיוק מתחת ל-viewport
100% =תחתית הדף/containerאלמנט בדיוק מעל ל-viewport
Use case קלאסיProgress bar, parallax heroFade-in reveal, image zoom
האנימציה חוזרת אחורה?כן — גוללים למעלה, חוזר אחורהכן — האלמנט יוצא, האנימציה חוזרת
כמה אלמנטים?אחד (הדף כולו)רבים — אנימציה לכל אלמנט בנפרד
כלל האצבע

אם האנימציה צריכה להיות אחת ולקרות פעם אחת בלבד לכל הדף — scroll(). אם האנימציה צריכה להיות לכל אלמנט בנפרד, וכל אלמנט מגיב לעצמו — view(). Progress bar = אחד → scroll(). Fade-in לכל כרטיס בגלריה של 20 כרטיסים = 20 פעמים → view().

עשו עכשיו 5 דקות

ב-CodePen שלכם, הוסיפו 6 כרטיסים (div.card) עם רווח גדול ביניהם. הוסיפו את ה-CSS של .fade-up (8 שורות). תנו לכל כרטיס את class fade-up. גללו. עובד! כעת שנו את animation-range ל-entry 0% cover 10% — מה קרה? (תשובה: האנימציה מתבצעת מהר יותר, נגמרת כשהאלמנט מכסה רק 10%). שנו ל-cover 0% cover 100%. מה קורה עכשיו? (תשובה: האנימציה רצה כל זמן שהאלמנט במסך — גם בגלילה למעלה).

בינוני 10 דקות חינם מושג

animation-range — 6 הטווחים שצריך להכיר

ה-animation-range הוא האזור שבו view() timeline פועל. ה-W3C הגדיר 6 שמות-טווח (named timeline ranges) שמקבילים ל-phase שבו האלמנט נמצא ביחס ל-viewport:

שם הטווחתחילהסוףמשמעות ויזואלית
entryהאלמנט מתחיל להיכנס לקו התחתון של ה-viewportהאלמנט כולו ב-viewport (top edge על ה-bottom, bottom edge על ה-bottom)שלב הכניסה — רק הקצה העליון של האלמנט מגיע
exitהאלמנט כולו ב-viewport, עומד לצאת מה-topהאלמנט כולו יצא מה-topשלב היציאה — מתחיל לעזוב מלמעלה
coverהאלמנט מתחיל להיכנס (זהה ל-entry 0%)האלמנט יצא כולו (זהה ל-exit 100%)כל הזמן שהאלמנט "מכסה" חלק מה-viewport
containהאלמנט כולו ב-viewportהאלמנט כולו עדיין ב-viewportפאזת הכלת-מלאה — כל האלמנט נראה
entry-crossingקצה עליון של האלמנט מגיע ל-bottomקצה עליון של האלמנט מגיע ל-top"הקצה העליון חוצה את המסך"
exit-crossingקצה תחתון של האלמנט מגיע ל-bottomקצה תחתון מגיע ל-top"הקצה התחתון חוצה את המסך"

דיאגרמה ויזואלית

  Viewport top ───────────────────────
                                       │
                                       │ exit    (item about to leave from top)
                 ┌───────────┐         │
                 │           │         │ contain (item fully in viewport)
                 │   item    │         │
                 └───────────┘         │
                                       │
                                       │ entry   (item entering from bottom)
  Viewport bot ───────────────────────
                 │
                 │         item below viewport
                 │         (animation not active yet)
                 ▼

מתמטיקת הפיקסלים

נגיד viewport height = 800px, element height = 400px. איך המספרים עובדים:

זה מסביר למה animation-range: entry 0% cover 50% = אנימציה ש"מואצת" בכניסה — היא מתחילה ברגע הכניסה (entry 0%) ונגמרת כשהאלמנט מגיע בערך לאמצע המסך (cover 50% = 600px של גלילה בדוגמה שלנו).

5 הטווחים הפופולריים ומתי להשתמש

מה אתם רוציםRange מומלץלמה
Reveal מהיר בכניסהentry 0% entry 100%האנימציה רצה רק במהלך הכניסה, 400px של גלילה
Reveal חלק ונדיבentry 0% cover 40%מתחיל בכניסה, נגמר כשהאלמנט באמצע. סטנדרט המקצועי
Parallax לכל זמן הנראותcover 0% cover 100%האנימציה רצה כל זמן שהאלמנט במסך
Pin + זום ברגע שמגיעcontain 0% contain 100%רק כשהאלמנט כולו במסך
"Exit" animation לפני יציאהexit 0% exit 100%האלמנט נעלם/מזעיר לפני שיוצא מלמעלה
עשו עכשיו 3 דקות

חזרו ל-scroll-driven-animations.style. לחצו על "view() timeline tool" בצד הימני. זהו visualizer אינטראקטיבי שמראה כל אחד מה-6 ranges בזמן אמת. גרירו את ה-sliders, ראו איך entry, cover, ו-contain שונים זה מזה. זה ה-2 דקות הכי שוות שתשקיעו בפרק.

בינוני 6 דקות חינם Framework

Framework: scroll() vs view() — מתי מה

הבלבול הכי נפוץ אצל מי שמתחיל ב-scroll-driven animations הוא מתי להשתמש ב-scroll() ומתי ב-view(). הנה framework שפותר את זה ב-3 שאלות.

Framework #1 — החלטת scroll() vs view()
  1. שאלה 1: כמה אלמנטים יש לכם עם האנימציה הזו?
    • אחד (progress bar, parallax background) → המשיכו לשאלה 2
    • רבים (fade-in לכל כרטיס בגלריה) → view() — כל אלמנט צריך את ה-viewport-awareness שלו
  2. שאלה 2: האנימציה קשורה ל-מיקום במסמך או ל-מיקום במסך?
    • מיקום במסמך ("כמה גללתי מכל הדף?") → scroll() — progress bar, parallax של hero
    • מיקום במסך ("מתי האלמנט נכנס/יצא?") → view()
  3. שאלה 3: האם האנימציה צריכה להיות קשורה לאלמנט אחר (לא ה-root scroller)?
    • כן (horizontal gallery בתוך section, modal scroll) → scroll(nearest) או scroll(self)
    • לא (הדף כולו) → scroll(root block)

Rule of thumb סופי: אם אתם מוסיפים את האנימציה ל-component שיופיע מספר פעמים → view(). אם אתם מוסיפים לאלמנט ייחודי (אחד ב-layout) → scroll().

5 Use Cases נפוצים ומה לבחור

Use CaseTimelineRange
Progress bar בראש הדףscroll(root block)(ללא — רץ מ-0 ל-100)
Hero parallaxscroll(root block)(ללא — רץ מ-0 ל-100)
Fade-in לכרטיסיםview()entry 0% cover 40%
Image zoom כשתמונה במרכזview()cover 0% cover 100%
Sticky nav שמחליף צבע ב-scrollscroll(root block)0 200px (pixel-based)
Horizontal gallery בתוך sectionscroll(self inline)(ללא)
Text שחושף מילה-מילהview() לכל spanentry 0% contain 50%
מתחיל 12 דקות חינם מעשי

Reveal Patterns — fade-up, slide-in, scale-in

הפטרן הנפוץ ביותר באתרים מודרניים. תוכן שמופיע עם תנועה עדינה כשהוא נכנס ל-viewport. לפני 2024 — ספריית AOS.js או ScrollReveal.js, כל אחת 20-40KB. היום — 8-12 שורות CSS לכל וריאציה.

וריאציה 1: Fade-Up (הקלאסית)

.reveal-fade-up {
  animation: reveal-fade-up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

@keyframes reveal-fade-up {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

שימושים: כרטיסי features, testimonials, blog cards, כל list of things. זו ברירת מחדל טובה ל-99% מהמקרים.

וריאציה 2: Slide-In-From-Right (למסמכי RTL: Slide-In-From-Left)

.reveal-slide-right {
  animation: reveal-slide-right linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 50%;
}

@keyframes reveal-slide-right {
  from {
    opacity: 0;
    transform: translateX(60px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

שימושים: alternating layouts (text-then-image, image-then-text), side panels, asymmetric reveals. ב-RTL — השתמשו ב-translateX(-60px) או logical translate(inline-start, 60px) (חדש).

וריאציה 3: Scale-In (דרמטי)

.reveal-scale {
  animation: reveal-scale linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 50%;
}

@keyframes reveal-scale {
  from {
    opacity: 0;
    transform: scale(0.85);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

שימושים: hero images שנחשפות, CTA cards גדולים, statistics sections. אל תשתמשו ב-scale-in לרשימת כרטיסים — זה נראה "מפוצץ".

Timing curves — הסוד של המראה המקצועי

linear עבור scroll-driven הוא נכון ברוב המקרים — הגלילה עצמה היא linear, והוספת easing מסנכרנת לא-טוב. אבל, ל-reveal עם animation-range קצר (entry 0% entry 100%), אפשר להשתמש ב-easing כדי לתת יותר "אופי":

Easingתחושהמתי להשתמש
linearישר, אחידDefault. Parallax, progress bar, רוב ה-reveal
ease-outמתחיל מהר, נרגעReveal מהיר שצריך להרגיש "מגיע" ונעצר
cubic-bezier(0.22, 1, 0.36, 1)"ease-out-quart" — מאוד רךScale-in דרמטי של hero images
cubic-bezier(0.34, 1.56, 0.64, 1)"back" — עובר מעט את היעד וחוזרEmojis, icons, CTA cards (overshoot קטן)

Staggered reveal — אפקט השלב-שלב

כשיש רשימת כרטיסים ורוצים שהם יופיעו זה אחרי זה ולא כולם ביחד, הדרך ב-scroll-driven היא להזיז את ה-animation-range לפי מיקום:

.card:nth-child(1) { animation-range: entry 0%  cover 40%; }
.card:nth-child(2) { animation-range: entry 10% cover 50%; }
.card:nth-child(3) { animation-range: entry 20% cover 60%; }

לחלופין — פשוט תנו לכל כרטיס animation-range שונה דרך custom property. זה יוצר "delay" שמבוסס-גלילה ולא על זמן.

מקרים מיוחדים: RTL, icons, ו-accessibility

שלושה תרחישים שדורשים טיפול מיוחד ברוב הפרויקטים הישראליים:

1. RTL reveal אנימציות: אם כתבתם translateX(-60px) לאתר בעברית, ב-LTR ה-slide יבוא מימין — אבל בעברית זה לא משרת את כיוון הקריאה. הפתרון המקצועי הוא להשתמש ב-logical properties:

/* RTL-aware slide — עובד גם בעברית וגם באנגלית */
@keyframes reveal-slide-inline-start {
  from {
    opacity: 0;
    translate: 60px 0;
  }
  to {
    opacity: 1;
    translate: 0;
  }
}

לחלופין, עם [dir="rtl"] override:

.reveal-slide-in { transform: translateX(-60px); }
[dir="rtl"] .reveal-slide-in { transform: translateX(60px); }

2. אנימציות על icons: SVG icons שמופיעים ב-scroll לרוב טובים עם scale-in עדין + rotate קטן. אל תשתמשו בתזוזה גדולה (translateY יותר מ-20px) על icons — זה נראה "מוחמץ" כשהגלילה מהירה.

.icon-reveal {
  animation: icon-pop linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
}

@keyframes icon-pop {
  from {
    opacity: 0;
    transform: scale(0.7) rotate(-8deg);
  }
  to {
    opacity: 1;
    transform: scale(1) rotate(0);
  }
}

3. prefers-reduced-motion — זה לא רק "תכבה הכל": בעלי motion sensitivity עדיין רוצים לדעת שמשהו השתנה. הפתרון המקצועי הוא לא לכבות לגמרי, אלא להחליף תנועה ב-opacity בלבד:

/* Default: full animation */
.reveal {
  animation: full-reveal linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    animation: opacity-only linear both;
  }
}

@keyframes full-reveal {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes opacity-only {
  from { opacity: 0; }
  to   { opacity: 1; }
}

זה מכבד את ההעדפה של המשתמש, אבל עדיין נותן signal שמשהו חדש מופיע — חשוב במיוחד לכרטיסים שיש להם הודעות חשובות או CTAs.

תרגיל 1 — Reveal Gallery 15 דקות
  1. פתחו CodePen חדש. בנו HTML: כותרת h1, אחריה 6 div.card (כל אחד עם תמונה מ-Unsplash או רקע צבעוני, כותרת, וטקסט קצר).
  2. תנו לכולם width של 600px ו-margin אוטומטי. הוסיפו height: 80vh לפני הכרטיסים כדי שתוכלו לגלול.
  3. הוסיפו את ה-CSS של .reveal-fade-up (8 שורות מהסעיף). תנו לכל הכרטיסים class reveal-fade-up. גללו — אמורים לראות את כולם מופיעים.
  4. שנו את 3 הכרטיסים הראשונים ל-reveal-slide-right, ואת האחרונים ל-reveal-scale. גללו. איזה מהשלושה נראה הכי "מקצועי"?
  5. עכשיו הוסיפו stagger: .card:nth-child(odd) עם animation-range: entry 0% cover 40%, ו-.card:nth-child(even) עם entry 10% cover 50%. גללו באיטיות — שמתם לב ל-stagger?
  6. Expected output: CodePen עם 6 כרטיסים עם 3 וריאציות reveal + stagger. שמרו את הלינק.
  7. Bonus: הוסיפו @media (prefers-reduced-motion: reduce) עם animation: none — נגן נגישות חובה.
בינוני 10 דקות חינם מעשי

Parallax ב-CSS טהור — שכבות במהירויות שונות

Parallax הוא האפקט שבו שכבות ברקע זזות במהירות שונה מהשכבות בקדמה, יוצר תחושת עומק. עד 2024 זה דרש JavaScript (GSAP + ScrollTrigger), או CSS background-attachment: fixed שהיה פגום על mobile. היום זה CSS טהור שעובד בכל מקום.

Parallax של 3 שכבות

HTML:

<section class="parallax-hero">
  <div class="layer layer-bg"></div>
  <div class="layer layer-mid"></div>
  <div class="layer layer-fg">
    <h1>Welcome to the Future</h1>
  </div>
</section>

CSS:

.parallax-hero {
  position: relative;
  height: 100vh;
  overflow: hidden;
}

.layer {
  position: absolute;
  inset: 0;
  will-change: transform;
}

.layer-bg {
  background: url('mountains.jpg') center/cover;
  animation: parallax-slow linear;
  animation-timeline: scroll(root block);
}

.layer-mid {
  background: url('clouds.png') center/cover;
  animation: parallax-med linear;
  animation-timeline: scroll(root block);
}

.layer-fg {
  animation: parallax-fast linear;
  animation-timeline: scroll(root block);
}

@keyframes parallax-slow { to { transform: translateY(20%); } }
@keyframes parallax-med  { to { transform: translateY(50%); } }
@keyframes parallax-fast { to { transform: translateY(-15%); } }

איך זה עובד

ההבדל במהירויות (20%/50%/-15%) יוצר את תחושת העומק. אל תשתמשו בערכים גדולים מ-±30% בלי לבדוק — יותר מדי תזוזה נראה זול ומשקשק.

טריק מקצועי

תנו לכל שכבה height גדול מ-100vh — למשל height: 130vh ו-top: -15vh. כשהם זזים 20-50%, הם לא חושפים את הקצוות. זה הטריק שהופך parallax מ"חובבני" ל"מקצועי".

Parallax על טקסט (וריאציה קלה)

תרחיש אחר — hero עם כותרת גדולה, וטקסט תת-כותרת זז לאט יותר כשגוללים:

.hero-title {
  animation: text-slow linear;
  animation-timeline: scroll(root block);
  animation-range: 0 100vh;
}

@keyframes text-slow {
  to { transform: translateY(-100px); opacity: 0.4; }
}

animation-range: 0 100vh אומר שהאנימציה רצה רק ב-100vh הראשונים של הגלילה — עד שה-hero נגמר. אחרי זה, ה-title כבר מחוץ למסך ולא משנה.

Parallax עם pinning — Hero שמחזיק מקום

טכניקה מתקדמת שרואים בהרבה אתרי agency: ה-hero נתקע למסך בזמן שהתוכן מתחתיו מתחלף, ורק אחרי שהאנימציה מסתיימת ה-hero משחרר ומתחילה הגלילה הרגילה. זה נקרא scroll-pinning.

ב-CSS זה שילוב של position: sticky עם view() timeline על האלמנט הבא:

.pinned-hero {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
}

.pinned-hero .content {
  animation: hero-reveal linear;
  animation-timeline: view();
  animation-range: contain 0% contain 100%;
}

@keyframes hero-reveal {
  0%   { transform: scale(1.3); opacity: 0; }
  50%  { transform: scale(1);   opacity: 1; }
  100% { transform: scale(0.9); opacity: 0.7; }
}

ה-position: sticky מחזיק את ה-hero במקום במסך, וה-view() על התוכן בפנים עוקב אחרי המיקום של ה-hero ב-viewport — כלומר, כל זמן שה-sticky פעיל, האנימציה רצה.

הערה חשובה: את ה-GSAP ScrollTrigger כולם מכירים בגלל ה-pinning. CSS native יכול לעשות את זה, אבל עם מגבלות — choreography מורכבת של 5 אלמנטים שכל אחד מהם נפרד עם timeline משלו, זה עדיין GSAP territory.

מתחיל 6 דקות חינם מעשי

Progress Indicator — פס התקדמות עליון

הפטרן הנפוץ ביותר ב-blogs ארוכים ו-articles. פס עליון שמראה אחוז התקדמות בקריאה. חזרנו לקוד מסעיף 7.2 אבל הפעם עם 3 וריאציות מקצועיות.

וריאציה בסיסית — פס חד-צבעי

.progress-bar {
  position: fixed;
  top: 0;
  inset-inline: 0;        /* logical — עובד RTL ו-LTR */
  height: 4px;
  background: var(--color-accent);
  transform-origin: start center;  /* logical */
  transform: scaleX(0);
  animation: progress-grow linear;
  animation-timeline: scroll(root block);
  z-index: 100;
}

@keyframes progress-grow {
  to { transform: scaleX(1); }
}

וריאציה 2 — Gradient

במקום צבע אחד, gradient שמתגלה הדרגתית:

.progress-bar {
  /* ... הכל מלמעלה ... */
  background: linear-gradient(
    to right,
    var(--color-primary),
    var(--color-accent)
  );
}

וריאציה 3 — מעגלית (ל-floating action)

במקום פס, מעגל שמתמלא — נראה כמו "X% read" badge:

.progress-circle {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: conic-gradient(
    var(--color-accent) 0%,
    var(--color-accent) var(--progress, 0%),
    transparent var(--progress, 0%),
    transparent 100%
  );
  animation: progress-fill linear;
  animation-timeline: scroll(root block);
}

@keyframes progress-fill {
  to { --progress: 100%; }
}

הערה: animating CSS custom properties דורש @property declaration כדי שיעבוד חלק — נלמד את זה ב-ch9 (Modern CSS advanced).

תרגיל 2 — Progress Bar אישי 12 דקות
  1. לקחו את ה-CodePen מתרגיל 1. הוסיפו את ה-HTML של progress bar בתחילת ה-<body>.
  2. הדביקו את הקוד של הוריאציה הבסיסית (13 שורות CSS). גללו. רואים את הפס נמלא?
  3. שנו ל-gradient (וריאציה 2). איך זה נראה מול הצבע החד? התאימו את הצבעים לפלטה שלכם מ-ch3.
  4. הוסיפו opacity: 0.9, box-shadow: 0 2px 8px rgba(0,0,0,0.1), ו-border-radius: 0 4px 4px 0. האם זה נראה יותר "פרימיום"?
  5. בדקו ב-mobile emulator של DevTools. האם הפס עדיין עובד? אם כן — ברכות, יש לכם progress bar production-ready של 15 שורות.
  6. Expected output: CodePen עם progress bar עובד + 3 וריאציות מוכנות.
בינוני 8 דקות חינם מעשי

פטרן שכיח: nav bar שקוף בהתחלה (על רקע hero), הופך לבן/כהה עם shadow כשגוללים 80px. עד 2024 זה היה תמיד JavaScript — window.scrollY event listener. היום CSS טהור:

.site-nav {
  position: fixed;
  top: 0;
  inset-inline: 0;
  padding: 1rem 2rem;
  background: transparent;
  backdrop-filter: blur(0);
  box-shadow: 0 0 0 rgba(0,0,0,0);
  animation: nav-scroll linear;
  animation-timeline: scroll(root block);
  animation-range: 0 150px;  /* הופך מלא תוך 150px של גלילה */
}

@keyframes nav-scroll {
  to {
    background: rgba(255,255,255,0.85);
    backdrop-filter: blur(12px);
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
  }
}

מה קורה כאן

טעות נפוצה

מפתחים מנסים לאנמט background-color מ-transparent ל-rgba(255,255,255,0.85) ומופתעים שזה לא עובד חלק. הסיבה: transparent הוא בעצם rgba(0,0,0,0) — שחור שקוף. האינטרפולציה עוברת דרך "שחור דהוי" שנראה מוזר. הפתרון: השתמשו ב-rgba(255,255,255,0) כ-from state במקום transparent.

בינוני 6 דקות חינם מעשי

Image Zoom on Scroll עם view()

אפקט יוקרתי מאוד ב-e-commerce premium: תמונה שגדלה קלות (1.0 → 1.1) כשהיא עוברת דרך ה-viewport. יוצר תחושת "חיים" לתמונה ב-luxury fashion ו-car brands.

.zoom-on-scroll {
  overflow: hidden;
}

.zoom-on-scroll img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: zoom-effect linear;
  animation-timeline: view();
  animation-range: cover 0% cover 100%;
}

@keyframes zoom-effect {
  from { transform: scale(1); }
  to   { transform: scale(1.1); }
}

למה cover 0% cover 100%?

הטווח הזה אומר: "כל זמן שהאלמנט מכסה חלק מה-viewport — מהרגע שמתחיל להיכנס עד שיוצא". זה 100% של זמן הנראות, והזום קורה בצורה רציפה לאורך כל הגלילה. אם היינו משתמשים ב-entry 0% cover 40%, הזום היה רץ רק בכניסה ואחר כך קפוא — מה שהיה נראה מוזר לזום.

וריאציה: Zoom Out (נדיר אבל מעניין)

@keyframes zoom-effect-reverse {
  from { transform: scale(1.15); }
  to   { transform: scale(1); }
}

התמונה מתחילה מוגדלת ומצטמצמת לגודל טבעי. יוצר תחושת "מגיע לפוקוס" — מתאים ל-hero images של portfolio sites.

מתקדם 10 דקות חינם חדש 2026

Scroll-TRIGGERED Animations (Chrome 145) — החדש של 2026

עד עכשיו דיברנו על scroll-driven animations — ה-progress שלהן צמוד לגלילה. גוללים למעלה, האנימציה חוזרת אחורה. לפעמים זו בדיוק ההתנהגות שאתם רוצים (parallax). אבל לעיתים קרובות אתם רוצים דבר אחר: אנימציה שמופעלת ברגע שאלמנט נכנס ל-viewport, ואז רצה בזמן שלה — 600ms fade-in, בלי קשר למהירות הגלילה.

זה הרצון הקלאסי של "IntersectionObserver replacement". עד Chrome 145 (מרץ 2026), זה עדיין דרש JavaScript. אחרי Chrome 145, יש CSS native.

ההבדל הקריטי

מאפייןScroll-DrivenScroll-Triggered
מה מקדם את האנימציה?מיקום הגלילהזמן (time-based)
גלילה למעלה חוזרת אחורה?כןלא — האנימציה כבר הסתיימה
Duration נקבע על ידיהגלילהה-CSS (animation: name 600ms)
Use caseParallax, progress barFade-in "one-shot", typewriter
הסטנדרט הישןTop/margin hacks + JSIntersectionObserver

Syntax (Chrome 145+)

ה-proposal הנוכחי (אפריל 2026) נראה כך:

.fade-in-once {
  animation: fade-in 600ms ease-out both;
  animation-trigger: view() entry 0%;
  /* האנימציה מתחילה ברגע שהאלמנט נכנס,
     רצה ב-600ms, ונשארת במצב סיום */
}

@keyframes fade-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

ההבדל המהותי: animation-trigger (חדש) במקום animation-timeline. ה-animation עצמה יש לה duration רגיל (600ms), וה-animation-trigger אומר "התחל כשאלמנט נכנס, אל תחבר ל-progress של הגלילה".

מצב פיצ'ר באפריל 2026

הפיצ'ר נמצא ב-Chrome 145 (stable מרץ 2026). Firefox ו-Safari עדיין לא תמכו. זו אחת הסיבות שהפרק הזה freshness-sensitive: ההמלצות יתעדכנו תוך 6-12 חודשים. עד שיהיה תמיכה רחבה (סוף 2026 / 2027), הדפוס הבטוח הוא scroll-driven + animation-range קצר (entry 0% entry 100%) — זה מדמה scroll-triggered ועובד ב-95%+ מהדפדפנים.

הדפוס המדומה — עד שיש תמיכה מלאה

עד שכל הדפדפנים תומכים ב-animation-trigger, זה הדפוס המדומה שעובד כבר היום:

.fade-in-once-simulated {
  animation: fade-in linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 100%;
  /* האנימציה רצה רק בתקופת "entry" — 400px של גלילה
     מה שמרגיש כמו one-shot ברוב המכשירים */
}

זה לא זהה — גלילה איטית מאוד תאט את ה-"fade", וגלילה מהירה תמחק אותה — אבל ב-95% מחוויות המשתמש זה נראה כמו scroll-triggered.

מתחיל 5 דקות חינם מצב פיצ'ר

Browser Support (אפריל 2026) — המצב האמיתי

זה פרק freshness-sensitive במיוחד. הנתונים הבאים נכונים לאפריל 2026 — בדקו ב-Can I Use לפני שמפרסמים בפרויקט production.

Scroll-Driven Animations (scroll() + view())

דפדפןגרסה ראשונהמצב אפריל 2026אחוז משתמשים
Chrome115 (יולי 2023)תמיכה מלאה~65%
Edge115 (יולי 2023)תמיכה מלאה~6%
Safari26 (אוקטובר 2025)תמיכה מלאה (iOS 26+ , macOS Tahoe 26+)~18%
Firefox139 (פברואר 2026)תמיכה מלאה~3%
Samsung Internet23 (בסיס Chromium 115)תמיכה מלאה~3%

תמיכה global משוערת: ~95%. הדפדפנים שלא תומכים: Safari ישן (< 26), Firefox ישן (< 139), WebViews ישנים באנדרואיד. לכולם — יש לתכנן fallback (סעיף 7.13).

Scroll-Triggered (animation-trigger)

דפדפןמצב אפריל 2026
Chrome 145+תמיכה מלאה (מרץ 2026)
Edge 145+תמיכה מלאה
Safariלא תומך עדיין
Firefoxלא תומך עדיין

תמיכה global: ~35%. לא לשימוש ב-production בלי fallback מלא.

טעות נפוצה

מפתחים רואים "95% תמיכה" ב-Can I Use ומניחים שהאתר שלהם יעבוד ברוב המקרים. הסטטיסטיקה הגלובלית לא תמיד משקפת את הקהל שלכם. בקהל ישראלי — Safari על iPhone הוא ~45% מהטראפיק (נתוני StatCounter 2025-2026). אם הקהל שלכם iOS דומיננטי, תבדקו שהגרסה שהם משתמשים בה היא Safari 26+ (iOS 26+). במכשירים ישנים (iPhone 11 ומטה שלא מעודכנים) — הם על Safari ישן יותר, בלי תמיכה.

בינוני 8 דקות חינם מעשי

Fallback Strategy — @supports כמו מקצוענים

חזרנו לעיקרון progressive enhancement מ-ch6: בונים קודם את הגרסה שעובדת בכל דפדפן, ואז משדרגים לגרסה הטובה יותר לדפדפנים שתומכים. לא "בונים enhanced ומפרקים למתחתיות".

ה-@supports הנכון

CSS נותן לכם @supports feature query — בלוק שרץ רק אם הדפדפן תומך ב-property/value מסוים:

/* ========== DEFAULT (fallback) ========== */
.reveal {
  opacity: 1;      /* תמיד נראה. ללא אנימציה. */
  transform: none;
}

/* ========== ENHANCED (scroll-driven) ========== */
@supports (animation-timeline: view()) {
  .reveal {
    animation: reveal-fade-up linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}

@keyframes reveal-fade-up {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}

למה זה חשוב

טעות נפוצה

מפתחים כותבים את ה-@keyframes עם opacity: 0 ב-from, שוכחים את ה-@supports check, ומפרסמים. בדפדפן שלא תומך ב-animation-timeline — ה-@keyframes לא רץ, אבל ה-CSS של opacity: 0 (אם כתוב ב-class עצמו) יישאר, והכרטיסים יהיו שקופים לצמיתות. הכלל: opacity: 0 אף פעם לא ב-class עצמו — תמיד רק בתוך @keyframes, בתוך @supports שנותן backup.

Pattern מלא — ready to paste

/* ========== FALLBACK DEFAULTS ========== */
.reveal-fade-up,
.reveal-slide-right,
.reveal-scale {
  opacity: 1;
}

/* ========== RESPECT prefers-reduced-motion ========== */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal-fade-up {
      animation: fade-up linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 40%;
    }
    .reveal-slide-right {
      animation: slide-right linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 50%;
    }
    .reveal-scale {
      animation: scale-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 50%;
    }
  }
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* ... שאר ה-@keyframes ... */

הקינון @media (prefers-reduced-motion) + @supports נותן לכם 3 שכבות:

  1. משתמש עם prefers-reduced-motion: reduce → לא מקבל אנימציה
  2. משתמש בדפדפן ישן → לא מקבל אנימציה
  3. משתמש בדפדפן מודרני בלי reduced-motion → מקבל את הניסיון המלא
בינוני 7 דקות חינם מעשי

Performance — מה לאנמט ומה לא

Scroll-driven animations native הרבה יותר מהירות מ-JavaScript equivalent — הדפדפן יכול לאופטם אותן, להפעיל אותן ב-compositor thread, ולא לחסום ה-main thread. אבל זה לא אומר שכל אנימציה תהיה מהירה. השילוב שבחרתם של properties ו-keyframes קובע.

הכלל של 2 ה-GPU-accelerated properties

יש שתי properties שהדפדפן יכול לאנמט ב-GPU בלי re-layout ובלי re-paint:

  1. transform — translate, scale, rotate, skew
  2. opacity — שקיפות

כל השאר — width, height, top, left, margin, padding — יגרמו ל-layout reflow וכל פריים חדש ידרוש חישוב מחדש. על mobile זה מוריד FPS מ-60 ל-15-20.

במקום זה (איטי)תעשו את זה (מהיר)
top: 40px → top: 0transform: translateY(40px) → translateY(0)
left: 60px → left: 0transform: translateX(60px) → translateX(0)
width: 80% → width: 100%transform: scaleX(0.8) → scaleX(1)
height: 200px → height: 400pxtransform: scaleY(0.5) → scaleY(1)

will-change — השתמשו בזהירות

will-change: transform אומר לדפדפן "אני עומד לאנמט את זה, תכין GPU layer". זה עוזר לפעמים, אבל שימוש יתר בזה הורס יותר ממה שעוזר — כל אלמנט עם will-change צורך GPU memory גם כשהוא לא זז.

כלל האצבע: will-change רק על אלמנטים שמתאנמים באופן פעיל, ורק כשראיתם בעצמכם בעיית performance ב-DevTools. אל תוסיפו "לכל מקרה". נעבור לעומק על זה ב-ch11 (Performance).

Mobile testing — חובה

טעות נפוצה

מפתחים בודקים את ה-scroll animations על MacBook Pro M3 ורואים 60fps חלקים. מפרסמים. משתמשים ב-Galaxy A14 חווים 15fps וסוחב. Scroll animations מגבירים CPU/GPU דרישות בעיקר על mobile. חובה לבדוק על מכשיר ישן אמיתי, לא רק DevTools throttle. שימוש חכם: DevTools → Performance → 4x CPU throttling + 3G network + Mobile emulation. אם זה לא חלק שם, זה לא יהיה חלק במציאות.

prefers-reduced-motion — נגישות

לחלק מהמשתמשים (vestibular disorders, motion sensitivity, migraine patients) אנימציות גלילה גורמות לסחרחורת ובחילה. זה לא preference קוסמטי — זה נגישות. תמיד עוטפים את ה-animation-timeline ב-@media (prefers-reduced-motion: no-preference):

@media (prefers-reduced-motion: no-preference) {
  .reveal {
    animation-timeline: view();
    /* ... */
  }
}

Debugging Scroll Animations — הטריקים של DevTools

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

  1. Chrome DevTools → Animations panel (F12 → three-dot menu → More tools → Animations). הפאנל הזה מראה כל אנימציה שרצה, כולל scroll-driven. אפשר להאט אותה (25%, 10%) כדי לראות בדיוק מה קורה בכל fram.
  2. Animation timeline inspector — בגרסאות חדשות של Chrome (115+), כשבוחרים אלמנט עם animation-timeline, ה-Styles panel מראה את ה-progress הנוכחי של ה-timeline באחוזים. בזמן גלילה — תראו איך ה-0% → 100% קורה.
  3. Performance recording בזמן scroll — F12 → Performance → Record → גללו → Stop. מחפשים "Composite Layers" ו-"Recalc Style". אם יש יותר מ-2-3 recalc per frame, יש בעיית performance.
  4. FPS meter — F12 → Rendering → Frame Rendering Stats. מראה FPS בזמן אמת. אם יורד מתחת ל-55 בזמן scroll — בעיה.
  5. "Paint flashing" — F12 → Rendering → Paint flashing. מסמן ירוק בכל מקום שה-browser מצייר מחדש. אם אנימציה שלכם מדליקה הרבה ירוק — אתם מאנמים properties שגורמות ל-repaint (כמו background-color), לא רק לקומפוזיציה.
טריק פרו

יש property ניסיוני ב-Chrome: animation-timeline-scope. הוא מאפשר להגדיר timeline ב-parent וליישם על children בלי לחזור על השם. זה עדיין experimental (אפריל 2026) — לא לשימוש production — אבל שווה לעקוב. זה יקצר את הקוד ב-30-40% ברגע שיתייצב.

בינוני 8 דקות חינם Framework

Framework: CSS vs JavaScript — החלטה כנה

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

Framework #2 — CSS vs JavaScript Animation Library

שאלו 5 שאלות בסדר הזה:

  1. האם האנימציה היא Reveal פשוט (fade / slide / scale) לאלמנט בודד כשהוא נכנס ל-viewport?
    • כן → CSS view() timeline (8 שורות, 0 JS)
  2. האם האנימציה היא Progress indicator או Parallax של שכבות?
    • כן → CSS scroll() timeline (12 שורות, 0 JS)
  3. האם האנימציה מערבת choreography בין כמה אלמנטים שונים שצריכים לנוע בסינכרון מדויק (למשל: text fade + image scale + background color change ביחד)?
    • כן → GSAP + ScrollTrigger — עדיין הסטנדרט לזה. CSS לא נותן synchronization גסטורלי בין אלמנטים.
  4. האם אתם רוצים smooth scrolling (lerp, inertia) שהוא שונה מה-default של הדפדפן?
    • כן → Lenis. אין CSS-native לזה. Lenis הוא 3KB, בטוח ומהיר.
  5. האם האנימציה מבוססת-gesture (drag, swipe, spring physics)?
    • כן → Framer Motion (או Motion One). CSS לא תומך ב-physics-based animations.

Rule of thumb: התחילו מ-CSS. אם אתם לא מצליחים להשיג את האפקט ב-CSS אחרי 15 דקות של ניסוי — עברו ל-JS library. אל תתחילו מ-GSAP לפטרן של fade-up פשוט — זה overkill של 40KB.

טבלת 8 Use Cases

Use Caseפתרון 2026גודל bundle
Fade-in כשכרטיס נכנס ל-viewportCSS view()0 KB
Progress bar עליוןCSS scroll()0 KB
Parallax של 3 שכבותCSS scroll()0 KB
Sticky nav שמחליף צבעCSS scroll() + animation-range pixel-based0 KB
Horizontal scroll section (pin + scroll)GSAP ScrollTrigger~40 KB
Complex choreography (text + image + bg)GSAP + Timeline~40 KB
Smooth scrolling (lerp)Lenis~3 KB
Swipe gestures + spring animationsFramer Motion~60 KB

הערה: ב-4 ה-use cases הראשונים (95% מהאתרים), לא צריך ספרייה בכלל. רק ב-cases 5-8 (אתרים מורכבים — awards sites, marketing immersive) GSAP/Lenis/Framer עדיין הכרחיים.

תרגיל 3 — Parallax Hero 20 דקות
  1. בנו CodePen חדש עם section אחד של גובה 100vh. שם ה-class: parallax-hero.
  2. הוסיפו 3 שכבות: .layer-bg (תמונת הרים/שמיים), .layer-mid (תמונת עננים PNG שקופה), .layer-fg (כותרת h1).
  3. הדביקו את ה-CSS של 3 השכבות עם parallax (מסעיף 7.7).
  4. הוסיפו section נוסף למטה עם 150vh של תוכן כדי שתוכלו לגלול ולראות את האפקט.
  5. גללו באיטיות — האם רואים את 3 השכבות נעות במהירויות שונות?
  6. שנו את הערכים: parallax-slow מ-20% ל-10%, parallax-fast מ--15% ל--5%. האם האפקט הפך עדין יותר? יותר דרמטי?
  7. הוסיפו את ה-@supports (animation-timeline: scroll()) wrapper כדי שדפדפנים ישנים יראו rendering רגיל בלי parallax.
  8. Expected output: CodePen עם parallax hero + fallback + 2 וריאציות (עדין/דרמטי). שמרו את הלינק.
תרגיל 4 — animation-range Playground 15 דקות
  1. לכו ל-scroll-driven-animations.style/demos. מצאו את "View timeline: animation-range visualizer".
  2. נסו את כל 6 ה-ranges: entry, exit, cover, contain, entry-crossing, exit-crossing. עבור כל אחד, ציירו (על נייר או על whiteboard) מה קורה ויזואלית.
  3. עכשיו בנו ב-CodePen דף שיש בו 3 אלמנטים זהים, כל אחד עם animation-range שונה:
    • Element A: entry 0% entry 100%
    • Element B: entry 0% cover 50%
    • Element C: cover 0% exit 100%
  4. תנו לכולם את אותה אנימציית fade-up. גללו באיטיות. איך כל אחד מהם שונה?
  5. כתבו (על גבי הקוד או בתגובה) איזה range הכי מתאים ל-reveal "מהיר בכניסה", ואיזה ל-reveal "הדרגתי לאורך זמן".
  6. Expected output: CodePen עם 3 אלמנטים + הערה קצרה איזה range אתם אוהבים הכי הרבה. הבנה עמוקה של range = 50% מהפרק.
בינוני 8 דקות חינם פרומפט

Scroll Animation Prompt — מה לבקש מ-AI

אם תבקשו מ-AI "add fade-in animations when cards scroll into view", תקבלו ברוב המקרים IntersectionObserver עם 25 שורות JavaScript. זו ברירת המחדל של ה-training data שלו (2020-2023).

הפתרון: פרומפט ספציפי שמפרט את ה-CSS API שאתם רוצים.

הפרומפט הגנרי שלא עובד

"Add fade-in animations to the cards when they scroll into view"

תוצאה: 25 שורות JS עם IntersectionObserver, class toggle, ו-CSS transition. הסכום: 40+ שורות. ועובד גם ב-dinosaur browser שאף אחד לא משתמש בו.

הפרומפט המדויק שעובד

Use CSS scroll-driven animations for the card reveal.
Do NOT use IntersectionObserver or JavaScript.

Requirements:
- Use `animation-timeline: view()` on each card
- Use `animation-range: entry 0% cover 40%` for a smooth entry
- `@keyframes` should animate `opacity: 0 -> 1` and
  `transform: translateY(40px) -> translateY(0)`
- Use `linear` timing (not ease-in-out)
- Include `animation-fill-mode: both`
- Wrap the entire animation in `@supports (animation-timeline: view())`
  with a fallback of `opacity: 1`
- Wrap inside `@media (prefers-reduced-motion: no-preference)`
- Use logical properties where applicable

Output the complete CSS, nothing else.

תוצאה: 15-20 שורות CSS, 0 שורות JS, עם progressive enhancement, accessibility, ו-RTL awareness. כל מה שביקשתם.

Template ל-re-use

## Scroll Animation Spec v1.0

Timeline type: [view / scroll]
Timeline axis: [block / inline]
Timeline scroller: [root / nearest / self]
Animation range: [entry 0% cover 40% / cover 0% cover 100% / ...]
Animation properties: [opacity / transform only — GPU accelerated]
Timing: [linear (default for scroll-driven) / ease-out-quart for scale]

Pattern:
- fade-up / slide-right / scale-in / parallax-layer / progress-bar

Fallback:
- @supports (animation-timeline: view()) wrapper REQUIRED
- Fallback state: opacity: 1, transform: none
- @media (prefers-reduced-motion: no-preference) wrapper REQUIRED

Anti-patterns (do NOT use):
- IntersectionObserver (unless ch11 performance explicitly requires)
- GSAP / Framer Motion (overkill for simple reveals)
- Animating width/height/top/left (causes reflow)
- Animating background-color from `transparent` (interpolation through black)

Please output complete CSS only.
תרגיל 5 — AI Prompt Calibration 20 דקות
  1. פתחו את כלי ה-AI המועדף עליכם (Claude / ChatGPT / Cursor / Copilot). בקשו: "Add scroll reveal animations to my cards". שמרו את התוצאה בצד.
  2. עכשיו בקשו שוב עם ה-Template המלא מהסעיף הזה. ציינו ספציפית: timeline = view(), range = entry 0% cover 40%, pattern = fade-up, fallback required. שמרו את התוצאה.
  3. השוו: איזה תוצאה קצרה יותר? מי משתמש ב-CSS native? איזה תוצאה כוללת fallback ו-prefers-reduced-motion?
  4. הריצו את שתי התוצאות ב-CodePen ובדקו על Chrome + Firefox + Safari. האם ה-JS version עובד בכל מקום? ה-CSS version?
  5. הוסיפו לתוצאה של Template שאלה נוספת: "Can you also add a parallax hero with 3 layers?". האם AI כעת מבין את השפה ונשאר ב-CSS?
  6. Expected output: שני קטעי CSS, השוואה של 3-5 שורות ביניהם, ומסקנה איזה Template אתם תאמצו לפרויקטים שלכם.
  7. Bonus: שמרו את ה-Template כ-snippet בתוך Cursor/VS Code — תקראו לו בקיצור דרך בכל פרויקט חדש.
הרוטינה — איך לחיות עם scroll animations

יומית (בזמן עבודה על פרויקט)

שבועית

חודשית

Just One Thing — הדבר היחיד שכדאי לעשות עכשיו

אם יש לכם זמן רק לדבר אחד מהפרק הזה, עשו את זה: קחו את הפרומפט Template מסעיף 7.16 ושמרו אותו כ-snippet בכלי ה-AI שלכם (Cursor, VS Code, Claude Project instructions). בפעם הבאה שתבקשו אנימציית גלילה — ה-Template הזה יחליף 30 שורות JavaScript עם 15 שורות CSS. זה ה-ROI הכי גדול בפרק. הכל השאר (animation-range, parallax, progress bar) — יגיע עם הזמן. ה-Template הזה משנה התנהגות של AI באופן מיידי.

עשו עכשיו 3 דקות

פתחו את ה-IDE שלכם. צרו קובץ חדש: scroll-animation-template.md. הדביקו את ה-Template מסעיף 7.16. שמרו במיקום שנוח לכם (Desktop / Notes / Cursor snippets). בפעם הבאה שאתם מתחילים פרויקט חדש — פתחו אותו ועיקו לפרומפט הראשון שלכם.

עשו עכשיו 5 דקות

פתחו את האתר הגנרי שAI בנה לכם (מפרקים קודמים). פתחו DevTools → Sources. חפשו: האם יש IntersectionObserver בקוד? window.scrollY? addEventListener('scroll', ...)? ספרו כמה שורות JS מוקדשות לאנימציות גלילה. אם יותר מ-20 שורות — זה פרויקט לשכתב ל-CSS.

עשו עכשיו 4 דקות

בדקו caniuse.com/css-animation-timeline. רשמו את האחוז הגלובלי של תמיכה היום. השוו ל-95% המצוין בסעיף 7.12. גדול יותר? קטן יותר? אם גדול יותר — אולי אתם יכולים להוריד את ה-@supports wrapper באתרים חדשים.

עשו עכשיו 3 דקות

פתחו apple.com. גללו לאט. ספרו כמה reveal animations רואים בעמוד. ספרו כמה parallax effects. הסתכלו ב-DevTools → Elements → בדקו class של אלמנט שעושה reveal. האם רואים animation-timeline? (רמז: Apple ב-2026 משתמשים ב-CSS scroll-driven ברוב המקרים.)

Check Yourself — 5 שאלות לפני שעוברים לפרק הבא
  1. מה ההבדל בין animation-timeline: scroll() ל-animation-timeline: view()? (תשובה ב-sections 7.2-7.3 + framework 7.5)
  2. מה המשמעות של animation-range: entry 0% cover 40%, ולמה זה הסטנדרט המקצועי? (תשובה ב-section 7.4)
  3. תנו שני use-cases שבהם GSAP עדיין עדיף על CSS native, ושני use-cases שבהם CSS native מספיק לגמרי. (תשובה ב-framework 7.15 + טבלה)
  4. איך מוודאים שאנימציית scroll-driven לא שוברת את האתר בדפדפן שלא תומך בה? (תשובה ב-section 7.13 — @supports pattern)
  5. למה מאנמים transform: translateY(40px) במקום top: 40px? (תשובה ב-section 7.14 — GPU acceleration, no reflow)

אם עניתם ב-ביטחון על 4 מתוך 5 — אתם מוכנים לפרק הבא. אם פספסתם אחת — חזרו לסעיף הרלוונטי לפני ch8.

סיכום הפרק + גשר לפרק 8

הפרק הזה הוא המפגש בין שתי נקודות מפנה: CSS-native API חדש ו-שחרור תלות בספריות JavaScript. תחילת 2024 סימנה את הרגע שבו אנימציות הגלילה המרכזיות — reveal, parallax, progress indicator — הפסיקו להיות "משהו שדורש ספרייה" והפכו ל-"4 שורות CSS native". לקחתם את animation-timeline: scroll() (שמעקב אחרי גלילת הדף כולו — progress bars, parallax), את animation-timeline: view() (שמעקב אחרי אלמנט יחיד — reveal, image zoom), ואת animation-range (6 הטווחים שנותנים שליטה מדויקת מתי האנימציה מתחילה ומסתיימת). בניתם 5 patterns מקצועיים — fade-up/slide-in/scale-in reveal, parallax של 3 שכבות, progress bar של 12 שורות CSS, sticky nav שמחליף צבע, ו-image zoom. למדתם את הכללים הקריטיים: להשתמש ב-transform ו-opacity (GPU), להימנע מ-top/left/width/height (reflow), לעטוף ב-@supports ו-prefers-reduced-motion, ולבדוק על מכשיר mobile אמיתי. הכי חשוב — קיבלתם שפה חדשה לדבר עם AI: פרומפט Template שמזניק AI מ"IntersectionObserver" ל-"animation-timeline: view()", ומחליף 40 שורות JS ב-15 שורות CSS. זה לא רק שיפור — זו קפיצת דור של 10x על רוב האתרים.

גשר לפרק 8: עכשיו כשיש לכם אנימציות גלילה native, הצעד הבא הוא אנימציות ניווט native. View Transitions API הוא ה-counterpart של scroll-driven — אבל במקום "אנימציה בגלל גלילה", זו "אנימציה בגלל מעבר בין מצבי UI". כרטיס מוצר שמתפוצץ לעמוד מוצר מלא. עמוד home שעובר ל-about עם cross-fade חלק. אלמנטים שעפים בין מיקומים. כל זה CSS (לרוב) ו-JavaScript מזערי — בדיוק כמו שב-ch7 החלפתם IntersectionObserver ב-CSS. ch7 + ch8 ביחד = "אנימציות מקצועיות בלי ספריית 40KB". סטודיו עיצוב מלא בכסף של המרה מ-$5K לחודש על GSAP subscription. אתם עושים את אותו הדבר עם 30 שורות CSS ושתי APIs native. זה ההבדל בין "אתר של AI ב-2023" ל"אתר של סטודיו ב-2026".

Checklist השלמת פרק — 14 פריטים