סקירה כללית בסיסית של בניית מודעת שקף רספונסיבית יציאה צדדית
בפוסט הזה אשתף אתכם איך יצרתי אב טיפוס של רכיב Sidenav באינטרנט, שהוא רספונסיבי, עם שמירת מצב, תומך בניווט באמצעות המקלדת, פועל עם JavaScript ובלי JavaScript ופועל בכל הדפדפנים. כדאי לנסות את הדגמה.
אם אתם מעדיפים סרטון, הנה גרסה של הפוסט הזה ב-YouTube:
סקירה כללית
קשה לבנות מערכת ניווט רספונסיבית. חלק מהמשתמשים ישתמשו במקלדת, חלקם ישתמשו במחשבים שולחניים וחלקם ישתמשו בנייד קטן. כל מי שמבקר באתר אמור להיות מסוגל לפתוח ולסגור את התפריט.
טקטיקות לאינטרנט
בניתוח הרכיבים הזה נהניתי לשלב כמה תכונות קריטיות של פלטפורמת אינטרנט:
- CSS
:target
- grid ב-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 של כתובת ה-URL של הדף, ואז באמצעות פסאודו-סיווג אני מציג ומסתיר את תפריט הצד:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
CSS Grid
בעבר, השתמשתי רק ברכיבים ובפריסות של תפריט צד במיקום מוחלט או קבוע. עם זאת, באמצעות תבנית התצוגה של רשת, עם התחביר grid-area
, אפשר להקצות כמה רכיבים לאותה שורה או עמודה.
מקבצים
רכיב הפריסה הראשי #sidenav-container
הוא רשת שיוצרת שורה אחת ושתי עמודות, וכל אחת מהן נקראת 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>
הוא רכיב האנימציה שמכיל את תפריט הניווט הצדדי. יש לו 2 צאצאים: מאגר הניווט <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()
לבסיס 0
, ומתבוננים ב-CSS כשהרכיב מחליק מהמיקום -110vw
'מחוץ למסך' למיקום 0
'במסך' מעל var(--duration)
כשה-hash של כתובת ה-URL משתנה.
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
הרשאות גישה למעבר
המטרה עכשיו היא להסתיר את התפריט ממכשירי הקראה מסך כשהוא לא מוצג, כדי שהמערכות לא יתמקדו בתפריט שלא מופיע במסך. כדי לעשות זאת, מגדירים מעבר של רמת חשיפה כשהערך של :target
משתנה.
- בכניסה, אל תעבירו את החשיפה. חשוב להיות גלויים מיד כדי לראות את הרכיב בדף ולאשר את המיקוד.
- כשמסיימים את ההצגה, משנים את הסטטוס של החשיפה אבל מעכבים את השינוי, כך שהוא עובר ל-
hidden
בסוף המעבר.
שיפורים בחוויית המשתמש של הנגישות
קישורים
הפתרון הזה מבוסס על שינוי כתובת ה-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 בהיסטוריה בסגירה, כאילו התפריט לא נפתח אף פעם.
Focus UX
קטע הקוד הבא עוזר לנו להעביר את המיקוד ללחצני הפתיחה והסגירה אחרי שהם נפתחים או נסגרים. אני רוצה להקל על ההחלפה.
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: דוגמה וקוד