scroll-state()‎ ב-CSS

כמו שאילתות בקונטיינר, אבל לשאילתות תקועות, שאילתות עם צילום מסך ושאילתות עם עומס יתר.

תאריך פרסום: 15 בינואר 2025

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

סקירה כללית

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

לפניכם סקירה כללית על שאילתות המצב הזמינות מ-Chrome 133:

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

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

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

(שמאל) אנימציה שהופעל על ידי scroll-state(),‏ (ימין) אנימציה שמבוססת על גלילה
https://codepen.io/web-dot-dev/pen/emOrBaV

השאילתה הראשונה לגבי מצב גלילה

השלב הראשון הוא להגדיר את המאגר, באמצעות ערך חדש למאפיין container-type. בדומה לשאילתה של מאגר, הרכיב שרוצים לשלוח עליו שאילתה הוא זה שנותנים לו את הערך container-type ואת הערך container-name (אופציונלי). בשאילתות של מצב גלילה, נותנים לרכיב שנמצא בסטטוס 'התאמה אוטומטית', 'תקוע' או 'חרג מעבר למסך' את הערך container-type: scroll-state.

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;
}

השלב השני הוא לבחור את הצאצא של הקונטיינר הזה שישיב למצב. בדומה לשאילתות של קונטיינרים, זה לא יכול להיות אותו אלמנט שיש בו את container-type.

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  > nav {
    @container scroll-state(stuck: top) {
      background: Highlight;
      color: HighlightText;
    }
  }
}

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

https://codepen.io/web-dot-dev/pen/ByBxpwR

שיפור הדרגתי

בעזרת כלל at-rule‏ @supports והעיצוב בתצוגת עץ, אפשר להוסיף שיפורים הדרגתיים או שימוש בתכונות מותנות בכמה שורות קוד בלבד:

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  @supports (container-type: scroll-state) {
    > nav {
      @container scroll-state(stuck: top) {
        background: Highlight;
        color: HighlightText;
      }
    }
  }
}

בנוסף, אם אתם משתמשים באנימציה של רכיבים בדף באמצעות שאילתות של מצב גלילה, חשוב לזכור להשתמש ב-@media (prefers-reduced-motion: no-preference) {} סביב התנועה.

תרחישים לדוגמה

ללא שינוי

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

@container scroll-state(stuck: top) {}
@container scroll-state(stuck: bottom) {}

רשימת תחביר מלאה

הוספת צל כאשר נתקעים

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

https://codepen.io/web-dot-dev/pen/GgKdryj
.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  > nav {
    transition: box-shadow .3s ease;

    @container scroll-state(stuck: top) {
      box-shadow: var(--shadow-5);
    }
  }
}

הפעלת הכותרת הנוכחית שנתקעה

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

https://codepen.io/web-dot-dev/pen/pvzVRaK
.sticky-slide {
  dt {
    container-type: scroll-state;
    position: sticky;
    inset-block-start: 0;
    inset-inline: 0;

    > header {
      transition: 
        background .3s ease,
        box-shadow .5s ease;

      @container scroll-state(stuck: top) {
        background: hsl(265 100% 27%);
        box-shadow: 0 5px 5px #0003;
      }
    }
  }
}

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

https://codepen.io/web-dot-dev/pen/azoGpGg

רעיונות נוספים

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

סיכום שנתי בתמונות

בעזרת שאילתות של מצב מוצמד, אנחנו יכולים להסיר חלק מהאחריות מ-JavaScript ומ-Snap Events ולהעביר את הטיפול ל-CSS.

@container scroll-state(snapped: x) {}
@container scroll-state(snapped: y) {}
@container scroll-state(snapped: inline) {}
@container scroll-state(snapped: block) {}

רשימת תחביר מלאה

תזכורת קטנה, למקרה שדילוגתם על הקטע השאילתה הראשונה של מצב גלילה, המארז של שאילתה של הצמדה הוא האלמנט עם scroll-snap-align, והאלמנט שיכול להתאים את עצמו חייב להיות צאצא של האלמנט הזה. כלומר, יש שלושה רכיבים שצריך להגדיר כדי לעשות זאת:

a scroll container with `scroll-snap-type`
⤷ a snap target with both `scroll-snap-align` and `container-type: scroll-state`
    ⤷ a child of the snap target that can query the container for snap state

שיפור המראה של הפריט שצולמו

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

