ธีมสว่าง/มืดเฉพาะคอมโพเนนต์

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

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

ตัวอย่างกรณีการใช้งานมีดังนี้

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

ควรเปลี่ยนสีเมื่อใดและควรบังคับใช้ color-scheme เมื่อใด

ไม่ใช่องค์ประกอบทุกอย่างที่ใช้ข้อความสีอ่อนบนพื้นหลังสีเข้มในโหมดสว่างหรือข้อความสีเข้มบนพื้นหลังสีอ่อนในโหมดมืดจะต้องมี color-scheme ที่แตกต่างกัน ตัวอย่างเช่น ปุ่มหลักอาจแสดงเป็นสีน้ำเงินพร้อมข้อความสีขาวในโหมดสว่าง แต่ไม่ได้หมายความว่าจะมี color-scheme: dark

โดยทั่วไปแล้ว องค์ประกอบที่ใช้ color-scheme ต่างกันจะเป็นพื้นผิวที่ซับซ้อนซึ่งสร้างบริบทภาพของตนเอง ไม่ใช่คอนเทนเนอร์แบบตื้นๆ ที่เรียบง่าย

เมื่อพิจารณาใช้ color-scheme อื่นในองค์ประกอบ ให้ถามตัวเองดังนี้

  • UI ของเบราว์เซอร์ในตัวที่ไม่ได้ปรับแต่ง (เช่น ตัวควบคุมแบบฟอร์ม แถบเลื่อน ฯลฯ) ควรใช้รูปแบบสีนั้นหรือปรับให้เข้ากับรูปแบบสีของหน้าเว็บไหม -> if the former, don't use color-scheme.
  • สี light-dark() ควรแสดงผลเหมือนกับสีอื่นๆ ในหน้าเว็บหรือตามการลบล้าง -> if the former, don't use color-scheme.
  • ควรให้ผู้สืบทอดอยู่ใน color-scheme นั้นไหม หากไม่มีเจาะจง อย่าใช้ color-scheme

การติดตั้งใช้งานขั้นพื้นฐาน

โดยทั่วไปแล้ว (แม้ว่าจะไม่จำเป็นอย่างเคร่งครัด) ระบบจะใช้การลบล้างเฉพาะคอมโพเนนต์ในหน้าเว็บที่รองรับรูปแบบสีหลายรูปแบบผ่าน color-scheme ส่วนกลางด้วย ดูวิธีใช้โหมดมืดทั่วทั้งหน้าเว็บอย่างมีประสิทธิภาพได้ที่โหมดมืด

เมื่อมี color-scheme ทั่วทั้งหน้า และคุณใช้โทเค็นสีที่ไวต่อโทเค็นดังกล่าว (เช่น ผ่าน light-dark()) คุณสามารถตั้งค่า color-scheme ในคอมโพเนนต์ที่เฉพาะเจาะจงเพื่อลบล้างโหมดสีสำหรับซับทรีนั้นได้ ดังนี้

pre, code, .dark {
  color-scheme: dark;
}

โปรดทราบว่าเบราว์เซอร์บางรายการจะปรับคอมโพเนนต์ให้เข้ากับรูปแบบสีอื่นโดยอัตโนมัติอยู่แล้ว หากต้องการบังคับใช้รูปแบบสีที่ระบุในทุกกรณี ให้ใช้ only เช่น color-scheme: only dark; แทน color-scheme: dark;

การปรับค่าที่ไม่ใช่สี

ปัจจุบัน light-dark() ใช้ได้กับสีเท่านั้น

แนวทางปฏิบัติแนะนำ

  • บังคับ: อย่าตั้งค่า color-scheme ในองค์ประกอบที่ไม่มีพื้นหลัง เนื่องจากอาจทำให้เกิดการผสมคู่สีพื้นหลังและสีข้อความจากรูปแบบสีที่ต่างกัน ซึ่งส่งผลให้ข้อความอ่านไม่ได้
  • ไม่บังคับ: แม้ว่าการใช้คู่สีเดียวกันกับโหมดมืดทั้งหน้าจะง่ายกว่า แต่เราสามารถกำหนดคู่สีที่แตกต่างกันสำหรับคอมโพเนนต์เหล่านี้ได้ ตัวอย่างเช่น เราอาจต้องการให้คอมโพเนนต์โหมดมืดที่ใช้ในหน้าโหมดสว่างมีความมืดน้อยกว่าเมื่อใช้คอมโพเนนต์โหมดมืดเดียวกันในหน้าเว็บที่โดยรวมเป็นโหมดมืด

