- אנימציית Reveal מלאה (3 וריאציות) — CSS מוכן להעתקה ל-fade-up, slide-in-from-right, ו-scale-in, כולם מבוססי
animation-timeline: view()ו-animation-range, כולל עקומות timing מקצועיות (ease-out-quart) - Progress Bar עליון ב-CSS טהור — אלמנט יחיד עם
animation-timeline: scroll(root block),transform-origin: leftו-animation: grow linear. 12 שורות CSS, בלי JavaScript - Parallax של 3 שכבות — hero background נע לאט, שכבת mid ביניים, foreground שנע מהר — הכל ב-CSS טהור עם
scroll()timeline ו-translateYממוזער ל-GPU - השוואה מלאה: CSS vs JavaScript Animation Libraries — טבלה של 8 use-cases עם ההמלצה (CSS / GSAP / Framer Motion / Lenis) לכל אחד
- Cheat Sheet של 6 animation-ranges — entry, exit, cover, contain, entry-crossing, exit-crossing עם דיאגרמה ויזואלית ומתמטיקת פיקסלים
- Fallback Pattern מלא —
@supports (animation-timeline: scroll())עם גרסת enhanced + גרסת no-animation, מוכן להדבקה בכל פרויקט - Scroll Animation Prompt Template — פרומפט באנגלית שמפרט timeline type, range, easing, duration, fallback, ותנאי
prefers-reduced-motion, כדי ש-AI לא יחזור ל-IntersectionObserver כברירת מחדל - מילון של 12 מונחי אנימציית גלילה — scroll-driven vs scroll-triggered, timeline, range, entry/exit, parallax ועוד
- תוכלו להסביר את ההבדל בין
scroll()timeline ל-view()timeline ולבחור נכון —scroll()להתקדמות של הדף כולו (progress bars, parallax hero),view()לאלמנט בודד (reveal, image zoom), לפי flowchart של 3 שאלות - תוכלו לבקש מ-AI אנימציות reveal בגלילה ב-CSS טהור — fade-up, slide-in, scale-in — עם
animation-range: entry 0% cover 40%מדויק, בלי IntersectionObserver, בלי ספריה, בלי שורה אחת של JavaScript - תוכלו להנחות AI לבנות progress indicator ו-parallax effect — פס התקדמות עליון של 12 שורות CSS, ו-parallax של 3 שכבות במהירויות שונות — הכל בקוד שעובד ב-95%+ מהדפדפנים (אפריל 2026) עם fallback חינני
- תוכלו להחליט מתי CSS scroll animations מספיק ומתי עדיף JavaScript — decision framework עם 8 use-cases: CSS ל-reveal/progress/parallax פשוט, GSAP ל-choreography מורכב, Lenis ל-smooth scrolling, Framer Motion ל-gestures — לא להשתמש ב-sledgehammer כשצריך hammer
- פרקים קודמים: פרק 6 (Modern CSS Superpowers) — חובה. תצטרכו את המושגים browser support, progressive enhancement, ו-Can I Use מפרק 6. פרק 5 (Layout Mastery) מומלץ כי אנימציות גלילה משרתות layout, לא להפך
- מושגים שיחזרו מפרקים קודמים: progressive enhancement (ch6), browser support ו-Can I Use (ch6), visual hierarchy (ch1 — אנימציה צריכה להוביל עין למיקוד, לא להסיח),
@keyframesו-transformבסיסיים (היכרות מפרקים קודמים) - ידע בסיסי נדרש: הכרה עם CSS animations רגילים (
@keyframes name { from {} to {} },animation: name 1s ease-in-out). אם מעולם לא כתבתם@keyframes, הקדישו 10 דקות ל-MDN: @keyframes לפני שמתחילים - כלים: Chrome 115+ / Edge 115+ (תמיכה מלאה), Firefox 139+ או Safari 26+ לבדיקת fallback. DevTools Animation Panel. כלי AI לבניית אתרים (Bolt / Lovable / v0 / Cursor / Claude)
- אופציונלי: חשבון ב-scroll-driven-animations.style של Bramus Van Damme (Chrome DevRel) — הסנדבוקס הרשמי עם 40+ דמו אינטראקטיבי
- זמן משוער: 120-150 דקות (כולל 5 התרגילים)
לאורך הקורס אתם בונים את היכולת להפוך אתר גנרי ש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 של רב-פיתוח.
| מונח (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 |
7.1 המהפכה — למה זה פרק שלם ולא סעיף
תחשבו על הדברים הבאים: פס התקדמות בראש עמוד blog שמראה כמה קראתם. כרטיסי features שמופיעים עם fade-up כשגוללים אליהם. תמונת hero ש-parallax נעה לאט ברקע בעוד הטקסט מעליה נע מהר. Nav bar שמחליף מ-shadow שקוף ל-shadow מלא ברגע שגללתם 80 פיקסלים. כולם patterns שרואים ב-כל אתר פרימיום ב-2026. וכולם, עד 2024, דרשו JavaScript.
התשתית היסטורית:
- 2015: GSAP ScrollTrigger — ספריית JavaScript של 30-50KB שנהייתה הסטנדרט התעשייתי
- 2017:
IntersectionObserverAPI — browser-native JS API שהפך לברירת מחדל של reveal-on-scroll - 2020: Framer Motion — ספרייה של React שהפכה אנימציות ל-declarative
- 2022: Lenis — smooth scrolling library, הסטנדרט של הסטודיואים היוקרתיים
- מרץ 2023: Chrome 115 שחרר תמיכה ראשונה ב-
animation-timeline: scroll()ו-view()— מאחורי flag - יולי 2023: Chrome 115 stable — ללא flag. זו הנקודה שבה התחילה מהפכה שקטה
- 2024-2025: Edge, אחריו Safari 26, אחריו Firefox 139 — כולם הוסיפו תמיכה
- אפריל 2026: תמיכה global של ~95% לפי Can I Use — הגיע הרגע להחליף ספריות שלמות בקוד native
לפני 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 gallery | CSS animation-timeline: scroll() / view() |
| Scroll-TRIGGERED | האנימציה מתחילה כשמגיעים לנקודה מסוימת, אבל רצה בזמן שלה. גוללים בחזרה = האנימציה כבר רצה, לא משנה. | "fade in" של כרטיס כשהוא נכנס ל-viewport | Chrome 145+ CSS native. דפדפנים אחרים עדיין JS. |
| Smooth Scrolling / Scroll-Enhanced | שינוי האופי של הגלילה עצמה (inertia, lerp, easing). | אתרי Awwwards עם גלילה "חלקה" שונה מה-default | JavaScript — Lenis. אין CSS native. |
רוב הפרק יעסוק ב-scroll-driven (הוותיק יותר, תמיכה רחבה). נגע ב-scroll-triggered בסעיף 7.11 (חדש ב-Chrome 145, מרץ 2026). smooth scrolling — נסכם שעדיין צריך ספרייה.
פתחו stripe.com. גללו לאט למטה. ספרו את האנימציות שרואים: כרטיסים שמופיעים, תמונות שזזות יחסית לטקסט, progress indicator, sticky nav שמשנה צבע. רשמו 5 אנימציות שזיהיתם. כעת פתחו DevTools → Network → צרפו filter JS → גללו שוב. האם הטעינה של JS חדש כשאתם גוללים? כנראה שלא — Stripe ב-2026 משתמש ב-scroll-driven CSS לרבים מהאפקטים האלה.
פתחו scroll-driven-animations.style. זה הסנדבוקס הרשמי של Bramus Van Damme מ-Chrome DevRel — 40+ דמואים אינטראקטיביים. לחצו על "progress bar" בצד הימני. גללו למטה — ראו את הפס מתמלא ב-CSS טהור. לחצו "view source" — מינימום קוד. שמרו את האתר הזה — תחזרו אליו במהלך הפרק.
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 מקצועי. בואו נפרק איך זה עובד:
animation: grow-progress linear— אנימציה רגילה, עםlineartiming (כי הגלילה היא linear — אנחנו לא רוצים easing על התקדמות). שימו לב: איןdurationכי ה-duration מגיע מהגלילה, לא מהשעון.animation-timeline: scroll(root block)— זו השורה הקריטית. אנחנו מחליפים את ה-"wall clock time" ב-timeline של גלילתroot(ה-<html>root) על ציר ה-block(אנכי במסמכי LTR/RTL, horizontal ב-writing-modeאנכי).transform: scaleX(0)←scaleX(1)— אנחנו משנים scale אופקי. עםtransform-origin: left centerזה גדל משמאל לימין. ב-RTL, שנו ל-right centerאו השתמשו ב-transform-origin: start center(חדש, logical).@keyframesרגיל — ה-@keyframesזהה לחלוטין לאנימציות רגילות. השינוי היחיד הוא ה-timeline שמחליף את ה-duration.
Anatomy של scroll(root block)
הפונקציה scroll() מקבלת שני ארגומנטים אופציונליים:
| ארגומנט | מה עושה | ערכים |
|---|---|---|
| Scroller | איזה scroll container לעקוב אחריו | nearest (default) / root / self |
| Axis | ציר הגלילה | block (default, אנכי) / inline (אופקי) / x / y |
ה-3 סוגי scroller הם:
scroll(nearest block)— מצא את ה-scroll container הקרוב ביותר במעלה ה-DOM. אם האלמנט שלך בתוך modal עםoverflow: scroll, זה יעקוב אחרי ה-modal, לא אחרי הדף.scroll(root block)— תמיד עקוב אחרי ה-<html>root. זה מה שאתם רוצים ל-progress bar של כל הדף.scroll(self block)— האלמנט עצמו הוא ה-scroller (עםoverflow: scroll). שימושי ל-horizontal scroll galleries בתוך section.
אנשים כותבים 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.
פתחו 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? הבנתם את ההבדל.
view() timeline — אנימציה מופעלת-כניסה-ל-viewport
אם scroll() עוקב אחרי גלילת הדף כולו, אז view() עוקב אחרי כניסה ויציאה של אלמנט ספציפי מה-viewport. זה ה-timeline שרוב ה-use cases של reveal-on-scroll ישתמשו בו.
ה-progress של ה-timeline מוגדר כך:
- 0% — האלמנט בדיוק מתחת ל-viewport (לא נראה עדיין, עומד להיכנס)
- 50% — האלמנט במרכז ה-viewport (מכוסה באופן מלא)
- 100% — האלמנט בדיוק מעל ל-viewport (יצא, כבר לא נראה)
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 hero | Fade-in reveal, image zoom |
| האנימציה חוזרת אחורה? | כן — גוללים למעלה, חוזר אחורה | כן — האלמנט יוצא, האנימציה חוזרת |
| כמה אלמנטים? | אחד (הדף כולו) | רבים — אנימציה לכל אלמנט בנפרד |
אם האנימציה צריכה להיות אחת ולקרות פעם אחת בלבד לכל הדף — scroll(). אם האנימציה צריכה להיות לכל אלמנט בנפרד, וכל אלמנט מגיב לעצמו — view(). Progress bar = אחד → scroll(). Fade-in לכל כרטיס בגלריה של 20 כרטיסים = 20 פעמים → view().
ב-CodePen שלכם, הוסיפו 6 כרטיסים (div.card) עם רווח גדול ביניהם. הוסיפו את ה-CSS של .fade-up (8 שורות). תנו לכל כרטיס את class fade-up. גללו. עובד! כעת שנו את animation-range ל-entry 0% cover 10% — מה קרה? (תשובה: האנימציה מתבצעת מהר יותר, נגמרת כשהאלמנט מכסה רק 10%). שנו ל-cover 0% cover 100%. מה קורה עכשיו? (תשובה: האנימציה רצה כל זמן שהאלמנט במסך — גם בגלילה למעלה).
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. איך המספרים עובדים:
entry= תקופה של 400px של גלילה (גובה האלמנט), מהרגע שהקצה העליון מגיע ל-bottom של ה-viewport, עד שהקצה התחתון מגיע ל-bottomcover= תקופה של 1200px (viewport + element), מהרגע שהאלמנט מתחיל להיכנס עד שהוא יוצא לגמריcontain= תקופה של 400px (viewport - element), רק כשהאלמנט נמצא כולו ב-viewport
זה מסביר למה 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% | האלמנט נעלם/מזעיר לפני שיוצא מלמעלה |
חזרו ל-scroll-driven-animations.style. לחצו על "view() timeline tool" בצד הימני. זהו visualizer אינטראקטיבי שמראה כל אחד מה-6 ranges בזמן אמת. גרירו את ה-sliders, ראו איך entry, cover, ו-contain שונים זה מזה. זה ה-2 דקות הכי שוות שתשקיעו בפרק.
Framework: scroll() vs view() — מתי מה
הבלבול הכי נפוץ אצל מי שמתחיל ב-scroll-driven animations הוא מתי להשתמש ב-scroll() ומתי ב-view(). הנה framework שפותר את זה ב-3 שאלות.
- שאלה 1: כמה אלמנטים יש לכם עם האנימציה הזו?
- אחד (progress bar, parallax background) → המשיכו לשאלה 2
- רבים (fade-in לכל כרטיס בגלריה) →
view()— כל אלמנט צריך את ה-viewport-awareness שלו
- שאלה 2: האנימציה קשורה ל-מיקום במסמך או ל-מיקום במסך?
- מיקום במסמך ("כמה גללתי מכל הדף?") →
scroll()— progress bar, parallax של hero - מיקום במסך ("מתי האלמנט נכנס/יצא?") →
view()
- מיקום במסמך ("כמה גללתי מכל הדף?") →
- שאלה 3: האם האנימציה צריכה להיות קשורה לאלמנט אחר (לא ה-root scroller)?
- כן (horizontal gallery בתוך section, modal scroll) →
scroll(nearest)אוscroll(self) - לא (הדף כולו) →
scroll(root block)
- כן (horizontal gallery בתוך section, modal scroll) →
Rule of thumb סופי: אם אתם מוסיפים את האנימציה ל-component שיופיע מספר פעמים → view(). אם אתם מוסיפים לאלמנט ייחודי (אחד ב-layout) → scroll().
5 Use Cases נפוצים ומה לבחור
| Use Case | Timeline | Range |
|---|---|---|
| Progress bar בראש הדף | scroll(root block) | (ללא — רץ מ-0 ל-100) |
| Hero parallax | scroll(root block) | (ללא — רץ מ-0 ל-100) |
| Fade-in לכרטיסים | view() | entry 0% cover 40% |
| Image zoom כשתמונה במרכז | view() | cover 0% cover 100% |
| Sticky nav שמחליף צבע ב-scroll | scroll(root block) | 0 200px (pixel-based) |
| Horizontal gallery בתוך section | scroll(self inline) | (ללא) |
| Text שחושף מילה-מילה | view() לכל span | entry 0% contain 50% |
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.
- פתחו CodePen חדש. בנו HTML: כותרת
h1, אחריה 6div.card(כל אחד עם תמונה מ-Unsplash או רקע צבעוני, כותרת, וטקסט קצר). - תנו לכולם width של 600px ו-margin אוטומטי. הוסיפו
height: 80vhלפני הכרטיסים כדי שתוכלו לגלול. - הוסיפו את ה-CSS של
.reveal-fade-up(8 שורות מהסעיף). תנו לכל הכרטיסים classreveal-fade-up. גללו — אמורים לראות את כולם מופיעים. - שנו את 3 הכרטיסים הראשונים ל-
reveal-slide-right, ואת האחרונים ל-reveal-scale. גללו. איזה מהשלושה נראה הכי "מקצועי"? - עכשיו הוסיפו stagger:
.card:nth-child(odd)עםanimation-range: entry 0% cover 40%, ו-.card:nth-child(even)עםentry 10% cover 50%. גללו באיטיות — שמתם לב ל-stagger? - Expected output: CodePen עם 6 כרטיסים עם 3 וריאציות reveal + stagger. שמרו את הלינק.
- Bonus: הוסיפו
@media (prefers-reduced-motion: reduce)עםanimation: none— נגן נגישות חובה.
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%); } }
איך זה עובד
layer-bgנע 20% של ה-height שלו כלפי מטה — נע לאט (תחושת רחוק)layer-midנע 50% — בינוניlayer-fgנע -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.
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).
- לקחו את ה-CodePen מתרגיל 1. הוסיפו את ה-HTML של progress bar בתחילת ה-
<body>. - הדביקו את הקוד של הוריאציה הבסיסית (13 שורות CSS). גללו. רואים את הפס נמלא?
- שנו ל-gradient (וריאציה 2). איך זה נראה מול הצבע החד? התאימו את הצבעים לפלטה שלכם מ-ch3.
- הוסיפו
opacity: 0.9,box-shadow: 0 2px 8px rgba(0,0,0,0.1), ו-border-radius: 0 4px 4px 0. האם זה נראה יותר "פרימיום"? - בדקו ב-mobile emulator של DevTools. האם הפס עדיין עובד? אם כן — ברכות, יש לכם progress bar production-ready של 15 שורות.
- Expected output: CodePen עם progress bar עובד + 3 וריאציות מוכנות.
Sticky Nav שמחליף צבע בגלילה
פטרן שכיח: 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);
}
}
מה קורה כאן
animation-range: 0 150px— הטריק המרכזי. האנימציה רצה רק בין pixel 0 ל-pixel 150 של גלילה. אחרי 150px — היא "תקועה" במצב הסיום.backdrop-filter: blur(0)→blur(12px)— המעבר מ"שקוף" ל"glassmorphism". נלמד את זה עמוקות ב-ch9.box-shadowtransparent → opaque — ה-shadow מופיע בהדרגה בזמן הגלילה.
מפתחים מנסים לאנמט background-color מ-transparent ל-rgba(255,255,255,0.85) ומופתעים שזה לא עובד חלק. הסיבה: transparent הוא בעצם rgba(0,0,0,0) — שחור שקוף. האינטרפולציה עוברת דרך "שחור דהוי" שנראה מוזר. הפתרון: השתמשו ב-rgba(255,255,255,0) כ-from state במקום transparent.
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.
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-Driven | Scroll-Triggered |
|---|---|---|
| מה מקדם את האנימציה? | מיקום הגלילה | זמן (time-based) |
| גלילה למעלה חוזרת אחורה? | כן | לא — האנימציה כבר הסתיימה |
| Duration נקבע על ידי | הגלילה | ה-CSS (animation: name 600ms) |
| Use case | Parallax, progress bar | Fade-in "one-shot", typewriter |
| הסטנדרט הישן | Top/margin hacks + JS | IntersectionObserver |
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 של הגלילה".
הפיצ'ר נמצא ב-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.
Browser Support (אפריל 2026) — המצב האמיתי
זה פרק freshness-sensitive במיוחד. הנתונים הבאים נכונים לאפריל 2026 — בדקו ב-Can I Use לפני שמפרסמים בפרויקט production.
Scroll-Driven Animations (scroll() + view())
| דפדפן | גרסה ראשונה | מצב אפריל 2026 | אחוז משתמשים |
|---|---|---|---|
| Chrome | 115 (יולי 2023) | תמיכה מלאה | ~65% |
| Edge | 115 (יולי 2023) | תמיכה מלאה | ~6% |
| Safari | 26 (אוקטובר 2025) | תמיכה מלאה (iOS 26+ , macOS Tahoe 26+) | ~18% |
| Firefox | 139 (פברואר 2026) | תמיכה מלאה | ~3% |
| Samsung Internet | 23 (בסיס 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 ישן יותר, בלי תמיכה.
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); }
}
למה זה חשוב
- דפדפן ישן: המשתמש רואה את הכרטיסים מיד, בלי fade-in. לא מושלם אבל לגמרי usable.
- דפדפן מודרני: המשתמש מקבל את האנימציה המלאה.
- אם הופכים את הסדר (enhanced קודם,
@supports notלfallback) — דפדפן שלא תומך רואה opacity: 0 ולא רואה את הכרטיסים בכלל. זו כשלה קריטית של UX.
מפתחים כותבים את ה-@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 שכבות:
- משתמש עם
prefers-reduced-motion: reduce→ לא מקבל אנימציה - משתמש בדפדפן ישן → לא מקבל אנימציה
- משתמש בדפדפן מודרני בלי
reduced-motion→ מקבל את הניסיון המלא
Performance — מה לאנמט ומה לא
Scroll-driven animations native הרבה יותר מהירות מ-JavaScript equivalent — הדפדפן יכול לאופטם אותן, להפעיל אותן ב-compositor thread, ולא לחסום ה-main thread. אבל זה לא אומר שכל אנימציה תהיה מהירה. השילוב שבחרתם של properties ו-keyframes קובע.
הכלל של 2 ה-GPU-accelerated properties
יש שתי properties שהדפדפן יכול לאנמט ב-GPU בלי re-layout ובלי re-paint:
transform— translate, scale, rotate, skewopacity— שקיפות
כל השאר — width, height, top, left, margin, padding — יגרמו ל-layout reflow וכל פריים חדש ידרוש חישוב מחדש. על mobile זה מוריד FPS מ-60 ל-15-20.
| במקום זה (איטי) | תעשו את זה (מהיר) |
|---|---|
top: 40px → top: 0 | transform: translateY(40px) → translateY(0) |
left: 60px → left: 0 | transform: translateX(60px) → translateX(0) |
width: 80% → width: 100% | transform: scaleX(0.8) → scaleX(1) |
height: 200px → height: 400px | transform: 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 כלים ו-טכניקות ש-יחסכו לכם שעות:
- Chrome DevTools → Animations panel (F12 → three-dot menu → More tools → Animations). הפאנל הזה מראה כל אנימציה שרצה, כולל scroll-driven. אפשר להאט אותה (25%, 10%) כדי לראות בדיוק מה קורה בכל fram.
- Animation timeline inspector — בגרסאות חדשות של Chrome (115+), כשבוחרים אלמנט עם
animation-timeline, ה-Styles panel מראה את ה-progress הנוכחי של ה-timeline באחוזים. בזמן גלילה — תראו איך ה-0% → 100% קורה. - Performance recording בזמן scroll — F12 → Performance → Record → גללו → Stop. מחפשים "Composite Layers" ו-"Recalc Style". אם יש יותר מ-2-3 recalc per frame, יש בעיית performance.
- FPS meter — F12 → Rendering → Frame Rendering Stats. מראה FPS בזמן אמת. אם יורד מתחת ל-55 בזמן scroll — בעיה.
- "Paint flashing" — F12 → Rendering → Paint flashing. מסמן ירוק בכל מקום שה-browser מצייר מחדש. אם אנימציה שלכם מדליקה הרבה ירוק — אתם מאנמים properties שגורמות ל-repaint (כמו
background-color), לא רק לקומפוזיציה.
יש property ניסיוני ב-Chrome: animation-timeline-scope. הוא מאפשר להגדיר timeline ב-parent וליישם על children בלי לחזור על השם. זה עדיין experimental (אפריל 2026) — לא לשימוש production — אבל שווה לעקוב. זה יקצר את הקוד ב-30-40% ברגע שיתייצב.
Framework: CSS vs JavaScript — החלטה כנה
יש עולם שלם של ספריות JavaScript לאנימציות גלילה שעדיין חיות, משתמשות במיליוני אתרים, ולא הולכות להיעלם. ההחלטה "CSS או JS" לא דיכוטומית — החלטה נכונה משתמשת בשתיהן, כל אחת במקום שלה.
שאלו 5 שאלות בסדר הזה:
- האם האנימציה היא Reveal פשוט (fade / slide / scale) לאלמנט בודד כשהוא נכנס ל-viewport?
- כן → CSS
view()timeline (8 שורות, 0 JS)
- כן → CSS
- האם האנימציה היא Progress indicator או Parallax של שכבות?
- כן → CSS
scroll()timeline (12 שורות, 0 JS)
- כן → CSS
- האם האנימציה מערבת choreography בין כמה אלמנטים שונים שצריכים לנוע בסינכרון מדויק (למשל: text fade + image scale + background color change ביחד)?
- כן → GSAP + ScrollTrigger — עדיין הסטנדרט לזה. CSS לא נותן synchronization גסטורלי בין אלמנטים.
- האם אתם רוצים smooth scrolling (lerp, inertia) שהוא שונה מה-default של הדפדפן?
- כן → Lenis. אין CSS-native לזה. Lenis הוא 3KB, בטוח ומהיר.
- האם האנימציה מבוססת-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 כשכרטיס נכנס ל-viewport | CSS view() | 0 KB |
| Progress bar עליון | CSS scroll() | 0 KB |
| Parallax של 3 שכבות | CSS scroll() | 0 KB |
| Sticky nav שמחליף צבע | CSS scroll() + animation-range pixel-based | 0 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 animations | Framer Motion | ~60 KB |
הערה: ב-4 ה-use cases הראשונים (95% מהאתרים), לא צריך ספרייה בכלל. רק ב-cases 5-8 (אתרים מורכבים — awards sites, marketing immersive) GSAP/Lenis/Framer עדיין הכרחיים.
- בנו CodePen חדש עם section אחד של גובה 100vh. שם ה-class:
parallax-hero. - הוסיפו 3 שכבות:
.layer-bg(תמונת הרים/שמיים),.layer-mid(תמונת עננים PNG שקופה),.layer-fg(כותרתh1). - הדביקו את ה-CSS של 3 השכבות עם parallax (מסעיף 7.7).
- הוסיפו section נוסף למטה עם 150vh של תוכן כדי שתוכלו לגלול ולראות את האפקט.
- גללו באיטיות — האם רואים את 3 השכבות נעות במהירויות שונות?
- שנו את הערכים:
parallax-slowמ-20% ל-10%,parallax-fastמ--15% ל--5%. האם האפקט הפך עדין יותר? יותר דרמטי? - הוסיפו את ה-
@supports (animation-timeline: scroll())wrapper כדי שדפדפנים ישנים יראו rendering רגיל בלי parallax. - Expected output: CodePen עם parallax hero + fallback + 2 וריאציות (עדין/דרמטי). שמרו את הלינק.
animation-range Playground
15 דקות
- לכו ל-scroll-driven-animations.style/demos. מצאו את "View timeline: animation-range visualizer".
- נסו את כל 6 ה-ranges:
entry,exit,cover,contain,entry-crossing,exit-crossing. עבור כל אחד, ציירו (על נייר או על whiteboard) מה קורה ויזואלית. - עכשיו בנו ב-CodePen דף שיש בו 3 אלמנטים זהים, כל אחד עם
animation-rangeשונה:- Element A:
entry 0% entry 100% - Element B:
entry 0% cover 50% - Element C:
cover 0% exit 100%
- Element A:
- תנו לכולם את אותה אנימציית
fade-up. גללו באיטיות. איך כל אחד מהם שונה? - כתבו (על גבי הקוד או בתגובה) איזה range הכי מתאים ל-reveal "מהיר בכניסה", ואיזה ל-reveal "הדרגתי לאורך זמן".
- Expected output: CodePen עם 3 אלמנטים + הערה קצרה איזה range אתם אוהבים הכי הרבה. הבנה עמוקה של range = 50% מהפרק.
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.
- פתחו את כלי ה-AI המועדף עליכם (Claude / ChatGPT / Cursor / Copilot). בקשו: "Add scroll reveal animations to my cards". שמרו את התוצאה בצד.
- עכשיו בקשו שוב עם ה-Template המלא מהסעיף הזה. ציינו ספציפית: timeline = view(), range = entry 0% cover 40%, pattern = fade-up, fallback required. שמרו את התוצאה.
- השוו: איזה תוצאה קצרה יותר? מי משתמש ב-CSS native? איזה תוצאה כוללת fallback ו-
prefers-reduced-motion? - הריצו את שתי התוצאות ב-CodePen ובדקו על Chrome + Firefox + Safari. האם ה-JS version עובד בכל מקום? ה-CSS version?
- הוסיפו לתוצאה של Template שאלה נוספת: "Can you also add a parallax hero with 3 layers?". האם AI כעת מבין את השפה ונשאר ב-CSS?
- Expected output: שני קטעי CSS, השוואה של 3-5 שורות ביניהם, ומסקנה איזה Template אתם תאמצו לפרויקטים שלכם.
- Bonus: שמרו את ה-Template כ-snippet בתוך Cursor/VS Code — תקראו לו בקיצור דרך בכל פרויקט חדש.
יומית (בזמן עבודה על פרויקט)
- לפני שכותבים JS לאנימציית גלילה — 30 שניות של "האם זה אפשר ב-CSS
view()/scroll()?" הרוב המוחלט של המקרים: כן. - לפני שמעתיקים קוד מ-GSAP tutorial — בדקו אם יש version CSS-native של הדפוס ב-scroll-driven-animations.style.
- אחרי כל אנימציה חדשה — DevTools → Performance → 4x CPU throttling. האם ה-FPS יורד מתחת ל-50? אם כן — חזרו לסעיף 7.14 (performance).
- תמיד —
@media (prefers-reduced-motion: no-preference)wrapper. זה לא אופציונלי ב-2026.
שבועית
- בדקו את המכשיר האמיתי — לא רק DevTools. iPhone + Android ישן. ה-scroll animations נראות חלקות בשני הצדדים?
- בדקו ב-Safari — Safari תמיד היה האחרון לאמץ scroll-driven. אם יש לכם Mac, תריצו את האתר ב-Safari לפחות פעם בשבוע.
חודשית
- בדקו את Can I Use לפיצ'רים שהשתמשתם בהם — caniuse.com/css-animation-timeline. האם תמיכה עלתה? האם אפשר להוריד את ה-
@supportswrapper? - קראו את ה-changelog של Chrome / Safari / Firefox — חדשים כמו
animation-triggerמגיעים. כדאי לדעת מתי להוסיף לארסנל. - בדקו שה-JS libraries שנשארו לכם (GSAP, Lenis) עדיין נחוצים — אם ה-CSS native השתדרג, אולי זה הזמן להסיר ספרייה ולחסוך 30-40KB.
אם יש לכם זמן רק לדבר אחד מהפרק הזה, עשו את זה: קחו את הפרומפט Template מסעיף 7.16 ושמרו אותו כ-snippet בכלי ה-AI שלכם (Cursor, VS Code, Claude Project instructions). בפעם הבאה שתבקשו אנימציית גלילה — ה-Template הזה יחליף 30 שורות JavaScript עם 15 שורות CSS. זה ה-ROI הכי גדול בפרק. הכל השאר (animation-range, parallax, progress bar) — יגיע עם הזמן. ה-Template הזה משנה התנהגות של AI באופן מיידי.
פתחו את ה-IDE שלכם. צרו קובץ חדש: scroll-animation-template.md. הדביקו את ה-Template מסעיף 7.16. שמרו במיקום שנוח לכם (Desktop / Notes / Cursor snippets). בפעם הבאה שאתם מתחילים פרויקט חדש — פתחו אותו ועיקו לפרומפט הראשון שלכם.
פתחו את האתר הגנרי שAI בנה לכם (מפרקים קודמים). פתחו DevTools → Sources. חפשו: האם יש IntersectionObserver בקוד? window.scrollY? addEventListener('scroll', ...)? ספרו כמה שורות JS מוקדשות לאנימציות גלילה. אם יותר מ-20 שורות — זה פרויקט לשכתב ל-CSS.
בדקו caniuse.com/css-animation-timeline. רשמו את האחוז הגלובלי של תמיכה היום. השוו ל-95% המצוין בסעיף 7.12. גדול יותר? קטן יותר? אם גדול יותר — אולי אתם יכולים להוריד את ה-@supports wrapper באתרים חדשים.
פתחו apple.com. גללו לאט. ספרו כמה reveal animations רואים בעמוד. ספרו כמה parallax effects. הסתכלו ב-DevTools → Elements → בדקו class של אלמנט שעושה reveal. האם רואים animation-timeline? (רמז: Apple ב-2026 משתמשים ב-CSS scroll-driven ברוב המקרים.)
- מה ההבדל בין
animation-timeline: scroll()ל-animation-timeline: view()? (תשובה ב-sections 7.2-7.3 + framework 7.5) - מה המשמעות של
animation-range: entry 0% cover 40%, ולמה זה הסטנדרט המקצועי? (תשובה ב-section 7.4) - תנו שני use-cases שבהם GSAP עדיין עדיף על CSS native, ושני use-cases שבהם CSS native מספיק לגמרי. (תשובה ב-framework 7.15 + טבלה)
- איך מוודאים שאנימציית scroll-driven לא שוברת את האתר בדפדפן שלא תומך בה? (תשובה ב-section 7.13 —
@supportspattern) - למה מאנמים
transform: translateY(40px)במקוםtop: 40px? (תשובה ב-section 7.14 — GPU acceleration, no reflow)
אם עניתם ב-ביטחון על 4 מתוך 5 — אתם מוכנים לפרק הבא. אם פספסתם אחת — חזרו לסעיף הרלוונטי לפני ch8.
הפרק הזה הוא המפגש בין שתי נקודות מפנה: 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".
- הבנתם את ההבדל בין scroll-driven (progress תלוי בגלילה) לבין scroll-triggered (רץ בזמן שלו אחרי שנפתח)
- יודעים לבחור בין
animation-timeline: scroll()לביןanimation-timeline: view()עם framework 3-השאלות - כתבתם progress bar של 12 שורות CSS שעובד ב-CodePen — Exercise 2 הושלם
- בניתם reveal gallery עם 3 וריאציות (fade-up, slide-right, scale-in) + stagger — Exercise 1 הושלם
- בניתם parallax hero של 3 שכבות ב-CSS טהור — Exercise 3 הושלם
- התנסיתם בכל 6 ה-
animation-range(entry, exit, cover, contain, entry-crossing, exit-crossing) — Exercise 4 הושלם - שמרתם את ה-Scroll Animation Prompt Template שלכם כ-snippet לשימוש חוזר — Exercise 5 הושלם
- כל האנימציות שלכם עטופות ב-
@supports (animation-timeline: ...)עם fallback ברור - כל האנימציות שלכם עטופות ב-
@media (prefers-reduced-motion: no-preference) - בדקתם את ה-scroll animations על מכשיר mobile אמיתי (לא רק DevTools) — FPS נראה חלק
- רק
transformו-opacityמתאנמים — לאtop/left/width/height - יודעים לזהות מתי CSS מספיק ומתי עדיף GSAP / Lenis / Framer Motion (Framework 7.15)
- בדקתם את ה-browser support ב-Can I Use לאחרונה, ועדכנתם את ה-
@supportswrappers בהתאם - מוכנים לפרק 8 — View Transitions API