https://codepen.io/web-dot-dev/pen/NPKMdBX
.demo {
  overflow: auto hidden;
  scroll-snap-type: x mandatory;

  > article {
    container-type: scroll-state;
    scroll-snap-align: center;

    @supports (container-type: scroll-state) {
      > * {
        transition: opacity .5s ease;

        @container not scroll-state(snapped: x) {
          opacity: .25;
        }
      }
    }
  }
}

הצגת הכותרת של הפריט שנצמד

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

https://codepen.io/web-dot-dev/pen/XJrqpBG
.demo {
  overflow-x: auto;
  scroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  > .card {
    container-type: scroll-state;
    scroll-snap-align: center;

    @supports (container-type: scroll-state) {
      @media (prefers-reduced-motion: no-preference) {
        figcaption {
          transform: translateY(100%);

          @container scroll-state(snapped: x) {
            transform: translateY(0);
          }
        }
      }
    }
  }
}

אנימציה ברכיבי השקף

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

https://codepen.io/web-dot-dev/pen/dPbeNqY
html {
  scroll-snap-type: y mandatory;
}

section {
  container-type: scroll-state;
  scroll-snap-align: start;
  scroll-snap-stop: always;

  @supports (container-type: scroll-state) {
    @media (prefers-reduced-motion: no-preference) {
      > h1 {
        transition: opacity .5s ease, transform .5s var(--ease-spring-3);
        transition-delay: .5s;
        opacity: 0;
        transform: scale(1.25);

        @container scroll-state(snapped: block) {
          opacity: 1;
          transform: scale(1);
        }
      }
    }
  }
}

יכול להיות שתבחינו שכל השאילתות של מצב CSS שנשמרו מתנהגות כמו scrollsnapchanging, בניגוד ל-scrollsnapchange. כך תקבלו את האפשרות המוקדם ביותר לספק משוב חזותי לגבי הרכיב שנצמד. אם האירוע מתרחש מוקדם מדי, כדאי להשתמש באירוע JavaScript.

ניתן לגלילה

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

@container scroll-state(scrollable: top) {}
@container scroll-state(scrollable: right) {}
@container scroll-state(scrollable: bottom) {}
@container scroll-state(scrollable: left) {}

רשימת תחביר מלאה

סימון גלילה באמצעות צללים

יש טריק ידוע ב-CSS של Lea Verou שמשתמש ב-background-attachment: local כדי להשיג אפקט דומה, וגם דרך לעשות זאת באמצעות אנימציה שמבוססת על גלילה. לכל שיטה יש יתרונות וחסרונות, ועלינו לבדוק מתי ואיפה כל אחת מהשיטות האלה מתאימה ביותר.

בדוגמה הבאה נעשה שימוש ברכיב דביק אחד שמשתרע על פני חלון הגלילה. כשהשאילתה של מצב הגלילה לפי הקשר חלה על העקומה בחלק העליון ועל העקומה בחלק התחתון, השקיפות שלהן מונפשת באמצעות @property: @container scroll-state(scrollable: top).

שימו לב גם שזהו המאגר הראשון שהוא גם מאגר size וגם מאגר scroll-state.

https://codepen.io/web-dot-dev/pen/OPLZWBj
.scroll-container {
  container-type: scroll-state size;
  overflow: auto;

  &::after {
    content: " ";

    background: var(--_shadow-top), var(--_shadow-bottom);
    transition: 
      --_scroll-shadow-color-1-opacity .5s ease,
      --_scroll-shadow-color-2-opacity .5s ease;

    @container scroll-state(scrollable: top) {
      --_scroll-shadow-color-1-opacity: var(--_shadow-color-opacity, 25%);
    }

    @container scroll-state(scrollable: bottom) {
      --_scroll-shadow-color-2-opacity: var(--_shadow-color-opacity, 25%);
    }
  }
}

הנחיה עם חץ

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

https://codepen.io/web-dot-dev/pen/OPLZWBj
@container scroll-state((scrollable: top) or (not (scrollable: bottom))) {
  translate: 0 calc(100% + 10px);
}

@container scroll-state((scrollable: top) and (not (scrollable: bottom))) {
  translate: 0 calc(100% + 10px);
  rotate: .5turn;
}

חזרה לראש הדף

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

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

https://codepen.io/web-dot-dev/pen/OPLZWBj
@container not scroll-state(scrollable: top) {
  translate: 0 calc(100% + 10px);
}

המשך המחקר

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