אנימציה לגובה: auto; (ומילות מפתח אחרות של שינוי גודל מהותי) ב-CSS

אפשר להשתמש במאפיין interpolate-size או בפונקציה calc-size() כדי להפעיל מעברים ואנימציות חלקות מאורכים למילות מפתח עם שינוי גודל מובנה, ולהפך.

תאריך פרסום: 17 בספטמבר 2024

מבוא

תכונה של CSS שמופיעה לעיתים קרובות בבקשות היא היכולת ליצור אנימציה ל-height: auto. וריאציה קלה על הבקשה הזו היא מעבר של המאפיין width במקום height, או מעבר לכל אחד מהגדלים המובנים האחרים שמיוצגים על ידי מילות מפתח כמו min-content,‏ max-content ו-fit-content.

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

קוד ה-CSS שבו נעשה שימוש הוא:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease; /* 👈 Transition the width */

    &:hover,
    &:focus-visible {
        width: max-content; /* 👈 Doesn't work with transitions */
    }
}

למרות ש-transition מוגדר להעביר את הנכס width ו-width: auto מוגדר ב-:hover, לא מתרחש מעבר חלק. במקום זאת, השינוי הוא פתאומי.

אנימציה של מעבר ממילות מפתח עם גודל מותאם אישית אל מילות מפתח עם גודל מובנה וחזרה מהן באמצעות interpolate-size

תמיכה בדפדפנים

  • Chrome: ‏ 129.
  • Edge: לא נתמך.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

הנכס interpolate-size ב-CSS מאפשר לכם לקבוע אם תהיה אפשרות להשתמש באנימציות ובמעברים של מילות מפתח פנימיות של CSS לבחירת גודל.

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

לפי המפרט:

  • numeric-only: לא ניתן לבצע אינטרפולציה של <intrinsic-size-keyword>.
  • allow-keywords: אפשר לבצע אינטרפולציה של שני ערכים אם אחד מהם הוא <intrinsic-size-keyword> והשני הוא <length-percentage>. […]

מכיוון שנכס interpolate-size הוא נכס שעובר בירושה, אפשר להצהיר עליו ב-:root כדי לאפשר מעבר למילות מפתח של תצוגה בגודל טבעי ומהן, לכל המסמך. זו הגישה המומלצת.

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

בדמו הבא, הכלל הזה מתווסף לקוד. כתוצאה מכך, האנימציות אל width: auto וממנו פועלות כמו שצריך (בדפדפנים עם תמיכה):

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

אם רוצים להגביל את ההסכמה ל-allow-keywords רק להסתעפות עץ משנית של המסמך, משנים את הבורר מ-:root רק לרכיב שרוצים לטרגט. לדוגמה, אם הרכיב <header> בדף לא תואם לסוגים האלה של מעברים, אפשר להגביל את ההסכמה רק לרכיב <main> ולצאצאיו באופן הבא:

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

למה לא מאפשרים אנימציה ממילת מפתח לבחירת גודל וממנה כברירת מחדל?

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

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

לכן, הנכס הוא נכס שבו צריך להביע הסכמה. בזכות מאפיין הירושה, הסכמה למסמך שלם היא רק הצהרת interpolate-size: allow-sizes ב-:root, כפי שמפורט למעלה.

אנימציה של מעבר ממילות מפתח עם גודל מותאם אישית אל מילות מפתח עם גודל מובנה וחזרה מהן באמצעות calc-size()

תמיכה בדפדפנים

  • Chrome: ‏ 129.
  • Edge: ‏ 129.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

דרך נוספת להפעיל אינטרפולציה אל מילת מפתח עם שינוי גודל מובנה וממנה היא להשתמש בפונקציה calc-size(). הוא מאפשר לבצע פעולות מתמטיות על גדלים מהותיים בצורה בטוחה ומוגדרת היטב.

הפונקציה מקבלת שני ארגומנטים, לפי הסדר:

  • בסיס calc-size, שיכול להיות <intrinsic-size-keyword> אבל גם calc-size() בתצוגת עץ.
  • חישוב calc-size, שמאפשר לבצע חישובים על בסיס calc-size. כדי להפנות לבסיס calc-size, משתמשים במילות המפתח size.

הנה כמה דוגמאות:

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

הוספת calc-size() לדמו המקורי, הקוד נראה כך:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

