סקירה כללית בסיסית על האופן שבו יוצרים רכיב של נתיב הניווט שמתאים למכשירים ניידים ונגיש למשתמשים, כדי לעזור להם לנווט באתר.
בפוסט הזה אני רוצה לשתף את הדרך שבה אני יוצר רכיבים של נתיב הניווט. רוצים לנסות את ההדגמה?
אם אתם מעדיפים סרטון, הנה גרסה של הפוסט הזה ב-YouTube:
סקירה כללית
רכיב נתיבי ניווט מראה את המיקום בהיררכיית האתר שבו נמצא המשתמש, הוא מ-הנזל וגרטל, שהשאירו מאחוריהם סימני דרך בחלק מהעצים חשוכים, והצליחו למצוא את הדרך הביתה על ידי מעקב אחר מיקומים מיותרים.
קוביות הלחם בפוסט הזה הן לא קוביות לחם רגילות, אלא קוביות לחם דמויות. הם מספקים פונקציונליות נוספת על ידי הצגת דפי אחים ישירות בתפריט הניווט באמצעות <select>
, וכך מאפשרים גישה בכמה רמות.
חוויית המשתמש ברקע
בסרטון ההדגמה של הרכיבים שלמעלה, קטגוריות ה-placeholder הן ז'אנרים של משחקי וידאו. השביל הזה נוצר על ידי ניווט בנתיב הבא: home »
rpg » indie » on sale
, כפי שמוצג בהמשך.
רכיב הלחצנים 'לחצן חזרה' אמור לאפשר למשתמשים לנוע בהיררכיית המידע הזו, לדלג על ענפים ולבחור דפים במהירות ובדיוק.
ארכיטקטורת מידע
לדעתי כדאי לחשוב על אוספים ופריטים.
אוספים
אוסף הוא מערך של אפשרויות לבחירה. בדף הבית של אב הטיפוס של הלחצן 'לחצן חזרה' שמוצג בפוסט הזה, האוספים הם: FPS, RPG, brawler, dungeon crawler, ספורט ופאזל.
פריטים
משחק וידאו הוא פריט, אוסף ספציפי יכול להיות גם פריט אם הוא מייצג אוסף אחר. לדוגמה, משחקי תפקידים הם פריט ואוסף תקף. אם מדובר בפריט, המשתמש נמצא בדף הקולקציה. לדוגמה, הן מופיעות בדף 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, וכנראה גם בדפדפנים אחרים, האירוע changed
של הבחירה מופעל כשהמשתמש מנווט באפשרויות באמצעות המקלדת. לכן קראתי לזה 'מוקדש', כי המשתמש בחר באפשרות רק באופן סופי, כמו העברת העכבר מעל האפשרות או התמקדות בה, אבל לא אישר את הבחירה באמצעות enter
או click
. אירוע eager הופך את התכונה הזו של שינוי קטגוריית רכיב לבלתי נגישה, מפני שפתיחת תיבת הבחירה ודפדוף בפריט פשוט יפעיל את האירוע ותשנה את הדף לפני שהמשתמש יהיה מוכן.
אירוע <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 כרכיב אינטרנט: דוגמה וקוד