יצירת סרגל התקדמות מעוצב

הרכיב <progress> הוא הדרך הסמנטית לייצג את התקדמות ההשלמה של משימה, כמו הורדה או העלאה של מידע או השלמת חלק מתהליך רב-שלבי. שימוש ברכיב המקורי עוזר לנגישות, כי לקוראי מסך יש role="progressbar" משתמע והם יכולים להקריא ערכים.

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

סרגל התקדמות או מעגל התקדמות?

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

Markup

  1. להשתמש ברכיב <progress> מקורי: הוא מספק נגישות מובנית והתנהגות עקבית בפלטפורמה.
  2. אם אתם צריכים סולם שונה מסולם ברירת המחדל של 0-1, השתמשו ב-max.
  3. כדי להגדיר ערך התקדמות ספציפי, מגדירים מאפיין value. אפשר להשמיט את value במצב לא מוגדר (למשל, לפני שמתחיל משוב קונקרטי יותר).
  4. כדי לשייך את רכיב ההתקדמות לתווית, משתמשים ב-<label for="...">, ב-aria-labelledby או ב-aria-label.

איזה סוג של התאמה אישית נדרש?

כדי להגדיר עיצוב מוגבל, המאפיינים האלה מספיקים:

  • accent-color כדי לשנות את הצבע של סרגל ההתקדמות
  • ‫width או inline-size כדי לשנות את הרוחב של סרגל ההתקדמות
  • ‫height/block-size לא חל באופן עקבי – צריך לבדוק קודם
  • writing-mode: vertical-lr לעמודות אנכיות.
  • אפקטים שמוחלים אחרי הציור: opacity,‏ filter,‏ clip-path, מסכות, טרנספורמציות, box-shadow וכו'.

אם עיצוב מוגבל מספיק, אפשר לדלג על הקטע הבא.

אזהרה: מאפיינים מסוימים (למשל border-radius,‏ background,‏ border) ישביתו את העיצוב של סרגל ההתקדמות שמוגדר כברירת מחדל בלי להציג הודעה. אם אתם רוצים להוסיף עיצוב מעבר למאפיינים שלמעלה, אתם צריכים להטמיע עיצוב בהתאמה אישית מלאה, כמו שמתואר בקטע הבא. אל תנסו להוסיף עיצוב נרחב יותר "רק למקרה שזה יעבוד".

עיצוב בהתאמה אישית מלאה

1. הגדרה בסיסית

  • חובה: צריך להשתמש ב-appearance: none כדי להפעיל סגנון אישי.
  • איפוס הגבולות של UA עם border: 0 solid
  • כדי לחתוך את הסרגל הפעיל לפי רדיוס הגבול של הרכיב, צריך להשתמש ב-overflow: clip או להחיל את אותו border-radius על הסרגל. אל תשתמשו ב-border-radius: inherit בסרגל, כי הוא תלוי במבנה הספציפי של הרכיבים ב-shadow DOM.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. עיצוב

  • מעצבים את הרצועה ברכיב progress עצמו.
  • משתמשים ברכיב המדומה (pseudo-element) המתאים שמטרגט את הדפדפן כדי לטרגט את סרגל ההתקדמות עצמו: ::-webkit-progress-value ל-WebKit/Blink ו-::-moz-progress-bar ל-Firefox.
  • חובה: אל תקבוצו את הרכיבים המדומים -webkit- ו--moz- באותו כלל. הכלל ייחשב כלא תקין והמערכת תתעלם ממנו. :is() ו-:where() לא עוזרים כאן, כי הם לא תומכים ברכיבי פסאודו.
  • כל רכיב מדומה שמטרגט דפדפן צריך להופיע פעם אחת בדיוק. התאמות אישיות ספציפיות למצב (כמו גודל הקטע הלא מוגדר והאנימציה) צריכות לעבור דרך מאפיינים מותאמים אישית שמוגדרים ברכיב progress עצמו.
  • אופציונלי: מעבר inline-size במילוי כדי שהשינויים בערך המוגדר יונפשו במקום לקפוץ. ב-Firefox, המילוי מוגדר לפי גודל הפריסה והמערכת מתעלמת ממנו, וזה לא מזיק.
