CSS
Advanced & Best Practices
Dark Mode (prefers-color-scheme)
เรียนรู้การออกแบบ dark mode ด้วย media query prefers-color-scheme — ปรับสี ความเปรียบต่าง และ hierarchy ให้อ่านง่ายทั้งในสภาพแวดล้อมมืดและสว่าง
หัวข้อนี้คืออะไร
Dark mode คือชุดสีที่ออกแบบสำหรับสภาพแวดล้อมที่มีแสงน้อย หรือสำหรับผู้ใช้ที่ชอบธีมมืด โดยมักใช้พื้นหลังสีเข้มและตัวอักษรสีอ่อน CSS มี media query ที่ชื่อว่า prefers-color-scheme ซึ่งตรวจสอบการตั้งค่า OS ของผู้ใช้ได้โดยตรง ทำให้เว็บปรับธีมให้อัตโนมัติตามความชอบของผู้ใช้ สิ่งสำคัญที่ต้องเข้าใจ: dark mode ไม่ใช่แค่ "กลับสีดำขาว" แต่ต้องออกแบบใหม่ทั้งระบบ ทั้ง contrast ระหว่าง text กับ background, สี border, เงา และ hierarchy ของข้อมูล
/* ตรวจสอบว่าผู้ใช้ต้องการ dark mode */
@media (prefers-color-scheme: dark) {
body {
background-color: #0f172a; /* พื้นหลังเข้ม */
color: #e2e8f0; /* ตัวอักษรสว่าง */
}
}
/* ตรวจสอบว่าผู้ใช้ต้องการ light mode */
@media (prefers-color-scheme: light) {
body {
background-color: #ffffff;
color: #1e293b;
}
}ทำไมหัวข้อนี้สำคัญ
ปัจจุบัน iOS, Android, macOS, Windows ต่างมีระบบ dark mode ในตัว ผู้ใช้จำนวนมากเปิด dark mode ตลอดเวลา เพราะช่วยลดความเมื่อยล้าของดวงตาในที่มืด และประหยัดพลังงานบน OLED screen ถ้าเว็บไม่รองรับ dark mode ผู้ใช้จะเห็นหน้าจอสว่างจ้าในความมืด ซึ่งสร้างความไม่สบายอย่างมาก การรองรับ dark mode ยังเป็นหนึ่งในตัวชี้วัดของ Web Accessibility (การเข้าถึงได้) และ User Experience ที่ดีในยุคปัจจุบัน นักพัฒนาที่เขียน dark mode ได้ถือว่ามีทักษะการดูแล UI ที่ครอบคลุมกว่า
ตัวอย่างจากชีวิตจริง
แอปพลิเคชันที่ใช้งานในชีวิตประจำวันส่วนใหญ่รองรับ dark mode แล้ว เช่น YouTube, Twitter/X, GitHub, VS Code, Notion สังเกตว่าแอปเหล่านี้ไม่ได้แค่กลับสีพื้นหลังเป็นดำและตัวอักษรเป็นขาว แต่ใช้สีเทาหลายระดับเพื่อสร้าง hierarchy เช่น พื้นหลังหลักเป็นเทาเข้มมาก, card เป็นเทาเข้มน้อยลง, text สำคัญเป็นขาว, text รองเป็นเทาอ่อน GitHub เปลี่ยน background จาก #ffffff เป็น #0d1117, card จาก #f6f8fa เป็น #161b22, text จาก #24292f เป็น #e6edf3 — ไม่ใช่แค่กลับสี แต่เลือกสีให้ contrast ดีและอ่านง่ายในทุกบริบท
แนวคิดหลักที่ต้องเข้าใจ
prefers-color-scheme เป็น media query ที่มี 2 ค่า: - dark — ผู้ใช้เลือก dark mode ใน OS - light — ผู้ใช้เลือก light mode ใน OS (ค่า default) วิธีที่นิยมที่สุดคือเขียน light mode เป็น base แล้วใช้ @media (prefers-color-scheme: dark) สำหรับ override ในธีมมืด CSS Variables ช่วยมากในกรณีนี้ เพราะแทนที่จะเขียน override ทุก property ในทุก element เราเพียงแค่เปลี่ยนค่าของ variable ที่ :root ครั้งเดียว ทุก element ที่ใช้ variable นั้นจะอัปเดตพร้อมกันทั้งหมด
| สี | Light Mode | Dark Mode |
|---|---|---|
| พื้นหลังหลัก | #ffffff (ขาว) | #0f172a (เข้มมาก) |
| พื้นหลัง card | #f8fafc (เทาอ่อนมาก) | #1e293b (เข้มน้อยลง) |
| ข้อความหลัก | #0f172a (ดำเกือบ) | #f1f5f9 (ขาวอ่อน) |
| ข้อความรอง | #475569 (เทา) | #94a3b8 (เทาอ่อน) |
| Border | #e2e8f0 (เทาจาง) | #1e293b / #334155 (เทาเข้ม) |
การทำงานทีละขั้นตอน
- กำหนดสี light mode เป็น base — เขียน CSS ปกติสำหรับธีมสว่างก่อน เช่น body { background: #fff; color: #1e293b; }
- เพิ่ม @media (prefers-color-scheme: dark) — ใส่ block นี้ไว้หลัง base styles แล้ว override สีที่ต้องเปลี่ยน
- เปลี่ยนสีที่จำเป็นทั้งหมด — ไม่ใช่แค่ body แต่รวมถึง card, button, input, border, shadow ทุก element ที่มีสี
- ตรวจสอบ contrast — ตัวอักษรสีอ่อนบนพื้นมืดต้องมี contrast ratio ไม่ต่ำกว่า 4.5:1 สำหรับข้อความทั่วไป
- ทดสอบจริง — เปลี่ยน OS เป็น dark mode แล้วดูว่าทุก element อ่านง่ายและใช้งานได้ โดยเฉพาะ interactive elements
ตัวอย่างที่ 1 — Dark Mode พื้นฐาน (Body & Text)
จุดเริ่มต้นที่ง่ายที่สุด: เปลี่ยนสีพื้นหลังและตัวอักษรของ body ให้รองรับ dark mode โดยใช้ media query
ตัวอย่างที่ 2 — Dark Mode ด้วย CSS Variables
วิธีที่ดีกว่าคือกำหนด color tokens ใน CSS Variables แล้วเปลี่ยนแค่ค่า variable เมื่อ dark mode — ทุก element ที่ใช้ variable จะอัปเดตพร้อมกันทันที
ตัวอย่างที่ 3 — ❌ Dark Mode ที่ Contrast ไม่ดี vs ✅ ที่ถูกต้อง
ข้อผิดพลาดที่พบบ่อยที่สุดใน dark mode คือ contrast ไม่เพียงพอ ข้อความสีเทากลางบนพื้นเทาเข้มทำให้อ่านยากมาก
จุดที่ผู้เริ่มต้นมักสับสน
- Dark mode ≠ กลับสีดำขาว — การ invert สีทั้งหมดจะทำให้รูปภาพ, ไอคอน, และ gradient ดูแปลก ควรออกแบบ color palette ใหม่สำหรับ dark mode โดยเฉพาะ
- Contrast ใน dark mode ไม่ได้แปลว่า 'ขาวสว่างจ้า' — สีขาวบริสุทธิ์ (#ffffff) บนพื้นมืดทำให้ดวงตาล้าได้ ควรใช้ off-white เช่น #e2e8f0 หรือ #f1f5f9 แทน
- Shadow ใน dark mode ต้องเข้มกว่า — shadow แบบปกติที่ใช้สีดำโปร่งแสงจะมองไม่เห็นบนพื้นหลังมืด ต้องปรับ opacity หรือใช้ glow effect แทน
- prefers-color-scheme ตรวจ OS ไม่ใช่เบราว์เซอร์ — ถ้า OS อยู่ใน dark mode แม้เบราว์เซอร์ไม่ได้ตั้ง dark mode เอง เว็บก็จะแสดง dark mode ตาม OS
เปรียบเทียบวิธีทำ Dark Mode
| วิธี | ข้อดี | ข้อจำกัด |
|---|---|---|
| @media prefers-color-scheme | อัตโนมัติตาม OS, ไม่ต้องใช้ JS | ผู้ใช้ไม่สามารถ toggle ใน web ได้ |
| CSS Variables + media query | จัดการสีเป็นระบบ, เปลี่ยนที่เดียวทุกที่อัปเดต | ต้องวางแผน token ล่วงหน้า |
| JavaScript toggle class | ผู้ใช้ toggle ได้ใน web, จำ preference ได้ | ต้องใช้ JS, ซับซ้อนกว่า |
| color-scheme property | บอก browser ให้ render scrollbar/form ตาม theme | ไม่ได้เปลี่ยน custom CSS ให้อัตโนมัติ |
สรุปท้ายบทแบบจำง่าย
Dark mode ที่ดีคือ dark mode ที่ผู้ใช้อ่านง่าย ไม่ใช่ dark mode ที่ดูเท่ที่สุด
- @media (prefers-color-scheme: dark) — ตรวจการตั้งค่า OS แล้ว override สีใน block นี้
- ใช้ CSS Variables — กำหนด color tokens ใน :root แล้วเปลี่ยนค่าใน media query ครั้งเดียว
- Contrast ratio ≥ 4.5:1 — ตรวจสอบให้แน่ใจทั้งใน light และ dark mode
- ไม่ใช่แค่ body — ต้องปรับ card, button, input, border, shadow ทุก element
- Off-white ดีกว่าขาวจ้า — ใช้ #e2e8f0 หรือ #f1f5f9 แทน #ffffff สำหรับ text หลักใน dark mode
Lab 1 — เพิ่ม Dark Mode พื้นฐาน
CSS ปัจจุบันมีแค่ light mode ให้เพิ่ม @media (prefers-color-scheme: dark) เพื่อให้ body มีพื้นหลังมืดและตัวอักษรสว่าง และให้ .card มีพื้นหลังและ border ที่เหมาะสมกับ dark mode
Lab 2 — Dark Mode ด้วย CSS Variables
CSS นี้ใช้ CSS Variables แต่ยังไม่มี dark mode ให้เพิ่ม @media (prefers-color-scheme: dark) เพื่อเปลี่ยนค่า variable ใน :root เพียงที่เดียว ทุก element จะอัปเดตพร้อมกันทันที
Lab 3 — แก้ Dark Mode ที่ Contrast ไม่ดี
dark mode นี้ใช้สีที่ contrast ไม่ดี ข้อความอ่านยากมาก ให้แก้สีของ --text, --text-muted, และ --surface ใน @media (prefers-color-scheme: dark) ให้มี contrast ที่ดีขึ้น