סקירה כללית בסיסית על בניית תצוגת גלילה אופקית רספונסיבית לטלוויזיות, לטלפונים, למחשבים וכו'.
בפוסט הזה אני רוצה לשתף רעיונות ליצירת חוויות גלילה אופקית באינטרנט שהן מינימליות, רספונסיביות, נגישות ופועלות בדפדפנים ובפלטפורמות שונות (כמו טלוויזיות!). כדאי לנסות את ההדגמה.
אם אתם מעדיפים לצפות בסרטון, הנה גרסת YouTube של הפוסט הזה:
סקירה כללית
אנחנו ניצור פריסה עם גלילה אופקית, שתיועד לאירוח תמונות ממוזערות של מדיה או מוצרים. הרכיב מתחיל כרשימה פשוטה <ul>
אבל הוא עובר טרנספורמציה באמצעות CSS לחוויית גלילה חלקה ונעימה, שבה מוצגות תמונות שמתיישרות עם רשת. הוספנו JavaScript כדי להקל על אינטראקציות של אינדקסים נעים, וכך לעזור למשתמשים במקלדת לדלג על מעבר בין יותר מ-100 פריטים.
בנוסף, נעשה שימוש בשאילתת מדיה ניסיונית, prefers-reduced-data
, כדי להפוך את רכיב המדיה לגלולת כותרות קלילה.
מתחילים עם תגי עיצוב נגישים
רכיב גלילה של מדיה מורכב רק מכמה רכיבי ליבה, רשימה עם פריטים. רשימה, בצורתה הפשוטה ביותר, יכולה לעבור בכל העולם ולהיות ברורה לכולם. משתמש שמגיע לדף הזה יכול לעיין ברשימה וללחוץ על קישור כדי לראות פריט. זהו בסיס נגיש.
העברת רשימה עם רכיב <ul>
:
<ul class="horizontal-media-scroller">
<li></li>
<li></li>
<li></li>
...
<ul>
הופכים את הפריטים ברשימה לאינטראקטיביים באמצעות רכיב <a>
:
<li>
<a href="#">
...
</a>
</li>
משתמשים ברכיב <figure>
כדי לייצג באופן סמנטי תמונה ואת הכיתוב שלה:
<figure>
<picture>
<img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
</picture>
<figcaption>Legends</figcaption>
</figure>
שימו לב למאפיינים alt
ו-loading
ב-<img>
. טקסט חלופי לגלגל מדיה הוא הזדמנות לשיפור חוויית המשתמש. הוא יכול לספק הקשר נוסף לתמונה הממוזערת, לשמש כטקסט חלופי אם התמונה לא נטענה, או לספק ממשק משתמש קולי למשתמשים שמסתמכים על טכנולוגיה מסייעת כמו קורא מסך. מידע נוסף על חמישה כללים חשובים ליצירת טקסט חלופי שעומד בדרישות
במאפיין loading
אפשר להשתמש במילת המפתח lazy
כדי לציין שיש לאחזר את מקור התמונה הזה רק כשהתמונה נמצאת באזור התצוגה. זה יכול להיות שימושי מאוד לרשימות גדולות, כי המשתמשים יורידו רק את התמונות של הפריטים שהם גללו כדי לראות.
תמיכה בהעדפות של המשתמש לגבי ערכת הצבעים
משתמשים בתג color-scheme
בתור תג <meta>
כדי לציין לדפדפן שהדף רוצה את הסגנונות של סוכן המשתמש שסופקו גם למצב בהיר וגם למצב כהה. זהו מצב כהה או מצב בהיר, תלוי איך מסתכלים על זה:
<meta name="color-scheme" content="dark light">
תג ה-meta מספק את האות המוקדם ביותר שאפשר, כדי שהדפדפן יוכל לבחור צבע כהה כברירת מחדל לרקע אם המשתמש מעדיף עיצוב כהה. המשמעות היא שבמהלך הניווט בין דפי האתר לא יוצג רקע לבן בין הטעינות. העיצוב הכהה נטען בצורה חלקה, ונעים יותר לעיניים.
במאמר הזה של Thomas Steiner בכתובת https://web.dev/color-scheme/ אפשר לקרוא מידע נוסף.
הוספת תוכן
בהתחשב במבנה התוכן של ul > li > a > figure > picture > img
שמוצג למעלה, המשימה הבאה היא להוסיף תמונות וכותרות שאפשר לגלול ביניהן. הדמו כולל תמונות וטקסט סטטיים של placeholder, אבל אתם יכולים להשתמש במקור הנתונים המועדף עליכם.
הוספת סגנון באמצעות CSS
עכשיו שירות ה-CSS לוקח את רשימת התוכן הכללית הזו והופך אותה לחוויה. ב-Netflix, בחנויות אפליקציות ובאתרים ובאפליקציות רבים אחרים יש אזורים עם גלילה אופקית כדי להציג את אזור התצוגה עם קטגוריות ואפשרויות.
יצירת פריסת הגלילה
חשוב להימנע מחיתוך של תוכן בפריסות או מהסתמכות על חיתוך טקסט עם שלוש נקודות. בטלוויזיות רבות יש סרגלי גלילה של מדיה כמו זה, אבל לעיתים קרובות מדי התוכן מוצג עם סימן של שלוש נקודות. לא בפריסה הזו. בנוסף, אפשר להגדיר שתוכן המדיה יבטל את גודל העמודה, כך שפריסה אחת תהיה גמישה מספיק כדי להתאים לשילובים מעניינים רבים.
הכלי המכיל מאפשר לשנות את גודל העמודה על ידי הגדרת גודל ברירת המחדל כמאפיין מותאם אישית. פריסת הרשת הזו מוגדרת מראש לגבי גודל העמודות, והיא מנהלת רק את הריווח והכיוון:
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
margin: 0;
}
לאחר מכן, המאפיין המותאם אישית משמש את רכיב <picture>
כדי ליצור את יחס הגובה-רוחב הבסיסי שלנו: תיבה:
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
& picture {
inline-size: var(--size);
block-size: var(--size);
}
}
כדי להשלים את השלד של רכיב המדיה לגלגול, מוסיפים עוד כמה סגנונות משניים:
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
& > li {
display: inline-block; /* removes the list-item bullet */
}
& picture {
inline-size: var(--size);
block-size: var(--size);
}
}
ההגדרה overflow
מאפשרת גלילה וניווט במקלדת ברשימה של <ul>
, ואז ההגדרה ::marker
מוסרת מכל רכיב צאצא ישיר <li>
על ידי קבלת סוג תצוגה חדש של inline-block
.
אבל התמונות עדיין לא רספונסיביות, והן יוצאות מהמסגרות שלהן. אפשר לשלוט בהם באמצעות גדלים, התאמה וסגנונות גבולות, וגם באמצעות מעבר צבעים ברקע כשמדובר בטעינה עצלה:
img {
/* smash into whatever box it's in */
inline-size: 100%;
block-size: 100%;
/* don't squish but do cover the space */
object-fit: cover;
/* soften the edges */
border-radius: 1ex;
overflow: hidden;
/* if empty, show a gradient placeholder */
background-image:
linear-gradient(
to bottom,
hsl(0 0% 40%),
hsl(0 0% 20%)
);
}
מרווח פנימי לגלילה
התאמה לתוכן הדף, בנוסף לשטח גלילה מקצה לקצה, חיונית ליצירת רכיב הרמוני ומינימלי.
כדי ליצור פריסת גלילה מקצה לקצה שתואמת לטיפוגרפיה ולפריסת הקווים שלנו, צריך להשתמש ב-padding
שתואם ל-scroll-padding
:
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
padding-inline: var(--gap);
scroll-padding-inline: var(--gap);
padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}
תיקון באג של ריווח בגלילה אופקית בדוגמה שלמעלה אפשר לראות כמה קל להוסיף ריווח למאגר גלילה, אבל יש בעיות תאימות שלא נפתרו (הבעיות האלה תוקנו ב-Chromium 91 ומעלה). כאן אפשר לקרוא קצת על ההיסטוריה, אבל הגרסה הקצרה היא שבעבר לא תמיד נלקח בחשבון ריווח פנימי בתצוגת גלילה.
כדי לגרום לדפדפנים להוסיף את הריווח בסוף אזור הגלילה, אטרגט את הדמות האחרונה בכל רשימה ואוסיף פסאודו-אלמנט ששווה לריווח הרצוי.
.horizontal-media-scroller > li:last-of-type figure {
position: relative;
&::after {
content: "";
position: absolute;
inline-size: var(--gap);
block-size: 100%;
inset-block-start: 0;
inset-inline-end: calc(var(--gap) * -1);
}
}
שימוש במאפיינים לוגיים מאפשר לגלול במדיה בכל מצב כתיבה ובכל כיוון של המסמך.
גלילה עם נקודות עצירה (scroll snapping)
אפשר להפוך מאגר עם גלילה וגלישה לתוך אזור תצוגה עם הצמדה באמצעות שורת CSS אחת, ואז הילדים יכולים לציין איך הם רוצים להתיישר עם אזור התצוגה הזה.
.horizontal-media-scroller {
--size: 150px;
display: grid;
grid-auto-flow: column;
gap: calc(var(--gap) / 2);
margin: 0;
overflow-x: auto;
overscroll-behavior-inline: contain;
padding-inline: var(--gap);
scroll-padding-inline: var(--gap);
padding-block-end: calc(var(--gap) / 2);
scroll-snap-type: inline mandatory;
& figure {
scroll-snap-align: start;
}
}
מיקוד
ההשראה לרכיב הזה מגיעה מהפופולריות העצומה שלו בטלוויזיות, בחנויות אפליקציות ועוד. הרבה פלטפורמות של משחקי וידאו משתמשות בפריסה של מסך הבית שדומה מאוד לזו, עם סרגל גלילה של מדיה. התכונה 'מיקוד' היא שיפור משמעותי בחוויית המשתמש, ולא רק תוספת קטנה. תארו לעצמכם שאתם משתמשים בגלגל הזה של המדיה מהספה עם שלט, ורוצים לשפר קצת את האינטראקציה:
.horizontal-media-scroller a {
outline-offset: 12px;
&:focus {
outline-offset: 7px;
}
@media (prefers-reduced-motion: no-preference) {
& {
transition: outline-offset .25s ease;
}
}
}
הפעולה הזו מרחיקה את סגנון המתאר של המיקוד 7px
מהתיבה, ויוצרת מרווח נעים. אם למשתמש אין העדפות לגבי הפחתת תנועה, המערכת מבצעת מעבר בין ההזחות, וכך נוצרת תנועה עדינה באירוע המיקוד.
אינדקס משתנה
משתמשים בבקר משחקים ובמקלדת צריכים תשומת לב מיוחדת ברשימות הארוכות האלה של תוכן ואפשרויות שדורשים גלילה. הדפוס הנפוץ לפתרון הבעיה הזו נקרא אינדקס נע. זה קורה כשקונטיינר של פריטים ממוקד במקלדת, אבל רק לצאצא אחד מותר להחזיק את המיקוד בכל פעם. החוויה הזו, שבה אפשר להתמקד רק בפריט אחד בכל פעם, נועדה לאפשר דילוג על רשימה ארוכה של פריטים, במקום ללחוץ על Tab יותר מ-50 פעמים כדי להגיע לסוף הרשימה.
יש 300 פריטים באזור הגלילה הראשון של ההדגמה. אנחנו יכולים לעשות יותר טוב מאשר לגרום להם לעבור את כל האפשרויות כדי להגיע לקטע הבא.
כדי ליצור את החוויה הזו, JavaScript צריך לעקוב אחרי אירועי מקלדת ואירועי מיקוד. יצרתי ספרייה קטנה בקוד פתוח ב-npm כדי לעזור לכם ליצור חוויית משתמש כזו בקלות. כך משתמשים בו כדי להוסיף 3 רכיבי גלילה:
import {rovingIndex} from 'roving-ux';
rovingIndex({
element: someElement
});
בהדגמה הזו, השאילתה מחפשת את רכיבי הגלילה במסמך, ולכל אחד מהם קוראת לפונקציה rovingIndex()
. מעבירים את rovingIndex()
לרכיב כדי לקבל את חוויית הניווט, כמו מאגר רשימה, ובורר שאילתת יעד, למקרה שיעדי המיקוד הם לא צאצאים ישירים.
document.querySelectorAll('.horizontal-media-scroller')
.forEach(scroller =>
rovingIndex({
element: scroller,
target: 'a',
}))
מידע נוסף על ההשפעה הזו זמין בספריית הקוד הפתוח roving-ux.
יחס גובה-רוחב
בזמן כתיבת הפוסט הזה, התמיכה ב-aspect-ratio
מוסתרת מאחורי דגל ב-Firefox, אבל היא זמינה בדפדפנים מבוססי Chromium או בממירים. מכיוון שפריסת הרשת של רכיב המדיה לגלגול מציינת רק את הכיוון והריווח, הגודל יכול להשתנות בתוך שאילתת מדיה שבודקת תמיכה ביחס גובה-רוחב.
שיפור הדרגתי של חלק מהרכיבים לגלולת מדיה דינמית יותר.
@supports (aspect-ratio: 1) {
.horizontal-media-scroller figure > picture {
inline-size: auto; /* for a block-size driven ratio */
aspect-ratio: 1; /* boxes by default */
@nest section:nth-child(2) & {
aspect-ratio: 16/9;
}
@nest section:nth-child(3) & {
/* double the size of the others */
block-size: calc(var(--size) * 2);
aspect-ratio: 4/3;
/* adjust size to fit more items into the viewport */
@media (width <= 480px) {
block-size: calc(var(--size) * 1.5);
}
}
}
}
אם הדפדפן תומך בתחביר aspect-ratio
, התמונות של פס הגלילה של המדיה משודרגות לגודל aspect-ratio
. בעזרת תחביר הקינון של הטיוטה, יחס הגובה-רוחב של כל תמונה משתנה בהתאם לשורה שבה היא מופיעה – הראשונה, השנייה או השלישית. תחביר הקינון מאפשר גם לבצע התאמות קטנות של אזור התצוגה, יחד עם לוגיקת הגודל האחרת.
בעזרת ה-CSS הזה, כשהתכונה תהיה זמינה במנועי דפדפן נוספים, יוצג פריסה קלה לניהול אבל מושכת יותר מבחינה ויזואלית.
העדפה לצמצום נתונים
הטכניקה הבאה זמינה רק מאחורי דגל ב-Canary, אבל רציתי להראות איך אפשר לחסוך הרבה זמן טעינה של דפים ושימוש בנתונים באמצעות כמה שורות של CSS. שאילתת המדיה prefers-reduced-data
מרמה 5 מאפשרת לבדוק אם המכשיר נמצא במצב של צמצום נתונים, כמו מצב חיסכון בנתונים. אם כן, אוכל לשנות את המסמך, ובמקרה הזה, להסתיר את התמונות.
figure {
@media (prefers-reduced-data: reduce) {
& {
min-inline-size: var(--size);
& > picture {
display: none;
}
}
}
}
עדיין אפשר לנווט בתוכן, אבל בלי להוריד את התמונות הגדולות. כך נראה האתר לפני הוספת ה-CSS של prefers-reduced-data
:
(7 בקשות, 100kb של משאבים ב-131ms)
כך נראים ביצועי האתר אחרי הוספת ה-prefers-reduced-data
CSS:
(71 בקשות, 1.2MB של משאבים ב-1.07 שניות)
64 בקשות פחות, שזה בערך 60 התמונות באזור התצוגה (בדיקות שבוצעו במסך רחב) של כרטיסיית הדפדפן הזו, שיפור של כ-80% בטעינת הדף ו-10% מהנתונים בחיבור. CSS די חזק.
סיכום
עכשיו שאתם יודעים איך עשיתי את זה, איך אתם הייתם עושים את זה?! 🙂
כדאי לגוון את הגישות שלנו וללמוד את כל הדרכים לבנות אתרים. אתם יכולים ליצור הדגמה ב-Codepen או לארח הדגמה משלכם, לצייץ לי אותה ואוסיף אותה לקטע 'רמיקסים של הקהילה' שבהמשך.
מקור
רמיקסים מהקהילה
עדיין אין מה לראות כאן!