ปุ่มสลับโหมดมืด

ฟีเจอร์บนเว็บ: color-scheme prefers-color-scheme media query light-dark() :has()

ปุ่มสลับโหมดมืดช่วยให้ผู้ใช้ลบล้างรูปแบบสีของระบบสำหรับเว็บไซต์เดียวได้ การลบล้างจะมีผลโดยการอัปเดต <meta name="color-scheme">: light dark หมายถึง "ทำตามค่ากำหนดของระบบ" light หรือ dark ปักหมุดรูปแบบที่ชัดเจน สี (light-dark()) สีของระบบ และ UI แบบเนทีฟทั้งหมดจะปิดพร็อพเพอร์ตี้ color-scheme ของ CSS ดังนั้นปุ่มเปิด/ปิดจึงทำงานโดยการซิงค์พร็อพเพอร์ตี้นั้นกับ <meta> (ดูแสดงการลบล้างใน CSS)

ปุ่มเปิด/ปิดแบบถาวร การควบคุมการตั้งค่า หรือไม่มีปุ่มเปิด/ปิดเลย

เว็บไซต์ส่วนใหญ่ไม่จำเป็นต้องมีปุ่มสลับรูปแบบสีแบบถาวรใน UI ของเว็บไซต์ (ส่วนหัว, การนำทาง, ส่วนท้าย ฯลฯ) ทำตามค่ากำหนดของระบบโดยค่าเริ่มต้น และหากจำเป็นต้องลบล้างด้วยตนเอง ให้วางตัวควบคุมไว้ในแพลตฟอร์มการตั้งค่าแยกต่างหาก เช่น หน้าการตั้งค่า แผง หรือการวางซ้อน

การปักหมุดรูปแบบกับระบบปฏิบัติการเป็นความตั้งใจที่ถูกต้องตามกฎหมายแต่เกิดขึ้นไม่บ่อยนัก และความตั้งใจที่เกิดขึ้นไม่บ่อยนักควรอยู่เบื้องหลังการเปิดเผยข้อมูลแบบต่อเนื่อง ไม่ใช่ในการควบคุมที่มองเห็นได้ตลอดเวลา

ปุ่มเปิด/ปิดแบบถาวรมักพบในเว็บไซต์ที่แสดงต่อนักพัฒนาซอฟต์แวร์หรือเว็บไซต์ที่เกี่ยวข้องกับนักพัฒนาซอฟต์แวร์ (เช่น สำหรับนักออกแบบ) ซึ่งการเปลี่ยนรูปแบบเป็นงานที่ทำบ่อยกว่าผู้ใช้เว็บทั่วไปมาก อย่าเพิ่มเพียงเพราะเป็นรูปแบบที่พบได้ทั่วไปในเว็บไซต์ของนักพัฒนาซอฟต์แวร์ เนื่องจากความสนใจของนักพัฒนาซอฟต์แวร์ในการควบคุมนี้ไม่ได้แสดงถึงผู้ใช้ทั่วไป

การใช้งาน

ก่อนอื่น ให้ทำตามขั้นตอนทั้งหมดในโหมดมืดเพื่อรองรับทั้ง 2 รูปแบบ โดยค่าเริ่มต้นจะเป็นค่ากำหนดของระบบ

โมเดลข้อมูล

โดยทั่วไปแล้ว โมเดลข้อมูลสำหรับการสลับโหมดมืดจะมี 3 สถานะ ได้แก่

  1. ระบบ (ไม่มีค่าที่จัดเก็บไว้): UI จะเป็นไปตามรูปแบบสีของระบบ ซึ่งควรเป็นค่าเริ่มต้น
  2. สว่าง (จัดเก็บเป็น "light"): UI จะใช้ธีมสว่างไม่ว่ารูปแบบสีของระบบจะเป็นอย่างไร
  3. มืด (จัดเก็บเป็น "dark"): UI จะใช้ธีมมืดโดยไม่คำนึงถึงรูปแบบสีของระบบ

โดยปกติแล้วสถานะนี้จะจัดเก็บไว้ใน localStorage และแสดงในองค์ประกอบ <meta name="color-scheme"> ในส่วนหัวของเอกสาร

แสดงการลบล้างใน CSS

<meta> เป็นเพียงคำแนะนำในการนำเสนอ: การประกาศ color-scheme ใดๆ ใน CSS ของผู้เขียนจะลบล้างคำแนะนำดังกล่าว เนื่องจากโหมดมืดตั้งค่า color-scheme: light dark ใน :root การอัปเดต <meta> เพียงอย่างเดียวจึงไม่มีผล เนื่องจาก CSS มีผลเหนือกว่า แมป <meta> กลับไปยังพร็อพเพอร์ตี้เพื่อให้การเรียงซ้อนเป็นไปตามนั้น

