סקירה כללית בסיסית על האופן שבו יוצרים רכיב של נתיב הניווט שמתאים למכשירים ניידים ונגיש למשתמשים, כדי לעזור להם לנווט באתר.
בפוסט הזה אני רוצה לשתף את הדרך שבה אני יוצר רכיבים של נתיב הניווט. לנסות את הדמו
אם אתם מעדיפים סרטון, הנה גרסה של הפוסט הזה ב-YouTube:
סקירה כללית
רכיב נתיב breadcrumbs מראה את המיקום של המשתמש בהיררכיית האתר. השם נגזר מהאגדה על הנסל וגרטל, שהשליכו נתיב breadcrumbs מאחוריהם ביער חשוך, והצליחו למצוא את הדרך הביתה על ידי מעקב אחר הפירורים לאחור.
קוביות הלחם בפוסט הזה הן לא קוביות לחם רגילות, אלא קוביות לחם דמויות. הם מציעים פונקציונליות נוספת על ידי הצגת דפים אחים ישירות בתפריט הניווט באמצעות <select>
, וכך מאפשרים גישה בכמה רמות.
חוויית משתמש ברקע
בסרטון הדגמה של הרכיב שלמעלה, הקטגוריות של סמלי ה-placeholder הן ז'אנרים של משחקי וידאו. כדי ליצור את הנתיב הזה, עוברים אל הנתיב הבא: home »
rpg » indie » on sale
, כפי שמוצג בהמשך.
רכיב הלחצנים 'לחצן חזרה' אמור לאפשר למשתמשים לנוע בהיררכיית המידע הזו, לדלג על ענפים ולבחור דפים במהירות ובדיוק.
ארכיטקטורת מידע
לדעתי כדאי לחשוב על אוספים ופריטים.
אוספים
אוסף הוא מערך של אפשרויות לבחירה. בדף הבית של אב הטיפוס של הלחצן 'לחצן חזרה' שמוצג בפוסט הזה, האוספים הם: FPS, RPG, brawler, dungeon crawler, ספורט ופאזל.
פריטים
משחק וידאו הוא פריט, אוסף ספציפי יכול להיות גם פריט אם הוא מייצג אוסף אחר. לדוגמה, RPG הוא פריט ואוסף תקין. אם מדובר בפריט, המשתמש נמצא בדף הקולקציה. לדוגמה, הן מופיעות בדף RPG, שבו מוצגת רשימה של משחקי RPG, כולל קטגוריות המשנה הנוספות AAA, Indie ו-Self Published.
במונחים של מדעי המחשב, הרכיב הזה של נתיב הניווט מייצג מערך רב-ממדי:
const rawBreadcrumbData = {
"FPS": {...},
"RPG": {
"AAA": {...},
"indie": {
"new": {...},
"on sale": {...},
"under 5": {...},
},
"self published": {...},
},
"brawler": {...},
"dungeon crawler": {...},
"sports": {...},
"puzzle": {...},
}
לאפליקציה או לאתר שלך תהיה ארכיטקטורת מידע (IA) בהתאמה אישית שתוצריה יוצרים מערך אחר רב-ממדי, אבל אני מקווה שהרעיון של דפי נחיתה של קולקציות ושל סריקה של היררכיה יוכל להופיע גם בפירוט הנתיב.
פריסות
Markup
רכיבים טובים מתחילים בקוד HTML מתאים. בקטע הבא אסביר על הבחירות שלי לגבי הרכיב של ה-Markup ואיך הן משפיעות על הרכיב הכולל.
ערכת צבעים כהה ובהירה
<meta name="color-scheme" content="dark light">
מטא התג color-scheme
בקטע הקוד שלמעלה מעדכן את הדפדפן שהדף הזה רוצה את סגנונות הדפדפן הבהירים והכהים. קוביות הניווט לדוגמה לא כוללות קובצי CSS לערכות הצבעים האלה, ולכן קוביות הניווט ישתמשו בצבעים שמוגדרים כברירת מחדל בדפדפן.
רכיב ניווט
<nav class="breadcrumbs" role="navigation"></nav>
מומלץ להשתמש ברכיב <nav>
לניווט באתר. לרכיב הזה יש תפקיד ARIA משתמע של ניווט.
בבדיקה, שמתי לב שהוספת המאפיין role
שינתה את האופן שבו קורא המסך יוצר אינטראקציה עם האלמנט. הוא הכריז עליו כעל ניווט, ולכן בחרתי להוסיף אותו.
סמלים
כשסמל חוזר על עצמו בדף, אלמנט ה-SVG <use>
מאפשר להגדיר את path
פעם אחת ולהשתמש בו בכל המופעים של הסמל. כך אפשר למנוע חזרה על אותו מידע לגבי נתיב, וכתוצאה מכך מסמכים גדולים יותר וסיכוי גבוה יותר לאי-עקביות בנתיב.
כדי להשתמש בשיטה הזו, מוסיפים לדף רכיב SVG מוסתר ומעטפת את הסמלים ברכיב <symbol>
עם מזהה ייחודי:
<svg style="display: none;">
<symbol id="icon-home">
<title>A home icon</title>
<path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
</symbol>
<symbol id="icon-dropdown-arrow">
<title>A down arrow</title>
<path d="M19 9l-7 7-7-7"/>
</symbol>
</svg>
הדפדפן קורא את ה-HTML של ה-SVG, שומר את פרטי הסמל בזיכרון וממשיך לחלקים האחרים בדף שמפנים למזהה לשימושים נוספים של הסמל, כך:
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-home" />
</svg>
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
<use href="#icon-dropdown-arrow" />
</svg>
מגדירים פעם אחת ומשתמשים כמה שרוצים, עם השפעה מינימלית על ביצועי הדף וסגנון גמיש. שימו לב שהערך aria-hidden="true"
נוסף לרכיב ה-SVG.
הסמלים לא מועילים למי שגולל באתר ושומע רק את התוכן, והסתרה שלהם ממשתמשים כאלה תמנע מהם להוסיף רעש מיותר.
קישור מפוצל .crumb
כאן נמצאת ההבדל בין נתיב הניווט המסורתי לבין נתיבי הניווט שברכיב הזה.
בדרך כלל, זה יהיה רק קישור <a>
, אבל הוספתי חוויית משתמש של סריקה עם בחירת 'הוספה' במסווה. הכיתה .crumb
אחראית על הפריסה של הקישור והסמל, והכיתה .crumbicon
אחראית על הצטברות הסמל והרכיב שנבחר יחד. קראתי לו קישור מפוצל כי הפונקציות שלו דומות מאוד ללחצן מפוצל, אבל לניווט בדפים.
<span class="crumb">
<a href="#sub-collection-b">Category B</a>
<span class="crumbicon">
<svg>...</svg>
<select class="disguised-select" title="Navigate to another category">
<option>Category A</option>
<option selected>Category B</option>
<option>Category C</option>
</select>
</span>
</span>
קישור וכמה אפשרויות הם לא משהו מיוחד, אבל הם מוסיפים פונקציונליות לנתיב ניווט פשוט. הוספת title
לאלמנט <select>
עוזרת למשתמשים בקורא מסך, ומספקת להם מידע על הפעולה של הלחצן. עם זאת, הוא מספק את אותה עזרה לכל המשתמשים, והוא יוצג במקום בולט ב-iPad. מאפיין אחד מספק הקשר של לחצן למשתמשים רבים.
קישוטים של מפרידים
<span class="crumb-separator" aria-hidden="true">→</span>
אפשר להוסיף רק מפריד אחד, והוא יעשה את העבודה (ראו הדוגמה השלישית בסרטון שלמעלה). לאחר מכן נותנים לכל אחת את הערך aria-hidden="true"
כי הן תכונות דקורטיביות ולא משהו שקורא המסך צריך להקריא.
המאפיין gap
, שנסביר עליו בהמשך, מאפשר להגדיר את הריווח שלהם בקלות.
סגנונות
מכיוון שהצבע משתמש בצבעים של המערכת, הוא בעיקר מרווחים וסטאקים של סגנונות.
כיוון וזרימה של הפריסה
רכיב הניווט הראשי nav.breadcrumbs
מגדיר מאפיין מותאם אישית ברמת ההיקף לשימוש של הצאצאים, ובמקרים אחרים מגדיר פריסה אופקית עם יישור אנכי. כך תוכלו לוודא שהסמנים, המחיצות והסמלים יהיו באותו קו.
.breadcrumbs {
--nav-gap: 2ch;
display: flex;
align-items: center;
gap: var(--nav-gap);
padding: calc(var(--nav-gap) / 2);
}
כל .crumb
גם יוצר פריסה אופקית עם התאמה אנכית עם מרווח מסוים, אבל מטרגט במיוחד את צאצאי הקישור שלו ומציין את הסגנון white-space: nowrap
. זה חשוב במיוחד לנתיבי ניווט שמכילים כמה מילים, כי אנחנו לא רוצים שהם ייפרסו על כמה שורות. בהמשך הפוסט נוסיף סגנונות כדי לטפל בחריגה האופקית שנגרמה על ידי המאפיין white-space
.
.crumb {
display: inline-flex;
align-items: center;
gap: calc(var(--nav-gap) / 4);
& > a {
white-space: nowrap;
&[aria-current="page"] {
font-weight: bold;
}
}
}
הערך aria-current="page"
מתווסף כדי לעזור לקישור לדף הנוכחי לבלוט מהשאר. משתמשים בקורא מסך יקבלו אינדיקציה ברורה לכך שהקישור הוא לדף הנוכחי, אבל גם משתמשים שרואים יקבלו חוויית משתמש דומה, כי עיצבנו את הרכיב באופן חזותי.
הרכיב .crumbicon
משתמש בתצוגת רשת כדי לערום סמל SVG עם רכיב <select>
"כמעט בלתי נראה".
.crumbicon {
--crumbicon-size: 3ch;
display: grid;
grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
place-items: center;
& > * {
grid-area: stack;
}
}
רכיב <select>
הוא האחרון ב-DOM, כך שהוא בראש הערימה והוא אינטראקטיבי. מוסיפים סגנון של opacity: .01
כדי שעדיין אפשר יהיה להשתמש ברכיב, והתוצאה היא תיבת בחירה שמתאימה בצורה מושלמת לצורת הסמל.
זו דרך נוחה להתאים אישית את המראה של רכיב <select>
תוך שמירה על הפונקציונליות המובנית.
.disguised-select {
inline-size: 100%;
block-size: 100%;
opacity: .01;
font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}
אפשרויות נוספות
נתיב הניווט צריך לייצג נתיב ארוך מאוד. אני אוהב לאפשר לדברים לעבור מחוץ למסך אופקית, כשזה מתאים, וחשבתי שרכיב הלחצנים 'לחזר אחורה' מתאים לכך.
.breadcrumbs {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scroll-padding-inline: calc(var(--nav-gap) / 2);
& > .crumb:last-of-type {
scroll-snap-align: end;
}
@supports (-webkit-hyphens:none) { & {
scroll-snap-type: none;
}}
}
סגנונות ה-overflow מגדירים את חוויית המשתמש הבאה:
- גלילה אופקית עם תכונה של גלילה מעבר לקצה המסך.
- מרווח פנימי לגלילה אופקית.
- נקודת הצמדה אחת על הפירור האחרון. כלומר, כשהדף נטען, הפירור הראשון נטען במקום שבו הוא נמצא באתר.
- הסרת נקודת הצמדה מ-Safari, שמתקשה להתמודד עם שילובים של גלילה אופקית ואפקט הצמדה.
שאילתות מדיה
אחת מההתאמות העדינות שאפשר לבצע בתצוגות קטנות יותר היא להסתיר את התווית 'דף הבית' ולהשאיר רק את הסמל:
@media (width <= 480px) {
.breadcrumbs .home-label {
display: none;
}
}
נגישות
תנועה
אין הרבה תנועה ברכיב הזה, אבל אם נעטוף את המעבר בבדיקת prefers-reduced-motion
, נוכל למנוע תנועה לא רצויה.
@media (prefers-reduced-motion: no-preference) {
.crumbicon {
transition: box-shadow .2s ease;
}
}
אין צורך לשנות את הסגנונות האחרים, האפקטים של העברת העכבר מעל התמונה וההתמקדות בהם נהדרים ומשמעותיים גם בלי transition
, אבל אם התנועה בסדר, נוסיף מעבר עדין לאינטראקציה.
JavaScript
ראשית, ללא קשר לסוג הנתב שבו אתם משתמשים באתר או באפליקציה, כשמשתמש משנה את נתיב הניווט, צריך לעדכן את כתובת ה-URL ולהציג למשתמש את הדף המתאים. שנית, כדי לשמור על חוויית משתמש רגילה, חשוב לוודא שלא מתרחשים אירועי ניווט לא צפויים כשהמשתמשים רק עיינו באפשרויות <select>
.
שני מדדים קריטיים של חוויית המשתמש שצריך לטפל בהם באמצעות JavaScript: מניעת הפעלה של אירוע שינוי <select>
מוקדם מדי ואירוע שינוי של select שהשתנה.
יש צורך במניעת אירועים מיידית בגלל השימוש ברכיב <select>
. בדפדפן Windows Edge, וכנראה גם בדפדפנים אחרים, האירוע select changed
מופעל כשהמשתמש מעייין באפשרויות באמצעות המקלדת. לכן קראתי לזה 'מוקדש', כי המשתמש בחר באפשרות רק באופן סופי, כמו מעקב או התמקדות, אבל לא אישר את הבחירה באמצעות enter
או click
. האירוע המהיר מונע גישה לתכונה הזו של שינוי קטגוריית הרכיב, כי פתיחת תיבת הבחירה וגלישת פריט כלשהו יפעילו את האירוע וישנו את הדף לפני שהמשתמש יהיה מוכן.
אירוע <select>
שונה משופר
const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])
// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
let ignoreChange = false
nav.addEventListener('change', e => {
if (ignoreChange) return
// it's actually changed!
})
nav.addEventListener('keydown', ({ key }) => {
if (preventedKeys.has(key))
ignoreChange = true
else if (allowedKeys.has(key))
ignoreChange = false
})
})
האסטרטגיה לכך היא לעקוב אחרי אירועי הקשה על המקלדת בכל רכיב <select>
ולהחליט אם המקש שנלחץ היה אישור ניווט (Tab
או Enter
) או ניווט מרחבי (ArrowUp
או ArrowDown
). בעזרת הקביעה הזו, הרכיב יכול להחליט אם להמתין או להמשיך, כשהאירוע של רכיב <select>
מופעל.
סיכום
עכשיו, אחרי שסיפרתי לך איך עשיתי את זה, איך היית עושה את זה? 🙂
נרחיב את הגישות שלנו ונלמד את כל הדרכים לפיתוח באינטרנט. אתם יכולים ליצור גרסת דמו, לשלוח לי קישורים בטוויטר ואוסיף אותה לקטע 'רמיקסים של הקהילה' שבהמשך.
רמיקסים של הקהילה
- Tux Solbakk כרכיב אינטרנט: דוגמה וקוד