סקירה כללית בסיסית של בניית מודעת שקף רספונסיבית יציאה צדדית
בפוסט הזה אשתף אתכם איך יצרתי אב טיפוס של רכיב Sidenav באינטרנט, שהוא רספונסיבי, עם שמירת מצב, תומך בניווט באמצעות המקלדת, פועל עם JavaScript ובלי JavaScript ופועל בכל הדפדפנים. אפשר לנסות את ההדגמה.
אם אתם מעדיפים סרטון, הנה גרסה של YouTube לפוסט:
סקירה
קשה לבנות מערכת ניווט רספונסיבית. חלק מהמשתמשים ישתמשו במקלדת, חלקם ישתמשו במחשבים שולחניים וחלקם יגיעו ממכשיר נייד קטן. צריך לאפשר לכל מי שרוצה לפתוח ולסגור את התפריט.
טקטיקה באינטרנט
בניתוח של הרכיב הזה נהניתי לשלב כמה תכונות קריטיות של פלטפורמת האינטרנט:
- שירות CSS
:target
- רשת CSS
- transforms בשירות 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>
לחיצה על כל אחד מהקישורים האלו משנה את מצב הגיבוב של כתובת ה-URL של הדף, ואז באמצעות פסאודו-מחלקה אני מציג ומסתיר את הניווט הצדי:
@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.
סיכום
עכשיו אתה יודע איך עשיתי את זה, איך היית?! זה יוצר ארכיטקטורת רכיבים כיפית! מי יקבל את הגרסה הראשונה עם מקומות? 🙂
בואו לגוון את הגישות שלנו ונלמד את כל הדרכים לבנות באינטרנט. אפשר ליצור Glitch, לשלוח לי ציוץ של הגרסה שלכם, ואני אוסיף אותה לקטע רמיקסים מהקהילה שבהמשך.
רמיקסים קהילתיים
- @_developit עם רכיבים בהתאמה אישית: הדגמה (דמו) וקוד
- @Mayeedwin1 עם HTML/CSS/JS: הדגמה וקוד
- @a_nurella עם רמיקס של תקלה: הדגמה וקוד
- @EvroMalarkey עם HTML/CSS/JS: הדגמה וקוד