ปัญหาที่ทราบซึ่งควรคำนึงถึง

ข้อควรระวัง: การรับค่าสี light-dark()

light-dark() จะได้รับการแก้ไขเมื่อถึงเวลาที่คำนวณค่า ซึ่งหมายความว่าพร็อพเพอร์ตี้ <color> ที่รับค่ามาซึ่งตั้งค่าเป็นสี light-dark() จะส่งต่อสีใดสีหนึ่งจาก 2 สีไปยังองค์ประกอบสืบทอดเท่านั้น ไม่ใช่ตัวนิพจน์ light-dark() เอง

ซึ่งรวมถึงเนื้อหาต่อไปนี้

  • พร็อพเพอร์ตี้สีในตัวที่รับค่ามา เช่น color, accent-color, fill, stroke, text-shadow, caret-color
  • คุณสมบัติที่กำหนดเองซึ่งรับค่าได้ที่ลงทะเบียนไว้ซึ่งมี syntax: <color> และ inherits: true
  • ตั้งค่าพร็อพเพอร์ตี้ <color> อื่นๆ เป็น inherit

ซึ่งหมายความว่าคุณควรทำดังนี้

  • อย่าลงทะเบียนคุณสมบัติที่กำหนดเองซึ่งมีไว้เพื่อเก็บโทเค็นการออกแบบ (เช่น --surface-color) เป็น <color> โทเค็นต้องคงนิพจน์ light-dark() ไว้เพื่อให้องค์ประกอบสืบทอดสามารถแก้ไขนิพจน์ดังกล่าวอีกครั้งภายใต้ color-scheme อื่น
  • เมื่อตั้งค่า color-scheme ในองค์ประกอบ ให้ระบุพร็อพเพอร์ตี้ <color> ที่รับค่ามาอีกครั้งซึ่งอาจตั้งค่าเป็นค่า light-dark() (โดยตรงหรือผ่านโทเค็นการออกแบบ) แม้ว่าจะเป็นโทเค็นการออกแบบเดียวกันก็ตาม
  • ไม่ใช้ inherit กับพร็อพเพอร์ตี้ <color> ในองค์ประกอบที่มีการลบล้าง color-scheme (ใช้กับองค์ประกอบสืบทอดได้)
  • ควรใช้พร็อพเพอร์ตี้ <color> ที่ลงทะเบียนไว้สำหรับกรณีการใช้งานที่ตรงกันข้าม นั่นคือเมื่อคุณต้องการสแนปชอตสีที่แก้ไขแล้วของระดับบนสุดโดยเจตนา และป้องกันไม่ให้มีการแก้ไขอีกครั้งภายใต้ color-scheme ของระดับล่างสุด เช่น การจับภาพพื้นหลังของหน้าเพื่อใช้ที่อื่น
  • หากต้องการทำให้สีเคลื่อนไหว ให้ใช้พร็อพเพอร์ตี้ <color> ที่ลงทะเบียน @property แยกต่างหากในองค์ประกอบที่เคลื่อนไหว (ต้องลงทะเบียนสำหรับการประมาณค่าสี) ซึ่งไม่ใช่โทเค็นการออกแบบ แต่เป็นเป้าหมายภาพเคลื่อนไหวต่อองค์ประกอบ จึงไม่ขัดแย้งกับกฎข้างต้น

ตัวอย่าง