:root {
  color-scheme: light dark;

  &:has(> head > meta[name="color-scheme"][content="dark"]) {
    color-scheme: dark;
  }
  &:has(> head > meta[name="color-scheme"][content="light"]) {
    color-scheme: light;
  }
}

อย่าฮาร์ดโค้ด color-scheme: light หรือ color-scheme: dark เป็นค่าเริ่มต้นของรูท การประกาศฐานต้องเป็น light dark CSS ต้องไม่ขึ้นอยู่กับ JS: หาก JS ไม่เคยทำงาน <meta> จะยังคงเป็น light dark และเว็บไซต์จะเป็นไปตามค่ากำหนดของระบบ โดยไม่มีอะไรเสียหาย

การคงอยู่และการป้องกัน FOUC

คงการลบล้างไว้ใน localStorage และนำรายการออกเมื่อกลับไปใช้ค่าเริ่มต้นของระบบ

หากต้องการหลีกเลี่ยงการแสดงธีมที่ไม่ถูกต้อง ให้ใช้ค่าที่จัดเก็บไว้ในสคริปต์ในบรรทัด (ไม่ใช่ type=module และไม่ใช่ defer) ซึ่งวางไว้ต่อจากองค์ประกอบ <meta> ทันที ดังนี้

<meta name="color-scheme" content="light dark">
<script>
document.querySelector('meta[name="color-scheme"]').content = localStorage.getItem("color-scheme") ?? "light dark";
</script>
  • สคริปต์ในบรรทัดนี้มีไว้เพื่อป้องกัน FOUC เท่านั้น ทำให้มีขนาดเล็กที่สุดเท่าที่จะเป็นไปได้ โดย JS เพิ่มเติมสำหรับการจัดการการสลับรูปแบบสีจะโหลดในภายหลังได้
  • ในบางกรณีที่เกิดขึ้นไม่บ่อยนัก การเข้าถึง localStorage อาจทำให้เกิดข้อผิดพลาด (เช่น ข้อมูลเว็บไซต์ถูกบล็อก) แต่เนื่องจากสคริปต์นี้ไม่ได้ทำอะไรอย่างอื่น ผลข้างเคียงเพียงอย่างเดียวคือข้อผิดพลาดในคอนโซล รวมใน try .. catch หากเป็นเรื่องสำคัญ ตรวจสอบว่ายังคงใช้ธีมกับหน้าเว็บอยู่ แม้ว่าจะไม่สามารถคงธีมไว้ได้ก็ตาม
  • การลบล้างสามารถเปลี่ยนจากแท็บอื่นได้: จัดการเหตุการณ์ storage ของ window เพื่อให้ซิงค์อยู่เสมอ
  • อย่าใช้ matchMedia() เพื่อนำค่าที่จัดเก็บไว้ออกเมื่อค่ากำหนดของระบบเปลี่ยนไปตรงกับค่านั้น ระบบปฏิบัติการของผู้ใช้หลายรายจะเปลี่ยนรูปแบบโดยอัตโนมัติตามช่วงเวลาของวัน การนำค่าที่จัดเก็บไว้ออกเมื่อทั้ง 2 อย่างเกิดขึ้นพร้อมกันจะทำให้ปักหมุดรูปแบบไม่ได้เลย

การแยกสาขาสำหรับ HTML และค่าที่ไม่ใช่สี

คุณไม่ต้องดำเนินการใดๆ เพิ่มเติมเกี่ยวกับสี เนื่องจากเมื่อจับคู่ <meta> กับ color-scheme (ด้านบน) แล้ว light-dark() จะเป็นไปตามการลบล้าง ส่วนนี้เกี่ยวกับการปรับค่าอื่นๆ (เช่น font-weight, แหล่งที่มาของสื่อ ฯลฯ)

เมื่อหน้าเว็บเป็นไปตามค่ากำหนดของระบบ คุณสามารถใช้ (prefers-color-scheme: dark) สำหรับการแยกสาขา รวมถึงในแหล่งที่มา <picture> หรือ <video> การลบล้างรูปแบบสีจะเพิ่มความซับซ้อนมากขึ้น เนื่องจากค่ากำหนดของระบบจะไม่ใช่แหล่งข้อมูลที่ถูกต้องอีกต่อไป และสำหรับ API ที่ยอมรับเฉพาะคิวรี่สื่อจะไม่มีทางเลือกอื่นโดยตรง

