סקירה כללית בסיסית על בניית רכיב רספונסיבי ונגיש של נתיב ניווט, שיעזור למשתמשים להתמצא באתר שלכם.
בפוסט הזה אני רוצה לשתף מחשבות על דרך לבנות רכיבי נתיב. צפייה בהדגמה
אם אתם מעדיפים לצפות בסרטון, הנה גרסת YouTube של הפוסט הזה:
סקירה כללית
רכיב breadcrumbs מראה למשתמש את המיקום שלו בהיררכיית האתר. השם מגיע מהסיפור עמי ותמי, שפיזרו פירורי לחם כדי למצוא את הדרך חזרה הביתה.
ה-breadcrumbs בפוסט הזה הם לא breadcrumbs רגילים, אלא דומים ל-breadcrumbs. הם מציעים פונקציונליות נוספת בכך שהם מציבים דפים מקבילים ישירות בניווט באמצעות <select>, וכך מאפשרים גישה רב-שכבתית.
חוויית משתמש ברקע
בסרטון ההדגמה של הרכיב שמוצג למעלה, קטגוריות ה-placeholder הן ז'אנרים של משחקי וידאו. הנתיב הזה נוצר כשעוברים בנתיב הבא: home »
rpg » indie » on sale, כמו שמוצג בהמשך.
רכיב ה-breadcrumb הזה צריך לאפשר למשתמשים לנווט בהיררכיית המידע הזו, לדלג בין ענפים ולבחור דפים במהירות ובדיוק.
ארכיטקטורת מידע
לדעתי, כדאי לחשוב במונחים של אוספים ופריטים.
אוספים
אוסף הוא מערך של אפשרויות לבחירה. מדף הבית של אב הטיפוס של נתיב הניווט של הפוסט הזה, האוספים הם FPS, RPG, brawler, dungeon crawler, sports ו-puzzle.
פריטים
משחק וידאו הוא פריט, ואוסף ספציפי יכול להיות פריט אם הוא מייצג אוסף אחר. לדוגמה, RPG הוא פריט ואוסף תקין. אם זה פריט, המשתמש נמצא בדף הקולקציה הזה. לדוגמה, הם מופיעים בדף של משחקי תפקידים, שבו מוצגת רשימה של משחקי תפקידים, כולל קטגוריות המשנה הנוספות AAA, Indie ו-Self Published.
במונחים של מדעי המחשב, רכיב ה-breadcrumbs הזה מייצג מערך רב-ממדי:
const rawBreadcrumbData = {
"FPS": {...},
"RPG": {
"AAA": {...},
"indie": {
"new": {...},
"on sale": {...},
"under 5": {...},
},
"self published": {...},
},
"brawler": {...},
"dungeon crawler": {...},
"sports": {...},
"puzzle": {...},
}
לאפליקציה או לאתר שלכם תהיה ארכיטקטורת מידע (IA) מותאמת אישית שתיצור מערך רב-ממדי שונה, אבל אני מקווה שהקונספט של דפי נחיתה לאוסף ושל מעבר היררכי יוכל להיכנס גם ללחצני הניווט שלכם.
פריסות
Markup
רכיבים טובים מתחילים עם HTML מתאים. בקטע הבא אפרט על אפשרויות התיוג שלי ואיך הן משפיעות על הרכיב הכולל.
ערכת צבעים כהה ובהירה
<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> עוזרת למשתמשים בקורא מסך, כי היא מספקת להם מידע על הפעולה של הכפתור. אבל הוא מספק עזרה לכולם, ותוכלו לראות שהוא מוצג במרכז המסך באייפד. מאפיין אחד מספק הקשר לכפתור למשתמשים רבים.

קישוטים של מפרידים
<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 צריכים להיות מסוגלים לייצג נתיב ארוך מאוד. אני אוהב לאפשר הצגה של דברים מחוץ למסך בצורה אופקית, כשזה מתאים, והרגשתי שרכיב הנתיב הזה מתאים לכך.
.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;
}}
}
הסגנונות של התוכן שגולש מגבולות הרכיב מגדירים את חוויית המשתמש הבאה:
- גלילה אופקית עם הגבלת גלילה.
- מרווח פנימי לגלילה אופקית.
- נקודת הצמדה אחת בפירור הדרך האחרון. כלומר, בזמן טעינת הדף, פירור הלחם הראשון נטען כשהוא צמוד וגלוי.
- ההגדרה הזו מסירה את נקודת ההצמדה מ-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 has
changed ו-eager <select> change event firing prevention.
הצורך במניעת אירועים מוקדמים נובע מהשימוש ברכיב <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 כרכיב אינטרנט: הדגמה וקוד