การสร้างคอมโพเนนต์การนำทางด้านข้าง

ภาพรวมพื้นฐานของวิธีสร้างสไลด์นำทางที่ตอบสนองตามอุปกรณ์

ในโพสต์นี้ ผมจะเล่าให้ฟังถึงวิธีที่ผมสร้างต้นแบบคอมโพเนนต์ Sidenav สำหรับเว็บ ที่ตอบสนองได้ทุกสถานะ รองรับการไปยังส่วนต่างๆ ด้วยแป้นพิมพ์ ทำงานกับและไม่ใช้ JavaScript และทำงานในเบราว์เซอร์ต่างๆ ได้ ทดลองใช้การสาธิต

หากชอบวิดีโอ นี่คือโพสต์นี้เวอร์ชัน YouTube

ภาพรวม

การสร้างระบบการนำทางที่ปรับเปลี่ยนตามอุปกรณ์นั้นเป็นเรื่องยาก ผู้ใช้บางรายจะใช้แป้นพิมพ์ บางคนใช้เดสก์ท็อปที่มีประสิทธิภาพ และบางคนเข้าชมจากอุปกรณ์เคลื่อนที่ขนาดเล็ก ทุกคนที่เข้าชมควรเปิดและปิดเมนูได้

การสาธิตเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์จากเดสก์ท็อปถึงอุปกรณ์เคลื่อนที่
ธีมสว่างและมืดใน iOS และ Android

กลยุทธ์สำหรับเว็บ

ในการสำรวจคอมโพเนนต์นี้ ฉันสนุกกับการรวมฟีเจอร์แพลตฟอร์มเว็บที่สำคัญ 2-3 รายการ ได้แก่

  1. CSS :target
  2. ตารางกริด CSS
  3. transformsของ CSS
  4. CSS Media Query สำหรับวิวพอร์ตและค่ากำหนดของผู้ใช้
  5. JS สำหรับ การเพิ่มประสิทธิภาพ UX ของ focus

โซลูชันของฉันมีแถบด้านข้าง 1 แถบและสลับเปิด/ปิดเฉพาะเมื่ออยู่ที่วิวพอร์ต "อุปกรณ์เคลื่อนที่" ที่ 540px หรือน้อยกว่า 540px จะเป็นจุดพักสำหรับการสลับระหว่างเลย์เอาต์แบบอินเทอร์แอกทีฟสำหรับอุปกรณ์เคลื่อนที่กับเลย์เอาต์เดสก์ท็อปแบบคงที่

คลาสเทียมของ CSS :target

ลิงก์ <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 คือตารางกริดที่สร้าง 1 แถวและ 2 คอลัมน์ โดย 1 ในแต่ละรายการมีชื่อว่า 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 เพื่อหาอัตราส่วนที่คุณต้องการสำหรับการวางซ้อนเมนูและปุ่มปิดพื้นที่ลบ

การสาธิตสิ่งที่จะเกิดขึ้นเมื่อคุณเปลี่ยนอัตราส่วน

การแปลงและการเปลี่ยนแบบ 3D ของ CSS

ตอนนี้เลย์เอาต์ของเราจะซ้อนกันที่ขนาดวิวพอร์ตบนอุปกรณ์เคลื่อนที่ ผมจะซ้อนทับบทความของเราโดยค่าเริ่มต้นจนกว่าผมจะเพิ่มสไตล์ใหม่ๆ ตัวอย่าง UX ที่ฉันกำลังถ่ายทำในส่วนถัดไปมีดังนี้

  • ทำให้ภาพเคลื่อนไหวเปิดและปิด
  • สร้างภาพเคลื่อนไหวแบบภาพเคลื่อนไหวก็ต่อเมื่อผู้ใช้ตกลง
  • ทำให้ 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 ไปยังตำแหน่ง "ใน" ของ 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>
การสาธิต UX ของเสียงบรรยายและการโต้ตอบด้วยแป้นพิมพ์

ตอนนี้ปุ่มการโต้ตอบหลักของเราจะระบุอย่างชัดเจนทั้งความตั้งใจในการใช้เมาส์และแป้นพิมพ์

: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 ออกเมื่อปิดเมนู ทำให้เหมือนว่าเมนูไม่ได้เปิดไว้

โฟกัส 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

บทสรุป

ตอนนี้คุณก็รู้แล้วว่าผมทำยังไงแล้วคุณล่ะ คุณจะต้องทำอย่างไร วิธีนี้ทำให้โครงสร้างคอมโพเนนต์สนุกขึ้นได้ ใครจะเป็นผู้สร้างเวอร์ชันแรกที่มีสล็อต 🙂

มาเพิ่มความหลากหลายให้กับวิธีการของเรา และเรียนรู้ทุกวิธีในการสร้างเว็บ สร้างกลิทช์ ทวีตฉันถึงเวอร์ชันของคุณ แล้วฉันจะเพิ่มไปยังส่วนรีมิกซ์ของชุมชนด้านล่าง

รีมิกซ์ของชุมชน