:root {
    --accent-color: light-dark(blue, skyblue);
    --surface-color: light-dark(white, #222);
    --text-color: light-dark(#111, white);

    color-scheme: light dark;
    accent-color: var(--accent-color);
    color: var(--text-color);
}

body {
    /* --surface-color dynamically switches despite being inherited because --surface-color is not registered */
    background: var(--surface-color);
}

pre, code {
    color-scheme: dark;
    background: var(--surface-color);

    /* Without this, accent-color would be blue, not skyblue! */
    accent-color: var(--accent-color);

    /* Without this, text-color would be #111, not white! */
    color: var(--text-color);
}

ปัญหาที่ควรทราบเมื่อใช้รูปแบบสี

  • Chrome และ Firefox จะใช้ color-scheme สำหรับ iframe โดยจะแสดงผลหน้าที่ฝังในรูปแบบสีที่ถูกต้องและปรับการค้นหาสื่อ prefers-color-scheme ของหน้าที่ฝังเพื่อแสดง color-scheme ของบริบทการฝัง แต่ Safari ไม่รองรับ และจะแปลง prefers-color-scheme เป็นการตั้งค่าระบบแม้จะอยู่ภายใน iframe
    • หากคุณควบคุมทั้งระดับบนสุดและ iframe: ให้ส่งรูปแบบสีของระดับบนสุดไปยัง iframe อย่างชัดเจนผ่านพารามิเตอร์ของ URL (?theme=dark) ณ เวลาที่สร้าง iframe หรือผ่าน postMessage() (ซึ่งช่วยให้คุณตอบสนองต่อการเปลี่ยนแปลงรันไทม์ได้ด้วย) ใน iframe ให้ตั้งค่าคลาสใน <html> (และ/หรือ color-scheme ใน :root) จากสัญญาณนั้นแทนที่จะใช้ prefers-color-scheme
    • หากคุณควบคุมเฉพาะหน้าที่ฝัง: ไม่มีวิธีที่เชื่อถือได้ในการตรวจหา color-scheme ของบริบทการฝังจากภายใน iframe ใน Safari แสดงพารามิเตอร์ธีมที่ชัดเจนใน API การฝัง (เช่น สตริงการค้นหาหรือโปรโตคอล postMessage) และจัดทำเอกสารสำหรับผู้ฝัง

กลยุทธ์สำรอง

การรองรับรูปแบบสีของเบราว์เซอร์และโหมดสำรอง

พร็อพเพอร์ตี้ color-scheme เป็นการเพิ่มประสิทธิภาพแบบต่อเนื่อง เบราว์เซอร์ที่ไม่รองรับจะละเว้นพร็อพเพอร์ตี้นี้และใช้ UI โหมดสว่างเริ่มต้น

หากต้องการปรับให้เข้ากับค่ากำหนดของผู้ใช้ในเบราว์เซอร์รุ่นเก่า ให้ใช้คิวรี่สื่อ prefers-color-scheme เพื่อแสดงสีที่แตกต่างกันเมื่อผู้ใช้ต้องการใช้โหมดมืด

  • ควรใช้การค้นหาสื่อเพื่อสลับคุณสมบัติที่กำหนดเองใน :root หรือ html
  • หลีกเลี่ยงการใช้การค้นหาสื่อในคอมโพเนนต์แต่ละรายการ เว้นแต่คอมโพเนนต์นั้นจะต้องมีการปรับแต่งโหมดมืดที่เฉพาะเจาะจงมากนอกเหนือจากสี
:root {
  /* Define brand colors for each mode */
  --color-brand-light: #0056b3;
  --color-brand-dark: #00e5ff;
  --color-brand: var(--color-brand-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --color-brand: var(--color-brand-dark);
  }

  /* Ignored in older browsers */
  color-scheme: light dark;
}

button.primary {
    background-color: var(--color-brand);
}

การสนับสนุนเบราว์เซอร์และทางเลือกสำรองสำหรับ light-dark()

สำหรับเบราว์เซอร์ที่รองรับ color-scheme แต่ยังไม่รองรับ light-dark() ควรกำหนดเวอร์ชันสีอ่อนและสีเข้มเป็นคุณสมบัติที่กำหนดเองก่อน และควรใช้มีเดียคิวรี prefers-color-scheme เพื่อตั้งค่าสีสำหรับโหมดที่เกี่ยวข้อง ดังตัวอย่างด้านล่าง

:root {
  /* Define browser UI accent color for each mode */
  --brand-accent-light: #0056b3;
  --brand-accent-dark: #00e5ff;
  --accent-color: var(--brand-accent-light);

  /* MANDATORY: Fallback for browsers without light-dark support */
  @media (prefers-color-scheme: dark) {
    --accent-color: var(--brand-accent-dark);
  }

  /* OPTIONAL: use light-dark() for more control of built-in UI colors */
  @supports (color: light-dark(white, black)) {
    --accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
  }

  /* MANDATORY: Automatically adapt native UI to user system preferences */
  color-scheme: light dark;

  /* Example inherited color property */
  accent-color: var(--accent-color);
}

pre, code {
  color-scheme: dark;

  /* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
  accent-color: var(--accent-color);
}