บทเรียนแนะนำ

การใช้งาน Dark Mode: คู่มือสมบูรณ์สำหรับนักพัฒนา

อ่าน 3 นาที

Dark mode ได้เปลี่ยนจากความชอบส่วนตัวของกลุ่มเล็กๆ มาเป็นฟีเจอร์ที่ผู้ใช้คาดหวัง ผู้ใช้บนทุกแพลตฟอร์ม — macOS, Windows, Android, iOS — สามารถตั้งค่าธีมสีเข้มในระดับระบบ และพวกเขาคาดหวังให้เว็บไซต์และแอปรองรับการตั้งค่านั้น การใช้งาน dark mode อย่างถูกต้องต้องการมากกว่าแค่การสลับสีขาวเป็นสีดำ: จำเป็นต้องมีแนวทางที่เป็นระบบต่อสี ความคมชัด และการควบคุมของผู้ใช้ คู่มือนี้จะพาคุณผ่านกระบวนการทั้งหมด ตั้งแต่สถาปัตยกรรม CSS ผ่านกลไก toggle JavaScript ไปจนถึงการทดสอบทั้งสองธีมอย่างละเอียด

CSS Custom Properties สำหรับธีม

วิธีที่ดูแลรักษาง่ายที่สุดสำหรับการจัดการ dark mode ใน CSS คือผ่าน custom properties (เรียกอีกชื่อว่า CSS variables) แทนที่จะกระจายค่าสีทั่วทั้ง stylesheet คุณกำหนดทุกสีเป็นตัวแปรบน :root แล้วนิยามตัวแปรเหล่านั้นใหม่สำหรับ dark mode สไตล์ของ component อ้างอิงเฉพาะตัวแปร — ไม่มีโค้ด hex โดยตรง

การกำหนดจานสีสว่างและสีเข้ม

เริ่มต้นด้วยจานสี light mode เป็นค่าเริ่มต้น:

:root {
  /* พื้นหลัง */
  --color-bg-base:      #FFFFFF;
  --color-bg-elevated:  #F8F9FA;
  --color-bg-overlay:   #F1F3F5;

  /* ข้อความ */
  --color-text-primary:   #1A1A2E;
  --color-text-secondary: #4A4A6A;
  --color-text-muted:     #6C757D;

  /* ขอบ */
  --color-border:         #DEE2E6;
  --color-border-strong:  #ADB5BD;

  /* Brand / accent */
  --color-accent:         #3B82F6;
  --color-accent-hover:   #2563EB;

  /* ข้อมูลสถานะ */
  --color-success:  #22C55E;
  --color-warning:  #F59E0B;
  --color-danger:   #EF4444;
}

จากนั้นกำหนดค่าแทนสำหรับ dark mode ในบล็อกแยก สิ่งสำคัญคือคุณไม่ได้แค่กลับสี — คุณกำลังเลือกจานสีอีกชุดที่ออกแบบมาสำหรับพื้นผิวสีเข้มโดยเฉพาะ:

[data-theme="dark"] {
  /* พื้นหลัง */
  --color-bg-base:      #0F0F17;
  --color-bg-elevated:  #1A1A2E;
  --color-bg-overlay:   #252540;

  /* ข้อความ */
  --color-text-primary:   #E8E8F0;
  --color-text-secondary: #A8A8C0;
  --color-text-muted:     #6A6A88;

  /* ขอบ */
  --color-border:         #2E2E4A;
  --color-border-strong:  #4A4A6A;

  /* Brand / accent — มักสว่างกว่าเล็กน้อยเพื่อให้อ่านง่ายบนพื้นหลังสีเข้ม */
  --color-accent:         #60A5FA;
  --color-accent-hover:   #93C5FD;

  /* ข้อมูลสถานะ — ลดความอิ่มตัวเล็กน้อยเพื่อลดความรุนแรง */
  --color-success:  #4ADE80;
  --color-warning:  #FCD34D;
  --color-danger:   #F87171;
}