อย่าพึ่งพาคลาสที่ใช้ JS เช่น .dark สำหรับการแยกสาขาเพียงอย่างเดียว เนื่องจากคลาสเหล่านี้จะไม่ถูกต้องหาก JS ไม่โหลดและค่าเริ่มต้นของระบบปฏิบัติการเป็นโหมดมืด

คุณสามารถรวมการสืบค้นสื่อกับตัวเลือก :root:has(> head > meta[name="color-scheme"][content="dark"]) (หรือ light) เพื่อแยกสาขาได้ ดูรายละเอียดเกี่ยวกับวิธีใช้ตัวเลือกนี้อย่างมีประสิทธิภาพได้ที่ชุดค่าผสมของตัวเลือกและ @-rule

ตัวเลือกของตัวควบคุมเองก็เป็นสาขาที่ไม่มีสีเช่นกัน โดยรวมทั้ง 2 สถานะไว้ในมาร์กอัป (ไอคอนบวกข้อความการดำเนินการที่ซ่อนอยู่ เช่น "เปลี่ยนเป็นธีมมืด") และซ่อนสถานะที่ไม่ได้ใช้งานผ่านตัวเลือกเหล่านี้ display: none ยังนำตัวเลือกที่ซ้ำซ้อนออกจาก AT ด้วย ดังนั้นการควบคุมจึงไม่จำเป็นต้องใช้ JS เพื่อให้เป็นปัจจุบันเมื่อค่ากำหนดของระบบเปลี่ยนแปลง

สำหรับสื่อใน HTML วิธีเดียวในตอนนี้คือการรวมทั้ง 2 เวอร์ชันเป็นองค์ประกอบแยกต่างหากและสลับระดับการเข้าถึงอย่างเหมาะสม การซ่อนองค์ประกอบ <source> ด้วย display: none ไม่ทำงาน

อินเทอร์เฟซผู้ใช้

2 หรือ 3 รัฐ

ในทุกกรณี UI ต้องรองรับการเปลี่ยนกลับหรือตั้งค่ารูปแบบสีเป็นระบบ

แนะนำให้แสดง 2 สถานะในปุ่มเปิด/ปิดที่มีพื้นที่จำกัด เช่น ปุ่มเปิด/ปิดแบบถาวรใน Chrome ของเว็บไซต์ (ส่วนหัว, การนำทาง, ส่วนท้าย ฯลฯ) ซึ่งมีพื้นที่จำกัดและการปรับแต่งการตั้งค่ามักจะไม่เกี่ยวข้องกับเป้าหมายของผู้ใช้

การควบคุมรูปแบบสีในแพลตฟอร์มการตั้งค่าแยกต่างหาก (หน้า แผง หรือการวางซ้อน) เป็นสถานการณ์ที่แตกต่างกัน และอาจแสดงทั้ง 3 สถานะอย่างชัดเจน ("สว่าง" "มืด" "ระบบ") ซึ่งในกรณีนี้ ผู้ใช้ได้ตัดสินใจอย่างรอบคอบเกี่ยวกับลักษณะการทำงานในอนาคตแล้ว และมีพื้นที่สำหรับอธิบายตัวเลือกต่างๆ

นอกจากนี้ยังรับประกันสถานะที่ชัดเจน 3 สถานะหากเว็บไซต์ใช้รูปแบบที่คำนึงถึงบริบท (เช่น โหมดแสงที่หรี่ลงเมื่อระบบปฏิบัติการมืด) เนื่องจาก "สว่าง" และ "ระบบ (สว่างอยู่)" จะแตกต่างกันอย่างแท้จริง

ปุ่มสลับ 2 สถานะ

เมื่อใช้การควบคุมดังกล่าว มักจะเป็นการปรับความสบายชั่วคราว ("ตอนนี้สว่างเกินไป") แทนที่จะเป็นการตรวจสอบว่าสถานะปัจจุบันได้รับการคงไว้ในระยะยาว ดังนั้น สำหรับปุ่มเปิด/ปิด 2 สถานะ การแสดงการตั้งค่า 2 ใน 3 รายการก็เพียงพอแล้ว ได้แก่ ระบบ (เพื่อให้เปลี่ยนการตั้งค่ากลับได้) และค่าตรงข้ามปัจจุบันของระบบ ซึ่งจัดเก็บเป็นค่าลิเทอรัลปัจจุบัน

