סקירה כללית בסיסית של בניית מודעת שקף רספונסיבית יציאה צדדית
בפוסט הזה אשתף אתכם איך יצרתי אב טיפוס של רכיב Sidenav באינטרנט הוא רספונסיבית, שומר על מצב, תומך בניווט באמצעות המקלדת, פועל עם או בלי JavaScript, ופועל בדפדפנים שונים. אפשר לנסות את ההדגמה.
אם אתם מעדיפים סרטון, הנה גרסה של YouTube לפוסט:
סקירה כללית
קשה לבנות מערכת ניווט רספונסיבית. חלק מהמשתמשים יוצגו במקלדת, לחלק יהיה מחשבים חזקים, ולחלקם יהיו מכשירים ניידים קטנים. צריך לאפשר לכל מי שרוצה לפתוח ולסגור את התפריט.
טקטיקה באינטרנט
בניתוח של הרכיב הזה נהניתי לשלב כמה תכונות קריטיות של פלטפורמת האינטרנט:
- שירות CSS
:target
- רשת CSS
- שינויים בשירות CSS
- שאילתות מדיה של CSS בנושא אזור תצוגה והעדפות משתמש
- JS עבור
focus
שיפורים של חוויית המשתמש
הפתרון שלי כולל סרגל צד אחד וניתן להחליף אותו רק כשהוא נמצא 'נייד' אזור תצוגה של 540px
או פחות.
540px
תהיה נקודת העצירה למעבר בין הפריסה האינטראקטיבית לנייד והפריסה הסטטית למחשב.
פסאודו-מחלקה :target
של CSS
קישור <a>
אחד מגדיר את הגיבוב של כתובת ה-URL כ-#sidenav-open
והשני כריק (''
).
לבסוף, רכיב זה מכיל את id
שתואם לגיבוב:
<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<aside id="sidenav-open">
…
</aside>
לחיצה על כל אחד מהקישורים האלה משנה את מצב הגיבוב (hash) של כתובת הדפים שלנו, ואז באמצעות מחלקה מדומה, אני מציג ומסתיר את הניווט הצדי:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
רשת CSS
בעבר השתמשתי רק במיקום מוחלט או קבוע
פריסות ורכיבים של סרגל ניווט צדדי. עם זאת, היא מציגה רשת, באמצעות התחביר grid-area
שלה,
מאפשרת להקצות מספר רכיבים לאותה שורה או לאותה עמודה.
מקבצים
רכיב הפריסה הראשי #sidenav-container
הוא רשת שיוצרת שורה אחת ו-2 עמודות,
אחד מהם נקרא stack
. כשהרווח מוגבל, שירות ה-CSS מקצה את כל המאפיינים של הרכיב <main>
הילדים לאותו שם רשת, מציבים את כל הרכיבים באותו רווח ויוצרים מקבץ.
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
רקע של התפריט
<aside>
הוא הרכיב האנימציה שמכיל את הניווט הצדדי. יש בו
שני צאצאים: מאגר הניווט <nav>
בשם [nav]
ורקע <a>
בשם [escape]
, שמשמש לסגירת התפריט.
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
כוונון של 2fr
ו- 1fr
כדי למצוא את היחס הרצוי לשכבת-העל של התפריט וללחצן סגירת המרחב השלילי.
המרות בתלת-ממד בשירות CSS מעברים
הפריסה שלנו מסודרת עכשיו לפי אזור תצוגה של נייד. עד שאוסיף סגנונות חדשים, שכבת-העל של המאמר שלנו כברירת מחדל. הנה מידע על חוויית המשתמש שרוצים לצלם בקטע הבא:
- הוספת אנימציה לפתיחה ולסגירה
- מוסיפים אנימציה עם תנועה רק אם המשתמש מסכים לכך
- מוסיפים אנימציה ל-
visibility
כך שמיקוד המקלדת לא ייכנס לרכיב מחוץ למסך
כשהתחלתי להטמיע אנימציות של תנועה, אני רוצה להתחיל מהנושא של נגישות.
תנועה נגישה
לא כל אחד רוצה חוויית החלקה החוצה. בפתרון שלנו, ההעדפה הזו
מיושם על ידי התאמה של משתנה CSS מסוג --duration
בתוך שאילתת מדיה. הערך של שאילתת המדיה מייצג
העדפות התנועה של המשתמש (אם זמינה).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
עכשיו, כשהניווט הצדי שלנו מחליק ופתוח, אם המשתמש מעדיף תנועה מופחתת, אני מעביר את הרכיב באופן מיידי לתצוגה, תוך שמירה על המצב ללא תנועה.
מעבר, טרנספורמציה, תרגום
ניווט צדדי החוצה (ברירת מחדל)
כדי להגדיר את מצב ברירת המחדל של הניווט הצדדי שלנו בנייד למצב 'לא במסך':
אני ממקם את הרכיב עם transform: translateX(-110vw)
.
הערה: הוספתי עוד 10vw
לקוד הטיפוסי של -100vw
מחוץ למסך,
כדי להבטיח שה-box-shadow
של הניווט הצדי לא יציץ לאזור התצוגה הראשי כשהוא מוסתר.
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
}
}
ניווט צידי ב-
כשהרכיב #sidenav
תואם ל-:target
, צריך להגדיר את המיקום של translateX()
כ-homebase 0
,
ו-CSS מחליק את הרכיב מהמיקום שלו מתוך -110vw
אל 'in' (ב)
מיקום של 0
מעל var(--duration)
כאשר הגיבוב של כתובת ה-URL משתנה.
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
הרשאות גישה למעבר
עכשיו המטרה היא להסתיר את התפריט בקוראי המסך כשהוא לא זמין.
כדי שהמערכות לא יתמקדו בתפריט שמחוץ למסך. כדי לעשות זאת,
את המעבר כאשר :target
משתנה.
- לאחר הכניסה, אל תעבירו את החשיפה. גלוי מיד כדי לראות את האלמנט נכנס פנימה ולקבל את המיקוד.
- כשיוצאים, החשיפה של המעבר אבל מעכבת אותה, לכן היא תהפוך ל-
hidden
בסוף המעבר.
שיפורי UX לנגישות
קישורים
הפתרון הזה מסתמך על שינוי כתובת ה-URL כדי לנהל את המצב.
כמובן שיש להשתמש באלמנט <a>
כאן, והוא נגיש למדי
בחינם. בואו נקשט את האלמנטים האינטראקטיביים שלנו בתוויות שמבטאות בבירור את הכוונה.
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
<svg>...</svg>
</a>
כעת, בלחצני האינטראקציה הראשיים שלנו מצוין בבירור כוונתם גם בעכבר וגם במקלדת.
:is(:hover, :focus)
פסאודו-בורר השימושי הזה של שירות CSS מאפשר לנו לקבל את כולם במהירות בסגנונות של העברת העכבר, על ידי שיתוף שלהם עם המיקוד.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
פיזור על JavaScript
יש ללחוץ על escape
כדי לסגור
האם הקשה על Escape
במקלדת אמורה לסגור את התפריט, נכון? בואו נחבר את זה לאינטרנט.
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
היסטוריית הדפדפן
כדי למנוע מקבצים של אינטראקציות פתוחות וסגורות להיסטוריית הדפדפן, מוסיפים את ה-JavaScript המוטבע הבאות לחצן הסגירה:
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
הפעולה הזו תסיר את הרשומה של היסטוריית כתובות ה-URL כשהיא סגורה, כך שהתפריט נראה מעולם לא נפתח.
התמקדות בחוויית המשתמש
קטע הקוד הבא עוזר לנו להתמקד בלחצני הפתיחה והסגירה אחרי שהם פותחים או סוגרים. אני רוצה להקל על ההחלפה.
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
כשחלונית הניווט הצדדית נפתחת, מתמקדים בלחצן הסגירה. כאשר הניווט הצדדי נסגר,
להתמקד בלחצן הפתיחה. כדי לעשות זאת, שולחים קריאה ל-focus()
ברכיב ב-JavaScript.
סיכום
עכשיו אתה יודע איך עשיתי את זה, איך היית?! זה יוצר ארכיטקטורת רכיבים כיפית! מי יקבל את הגרסה הראשונה עם מקומות? 🙂
כדאי לגוון וללמוד את כל הדרכים לבניית אתרים באינטרנט. יוצרים תקלה, שלחו לי ציוץ של הגרסה שלכם ואני אוסיף אותה הקטע רמיקסים לקהילה מופיע בהמשך.
רמיקסים קהילתיים
- @_developit עם רכיבים מותאמים אישית: demo & קוד
- @Mayeedwin1 עם HTML/CSS/JS: demo & קוד
- @a_nurella עם רמיקס של תקלה: demo & קוד
- @EvroMalarkey עם HTML/CSS/JS: demo & קוד