progress {
  --progress-bar-radius: 1e5px;
  border-radius: var(--progress-bar-radius);
  /* Apply color for custom progress track (background) */
  background: var(--progress-bar-track, var(--color-neutral-90));
  /* Subtle depth on the empty part of the track */
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
}

/* Let <progress> styling show through */
progress::-webkit-progress-bar {
  background: transparent;
}

/* Webkit, Chrome */
progress::-webkit-progress-value {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

/* Firefox */
progress::-moz-progress-bar {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

3. עיצוב של מצב לא מוגדר

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

  • צריך לעצב את המילוי עצמו, ולא ליצור אותו מחדש כרקע ב-progress.
  • המילוי הלא מוגדר קיים רק דרך האנימציה שלו: הפריימים המרכזיים מגדירים את האורך שלו באמצעות inline-size אמיתי (זה עדיף על inline-size בתוך השורה של סוכן המשתמש של Chromium) ומזיזים אותו באמצעות translate. אל תשתמשו ב-scale לאורך, כי זה מעוות את התבנית שמצוירת על המילוי.
  • אחוזי translate נפתרים ביחס לרוחב של המילוי עצמו, לכן צריך לבטא מיקומים ביחידות של טראק במקום זאת: הופכים את progress למאגר בגודל מוטבע ומשתמשים ב-cqi בפריימים המרכזיים. צריך לכלול את margin-right: auto ב-from, to כדי ש-Chromium יציג את המילוי בצד שמאל הפיזי גם ב-RTL.
  • כדי להזיז את המילוי, משתמשים בטרנספורמציות ולא בשוליים.
    • הטרנספורמציות שומרות את האנימציה ב-compositor (אלא אם גם inline-size מונפש)
    • השוליים לא מוצגים כמו שצריך ב-RTL, משתי סיבות נפרדות:
    • דפדפן Firefox ממקם את המילוי הלא מוגדר באמצעות השוליים הפיזיים השמאליים בלבד, כך של-margin-inline-start אין השפעה על כתיבה מימין לשמאל והמילוי אף פעם לא זז.
    • ב-Chromium וב-WebKit, שבהם הפריסה של המילוי היא כבלוק רגיל, ברגע שהשוליים דוחפים את המילוי מעבר לסוף המסלול, השוליים מצטברים ליותר מהמסלול. ב-CSS רגיל, הבעיה נפתרת על ידי התעלמות מ-margin-left ב-RTL, כך שהקטע קופץ לקצה במקום להחליק ממנו.
    • המאפיין translate מוחל אחרי הפריסה, ולכן שתי הבעיות לא רלוונטיות.
  • כדי לשקף את התנועה ב-RTL (כמו בסרגל המקורי של Chrome), צריך להפוך את האנימציה בקטע :dir(rtl):
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • מגדירים את המאפיינים הציבוריים של --progress-bar-indeterminate-* ב-progress (השם שלהם כבר מגדיר את ההיקף שלהם למצב) כדי שהצרכנים יוכלו לשנות אותם בלי לדעת על :indeterminate.
  • כדי לשנות את אורך הקטע (למשל, הקצה המוביל נע קדימה והקצה הנגרר מדביק את הפער), מגדירים את --progress-bar-indeterminate-animation לשתי אנימציות בו-זמניות: אחת שבה הקיפריימים מנפישים את translate ב-cqi (מיקום) ואחת שבה הקיפריימים מנפישים את inline-size (אורך), כך שכל אחת מקבלת קיפריימים ופונקציית easing משלה. אל תנסו לעצב את שני הקצוות ברשימת פריים-מפתח אחת. animation-timing-function לכל פריים-מפתח חל על כל מאפיין בפריים-המפתח הזה.
  • הערך --progress-bar-animation חל בשני המצבים, ואפשר להשתמש בו לאפקטים כמו נצנוץ מוגדר. המאפיין revert-rule משמש לשינוי רק כשהוא מוגדר, כדי למקסם את יכולת ההרכבה.
  • אף פעם אל תשביתו את האנימציה הלא מוגדרת (animation: none משאיר מילוי ברוחב אפס ב-Chromium). כדי להפחית את התנועה, אפשר להאט אותה.
progress {
  /* Lets the fill position itself in track units (cqi) */
  container-type: inline-size;
  /* Segment length relative to the track */
  --progress-bar-indeterminate-size: 30%;
  --progress-bar-indeterminate-animation-duration: 1.5s;

  /* Respect prefers-reduced-motion */
  @media (prefers-reduced-motion: reduce) {
    --progress-bar-indeterminate-animation-duration: 10s;
  }
}

progress:indeterminate {
  --progress-bar-animation: var(
      --progress-bar-indeterminate-animation,
      progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear
    );
}

/* Add to the existing pseudo-element rules */
progress::-webkit-progress-value {
  animation: var(--progress-bar-animation, revert-rule);
}

progress::-moz-progress-bar {
  animation: var(--progress-bar-animation, revert-rule);
}

/* Physical: left to right regardless of direction (reverse under :dir(rtl) to mirror). */
@keyframes progress-bar-indeterminate {
  from, to {
    inline-size: var(--progress-bar-indeterminate-size);
    /* Pin to the physical left; Chromium would otherwise lay the fill out at the inline start */
    margin-right: auto;
  }
  from { translate: -100%; } /* Own width: fully outside the left edge */
  to { translate: 100cqi; }  /* Track width: fully outside the right edge */
}

שיקולים נוספים בנושא נגישות

בנוסף להנחיות שצוינו למעלה:

  • הכרזה על השלמה בלי להעביר את המיקוד: אל תגדירו את tabindex ואל תפעילו את .focus() ב-<progress>, כי הוא לא אינטראקטיבי והעברת המיקוד אליו מפריעה למשתמשים במקלדת. כדי להודיע כשמשימה ברקע מסתיימת, מעדכנים רכיב סטטוס מנומס (role="status" או aria-live="polite").
  • אל תוסיפו טקסט חלופי בתוך <progress>: דפדפנים מודרניים מתעלמים מטקסט בתוך <progress>, וטכנולוגיות מסייעות לא משתמשות בו.
  • הימנעו משימוש ב-aria-busy + aria-describedby ב-<div> כללי ללא תווית. התמיכה של aria-busy בקורא מסך לא עקבית, וטכנולוגיות מסייעות מתעלמות מ-aria-describedby ב-<div> ללא שם וללא תפקיד. אם התוכן של אזור מסוים לא מוכן בזמן הטעינה, צריך להסתיר אותו באמצעות hidden או aria-hidden="true".
  • חשוב לוודא שיש ניגודיות מתאימה בין סרגל ההתקדמות לבין הרצועה כשמוסיפים סגנונות בהתאמה אישית.
  • שקלו חלופות לייצוג מצב לא מוגדר: אנימציות של סרגל התקדמות לא מוגדר שמוצגות במקביל לתוכן אחר יכולות להיחשב כמסיחות את הדעת עבור חלק מהמשתמשים.

אסטרטגיות חלופיות

תמיכה בדפדפן וחלופות ל-accent-color

המאפיין accent-color הוא שיפור הדרגתי (progressive enhancement). דפדפנים שלא תומכים במאפיין הזה יתעלמו ממנו וישתמשו בצבעי ברירת המחדל של ממשק המשתמש שלהם.

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

תמיכה בדפדפן וחלופות ל-revert-rule

אם אין תמיכה ב-revert-rule, animation: var(--progress-bar-animation, revert-rule) חוזר ל-animation-name: revert-rule עם משך זמן של 0s כש---progress-bar-animation לא מוגדר, וזו פעולה לא מזיקה כשאין @keyframes revert-rule.