สังเกตว่า accent #3B82F6 ใน light mode กลายเป็น #60A5FA ใน dark mode เฉดสีเหมือนกันแต่ความสว่างเพิ่มขึ้น — จำเป็นเพราะบริบทความคมชัดได้เปลี่ยนไป Shade Generator ช่วยให้คุณสำรวจช่วง 50–950 ทั้งหมดของสีใดก็ได้

การใช้ตัวแปรใน Component

เมื่อกำหนดจานสีแล้ว ทุก component อ้างอิงตัวแปรแทนค่าโดยตรง:

.card {
  background-color: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  color: var(--color-text-primary);
}

.btn-primary {
  background-color: var(--color-accent);
  color: #FFFFFF;
}

.btn-primary:hover {
  background-color: var(--color-accent-hover);
}

เมื่อมี attribute [data-theme="dark"] บน element <html> ตัวแปรทั้งหมดอัปเดตพร้อมกัน และทุก component ที่อ้างอิงจะเปลี่ยนรูปลักษณ์ — ไม่ต้องเขียน CSS เพิ่ม

Media Query prefers-color-scheme

ก่อนที่ผู้ใช้จะโต้ตอบกับ toggle คุณสามารถรองรับการตั้งค่าระบบปฏิบัติการของพวกเขาด้วย media query prefers-color-scheme media query นี้จะทำงานเมื่อ OS ตั้งค่าเป็นธีมสีเข้ม

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg-base:      #0F0F17;
    --color-bg-elevated:  #1A1A2E;
    --color-bg-overlay:   #252540;
    --color-text-primary:   #E8E8F0;
    --color-text-secondary: #A8A8C0;
    --color-text-muted:     #6A6A88;
    --color-border:         #2E2E4A;
    --color-border-strong:  #4A4A6A;
    --color-accent:         #60A5FA;
    --color-accent-hover:   #93C5FD;
    --color-success:  #4ADE80;
    --color-warning:  #FCD34D;
    --color-danger:   #F87171;
  }
}

วิธีนี้ทำงานโดยไม่ต้องใช้ JavaScript ไม่มี layout shift และรองรับการตั้งค่าของผู้ใช้ทันทีเมื่อโหลดหน้า นี่คือ baseline ที่ถูกต้อง ข้อจำกัดคือผู้ใช้ไม่สามารถแทนที่ได้ในแอปของคุณ นั่นคือเหตุผลที่การใช้งานจริงส่วนใหญ่จะซ้อน JavaScript toggle ไว้บน media query

การรวมทั้งสองวิธีเข้าด้วยกัน

รูปแบบที่แนะนำใช้ media query เป็นค่าเริ่มต้นและ attribute data-theme เป็นตัวแทนที่ชัดเจน:

/* 1. Light mode เริ่มต้น */
:root {
  --color-bg-base: #FFFFFF;
  /* ... */
}

/* 2. OS dark mode override (เมื่อไม่ได้ตั้งค่าชัดเจน) */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg-base: #0F0F17;
    /* ... */
  }
}

/* 3. Dark mode ชัดเจน (ผู้ใช้ toggle ผ่าน JS) */
[data-theme="dark"] {
  --color-bg-base: #0F0F17;
  /* ... */
}

กลไก Toggle ด้วย JavaScript

Toggle ที่ใช้งานได้ดีทำสามสิ่ง: เปลี่ยนรูปลักษณ์ปัจจุบันทันที บันทึกการตั้งค่าใน localStorage และอ่านการตั้งค่าที่บันทึกไว้เมื่อโหลดหน้าก่อน paint แรก

การอ่านการตั้งค่าเมื่อโหลด

สคริปต์นี้ต้องทำงานใน <head> — ก่อนที่หน้าจะแสดงผล — เพื่อป้องกัน flash ของธีมที่ผิด:

<head>
  <script>
    (function() {
      const stored = localStorage.getItem('theme');
      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      const theme = stored ?? (prefersDark ? 'dark' : 'light');
      document.documentElement.setAttribute('data-theme', theme);
    })();
  </script>
</head>

ฟังก์ชัน Toggle