สถานะทั้ง 2 สถานะควรเป็นดังนี้

  1. ค่าเริ่มต้นของระบบ - ไม่มีค่าที่จัดเก็บไว้, เนื้อหา <meta> light dark แสดงเป็นค่าที่แก้ไขแล้วในปัจจุบัน (เช่น ไอคอนดวงอาทิตย์เมื่อสว่าง)
  2. ลบล้าง - จัดเก็บตามตัวอักษรเป็น light หรือ dark

โดยพื้นฐานแล้ว การควบคุมนี้เป็นแบบ 3 สถานะ (light dark, light, dark) ซึ่งสถานะที่ชัดเจนซึ่งตรงกับค่ากำหนดของระบบปัจจุบันไม่สามารถเข้าถึงได้

เมื่อผู้ใช้สลับตัวเลือก

  1. รูปแบบเป้าหมาย = ตรงข้ามกับรูปแบบที่แสดงผลอยู่ในปัจจุบัน (ค่าที่จัดเก็บไว้ หากมี หรือค่ากำหนดของระบบ) ความตั้งใจของผู้ใช้คือ "เลือกสิ่งที่ตรงข้ามกับที่ฉันเห็นอยู่ขณะนี้" ไม่ใช่ "เลือกสิ่งที่ตรงข้ามกับค่าเริ่มต้นของระบบ"
  2. หากเป้าหมายแตกต่างจากค่ากำหนดของระบบปัจจุบัน ให้จัดเก็บตามตัวอักษร
  3. หากเป้าหมายตรงกับค่ากำหนดของระบบปัจจุบัน แสดงว่าผู้ใช้กำลังยกเลิกการปรับเปลี่ยน ให้นำค่าที่จัดเก็บไว้ออก อย่าจัดเก็บไว้ เพราะจะทำให้รูปแบบดังกล่าวถูกปักหมุดไว้โดยไม่แสดงให้เห็นเพื่อป้องกันการเปลี่ยนแปลงระบบในอนาคต
  4. ตั้งค่าเนื้อหา <meta> เป็นค่าที่จัดเก็บไว้ หรือ light dark หากไม่มี

สำคัญ: ต้องตรวจสอบความแตกต่างเฉพาะในเวลาที่จัดเก็บเท่านั้น ไม่สามารถตรวจสอบย้อนหลังได้ โดยต้องเก็บค่าที่จัดเก็บไว้ซึ่งค่ากำหนดของระบบเปลี่ยนให้ตรงกันในภายหลัง ระบบปฏิบัติการของผู้ใช้หลายรายจะเปลี่ยนรูปแบบโดยอัตโนมัติตามช่วงเวลาของวัน การนำค่าที่จัดเก็บไว้ออกเมื่อทั้ง 2 อย่างเกิดขึ้นพร้อมกันจะทำให้ปักหมุดรูปแบบไม่ได้เลย

ตัวอย่างสถานการณ์ โดยเริ่มจากระบบปฏิบัติการที่ตั้งค่าเป็นธีมสว่าง

  1. ผู้ใช้สลับ ธีมมืดแตกต่างจากค่ากำหนดของระบบ ระบบจึงจัดเก็บ dark ไว้ และเว็บไซต์จะเปลี่ยนเป็นธีมมืด
  2. การตั้งค่าระบบปฏิบัติการจะเปลี่ยนเป็นโหมดมืด เว็บไซต์จะยังคงเป็นสีมืด (ค่าที่จัดเก็บไว้ตรงกับค่ากำหนดของระบบ แต่จะยังคงอยู่)
  3. การตั้งค่าระบบปฏิบัติการจะเปลี่ยนกลับเป็นธีมสว่าง เว็บไซต์จะยังคงเป็นสีมืด
  4. ผู้ใช้สลับ เป้าหมาย (สว่าง) ตรงกับค่ากำหนดของระบบ ระบบจึงนำค่าที่จัดเก็บไว้ออกและเว็บไซต์จะทำตามระบบอีกครั้ง

ปุ่มสลับ 3 สถานะ

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

เพื่อลดภาระการรับรู้ ตัวเลือก "ระบบ" ควรแสดงการตั้งค่าระบบปัจจุบันด้วย เช่น ผ่านทาง:

  • ไอคอน เช่น ไอคอนดวงอาทิตย์ที่ด้านขวาล่างของไอคอนระบบ
  • ข้อความ เช่น "ระบบ (ปัจจุบันเป็นธีมสว่าง)"