סקירה כללית בסיסית של אופן הבנייה של רכיב נתיבי ניווט רספונסיבי ונגיש שיאפשר למשתמשים לנווט באתר.
בפוסט הזה אני רוצה לשתף איתכם חשיבה על דרך לפיתוח רכיבים של מיקומים באתר. רוצים לנסות את ההדגמה?
אם אתם מעדיפים סרטון, הנה גרסה של YouTube לפוסט:
סקירה כללית
רכיב נתיבי ניווט מוצג המיקום בהיררכיית האתר של המשתמש. השם הוא האנסל ו- גרטל, שנרשמה מיקומים של משתמשים מאחוריהם בכמה יערות חשוכים והצליחו למצוא את הדרך הביתה על ידי מעקב אחר מיקומים גיאוגרפיים לאחור.
נתיבי הניווט בפוסט הזה אינם סטנדרטיים
נתיבי ניווט,
הם דמויי נתיב. הם מספקים פונקציונליות נוספת על ידי הצגת דפי אחים ישירות בתפריט הניווט באמצעות <select>
, וכך מאפשרים גישה בכמה רמות.
חוויית המשתמש ברקע
בסרטון ההדגמה של הרכיבים שלמעלה, קטגוריות ה-placeholder הן הז'אנרים של
משחקי וידאו. השביל הזה נוצר על ידי ניווט בנתיב הבא: home »
rpg » indie » on sale
, כפי שמוצג בהמשך.
רכיב המיקום באתר אמור לאפשר למשתמשים לעבור היררכיית מידע; הסתעפויות מהירות ובחירת דפים במהירות מדויקות.
ארכיטקטורת מידע
לדעתי כדאי להתייחס לאוספים ולפריטים.
אוספים
אוסף הוא מערך של אפשרויות לבחירה. מדף הבית של אב הטיפוס של המיקום באתר של הפוסט הזה, האוספים הם FPS, משחקי תפקידים, קרבות של אחד מול רבים, סורקי צינוק, משחקי ספורט ופאזלים.
פריטים
משחק וידאו הוא פריט, אוסף ספציפי יכול להיות גם פריט אם הוא שמייצג אוסף אחר. לדוגמה, משחק תפקידים הוא פריט האוסף 'עדכונים'. כשמדובר בפריט, המשתמש נמצא בדף האוסף הזה. לדוגמה, בדף משחקי תפקידים, שמציג רשימה של משחקי תפקידים, קטגוריות משנה נוספות AAA, אינדי ופרסום עצמי.
במונחים של מדעי המחשב, הרכיב הזה של נתיב הניווט מייצג מערך רב-ממדי:
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>
, אבל הוספתי UX למעבר עם
בחירה מוסתרת. הכיתה .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 {
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
קודם כול, בלי קשר לסוג הנתב שבו אתם משתמשים באתר או באפליקציה,
כאשר משתמש משנה את נתיבי הניווט, יש לעדכן את כתובת האתר והמשתמש
הציג את הדף המתאים. שנית, כדי לשמור על חוויית משתמש רגילה, חשוב לוודא שלא מתרחשים אירועי ניווט לא צפויים כשהמשתמשים רק עיינו באפשרויות <select>
.
שני מדדים קריטיים של חוויית המשתמש שצריך לטפל בהם באמצעות JavaScript: מניעת הפעלה של אירוע שינוי <select>
מוקדם מדי ואירוע שינוי של select שהשתנה.
נדרשת מניעת אירוע eager עקב שימוש ב-<select>
לרכיב מסוים. ב-Windows Edge, וככל הנראה גם בדפדפנים אחרים, בוחרים באפשרות 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 Solbaks כרכיב אינטרנט: הדגמה וקוד