function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
}

document.getElementById('theme-toggle').addEventListener('click', toggleTheme);

กลยุทธ์การปรับสี

ลดความคมชัด ไม่ใช่แค่กลับสี

ข้อความสีขาวล้วนบนพื้นหลังสีดำล้วน (#FFFFFF บน #000000) มีความคมชัดสูงสุดทางเทคนิค — 21:1 — แต่ทำให้เมื่อยล้าทางสายตาเมื่ออ่านนาน ใช้สีขาวนวลเช่น #E8E8F0 สำหรับข้อความและสีน้ำเงินเข้มมากเช่น #0F0F17 สำหรับพื้นหลัง

ใช้ Contrast Checker เพื่อตรวจสอบว่าทุกคู่ข้อความ/พื้นหลังในธีมสีเข้มของคุณตรงตาม WCAG AA อย่างน้อย

ระดับชั้นของพื้นผิวสีเข้ม

/* สเกลระดับความสูงสำหรับ dark mode */
--color-bg-base:     #0F0F17;  /* พื้นหลังหน้า */
--color-bg-elevated: #1A1A2E;  /* การ์ด, ไซด์บาร์ */
--color-bg-overlay:  #252540;  /* Modal, dropdown */
--color-bg-tooltip:  #2E2E4A;  /* Tooltip */

ลดความอิ่มตัวของสีใน Dark Mode เล็กน้อย

สีที่มีความอิ่มตัวสูงดูแสบตาแบบนีออนบนพื้นหลังสีเข้ม เมื่อปรับสี brand สำหรับ dark mode ให้ลดความอิ่มตัว 10–20% พร้อมกับเพิ่มความสว่าง Shade Generator เหมาะสำหรับการนี้

การทดสอบทั้งสองโหมด

การจำลองใน DevTools ของ Browser

Chrome และ Firefox มีการจำลอง dark mode ใน DevTools โดยไม่ต้องเปลี่ยนการตั้งค่า OS

รายการตรวจสอบปัญหาที่พบบ่อย

  • ค่า hex ที่เขียนตายตัวใน CSS ของ component แทนที่จะใช้ตัวแปร
  • ไอคอน SVG ที่มี fill="#000000" เขียนตายตัว — เปลี่ยนเป็น fill="currentColor"
  • Component ของบุคคลที่สามที่ไม่รองรับ data-theme
  • ไม่ได้ตั้งค่า property color-scheme
<meta name="color-scheme" content="light dark">
:root {
  color-scheme: light dark;
}

การเพิ่มเล็กน้อยนี้ทำให้ scrollbar พื้นฐาน date picker และ form control ที่ OS แสดงผลสลับเป็นเวอร์ชันสีเข้มโดยอัตโนมัติ

สรุปสำคัญ

  • กำหนดสีทั้งหมดเป็น CSS custom properties บน :root และแทนที่สำหรับ dark mode โดยใช้ [data-theme="dark"] สไตล์ของ component อ้างอิงเฉพาะตัวแปร
  • ใช้ prefers-color-scheme: dark เป็นค่าเริ่มต้นอัตโนมัติ ซ้อน JavaScript toggle ด้วยการบันทึก localStorage ไว้ด้านบน
  • รันสคริปต์ป้องกัน flash ใน <head> ก่อนที่ CSS จะโหลด
  • สีใน dark mode ไม่ใช่สีสว่างที่กลับด้าน — ลดความคมชัดที่รุนแรง ใช้พื้นหลังที่สว่างกว่าเพื่อแสดงระดับชั้น
  • ตรวจสอบทุกคู่ข้อความ/พื้นหลังด้วย Contrast Checker และใช้ Shade Generator เพื่อค้นหาเฉดสีที่เหมาะสม
  • เพิ่ม color-scheme: light dark และ tag <meta> เพื่อให้ element UI ของ browser พื้นฐานสลับโดยอัตโนมัติด้วย

สีที่เกี่ยวข้อง

แบรนด์ที่เกี่ยวข้อง

เครื่องมือที่เกี่ยวข้อง