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

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

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

หากต้องการดูวิดีโอ โปรดใช้โพสต์นี้ในเวอร์ชัน YouTube

ภาพรวม

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

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

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

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

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

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

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

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

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

การแปลงและการเปลี่ยนแบบ 3 มิติ 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 ไปยังตำแหน่ง "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>
การสาธิตเสียงบรรยายและ 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

บทสรุป

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

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

รีมิกซ์ในชุมชน