מבחינה חזותית, התוצאה זהה לזו שמתקבלת כשמשתמשים ב-interpolate-size. לכן במקרה הספציפי הזה צריך להשתמש ב-interpolate-size.

היתרון של calc-size() הוא היכולת לבצע חישובים, דבר שלא ניתן לעשות באמצעות interpolate-size:

width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5);   // = Half the max-content width

לדוגמה, אם רוצים שהגודל של כל הקטעים בדף יהיה מכפיל של 50px הקרוב ביותר, אפשר להשתמש בקוד הבא:

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

נוסחת calc-size() מאפשרת גם לבצע אינטרפולציה בין שתי נוסחאות calc-size() כששתי הבסיסות שלהן לחישוב הגודל זהות. גם זה לא ניתן להשיג באמצעות interpolate-size.

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

למה לא לאפשר את <intrinsic-size-keyword> ב-calc()?

שאלה נפוצה לגבי calc-size() היא למה קבוצת העבודה של CSS לא שינתה את הפונקציה calc() כדי לתמוך במילות מפתח עם הגדרת גודל מובנית.

אחת הסיבות לכך היא שאסור לשלב בין מילות מפתח לבחירת גודל מובנית כשמבצעים חישובים. לדוגמה, יכול להיות שתתפתו לכתוב calc(max-content - min-content) שנראה תקין, אבל בפועל הוא לא תקין. הפונקציה calc-size() אוכפת את הנכונות כי, בניגוד ל-calc(), היא מקבלת רק <intrinsic-size-keyword> אחד בתור הארגומנט הראשון שלה.

סיבה נוספת היא התאמה להקשר. לאלגוריתמים מסוימים של פריסה יש התנהגות מיוחדת למילות מפתח ספציפיות לבחירת גודל מודעה מובנית. calc-size() מוגדר במפורש לייצוג גודל מהותי, ולא <length>. כך, האלגוריתמים האלה יכולים להתייחס ל-calc-size(<intrinsic-size-keyword>, …) כאל <intrinsic-size-keyword>, תוך שמירה על ההתנהגות המיוחדת של מילת המפתח הזו.

באיזו גישה כדאי להשתמש?

ברוב המקרים, מגדירים את interpolate-size: allow-keywords ב-:root. זו הדרך הפשוטה ביותר להפעיל אנימציה אל מילות מפתח עם שינוי גודל פנימי ומהן, כי היא בעצם שורה אחת.

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

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

כשצריך שליטה ברמת פירוט גבוהה יותר – למשל, ביצוע חישובים – או כשרוצים להשתמש בהתנהגות שרק calc-size() יכול לבצע, אפשר להשתמש ב-calc-size().

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

עם זאת, כדי להשתמש ב-calc-size() בקוד, תצטרכו לכלול חלופות לדפדפנים שלא תומכים ב-calc-size(). לדוגמה, הוספת הצהרות נוספות לגבי גודל או חזרה לזיהוי תכונות באמצעות @supports.

width: fit-content;
width: calc-size(fit-content, size + 1em);
       /* 👆 Browsers with no calc-size() support will ignore this second declaration,
             and therefore fall back to the one on the line before it. */

הדגמות נוספות

הנה כמה הדגמות נוספות שבהן נעשה שימוש ב-interpolate-size: allow-keywords.

התראות

הדגמה הבאה היא גרסת פורק (fork) של הדגמה הזו של @starting-style. הקוד הותאם כדי לאפשר הוספה של פריטים בגבהים שונים.

כדי לעשות זאת, צריך להביע הסכמה לשימוש בהשתנות של מילות מפתח לפי גודל בכל הדף, ולהגדיר את הערך של height בכל אלמנט .item כ-auto. אחרת, הקוד יהיה זהה לקוד שהיה לפני היצירה של ההסתעפות.

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

    @starting-style {
        height: 0px;
    }
}

הנפשת הרכיב <details>

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

בעזרת interpolate-size: allow-keywords אפשר להגיע רחוק:

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Clip off contents while animating */
        }
    }
}

עם זאת, כפי שאפשר לראות, האנימציה פועלת רק כשחלונית הווידג'ט של גילוי הנאות נפתחת. כדי לטפל בבעיה הזו, אנחנו עובדים ב-Chrome על ::details-content pseudo שיגיע ל-Chrome בהמשך השנה (ונתאר אותו בפרסום עתידי). שילוב של interpolate-size: allow-keywords ו-::details-content מאפשר ליצור אנימציה בשני הכיוונים: