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

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

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

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

ภาพรวม

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

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

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

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

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

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

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

การแปลง CSS 3 มิติและ ทรานซิชัน

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

บทสรุป

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

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

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