0Pricing
Tailwind CSS Academy · บทเรียน

การสร้างตัวสลับโหมดมืด

สร้างตัวสลับด้วย JavaScript ที่เพิ่มและลบคลาส dark บนองค์ประกอบ html และบันทึกค่าที่เลือกไว้ใน localStorage

การสร้างตัวสลับโหมดมืด เป็นบทเรียน Tailwind CSS Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Tailwind CSS Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Tailwind CSS Academy มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

What the Toggle Needs to Do

A dark mode toggle needs to accomplish three things: read the user's saved preference (or OS preference as a fallback), apply the correct class to the <html> element on page load, and update the class and save the preference when the user clicks the toggle button. Getting the initial load right is critical — applying the class before the page renders prevents a flash of incorrect theme.

Preventing Flash of Wrong Theme

The most common dark mode bug is a flash of light theme when a user in dark mode refreshes the page. This happens because JavaScript runs after the HTML has already been parsed and rendered. The fix is to run a tiny inline <script> in the <head> — before any CSS or body content — that reads localStorage and immediately adds the dark class if needed.

<!-- Place this FIRST inside <head> -->
<script>
  if (localStorage.theme === 'dark' ||
      (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark');
  }
</script>

HTML Structure for the Toggle

The toggle button itself can be as simple as a <button> with sun and moon icons. Show the sun icon in dark mode (to switch to light) and the moon icon in light mode (to switch to dark). Use Tailwind's hidden and block with dark: variants to swap which icon is visible based on the current mode.

<button id="theme-toggle" class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700">
  <!-- Moon icon (visible in light mode) -->
  <svg id="icon-moon" class="w-5 h-5 text-gray-800 dark:hidden" fill="currentColor" viewBox="0 0 20 20">
    <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
  </svg>
  <!-- Sun icon (visible in dark mode) -->
  <svg id="icon-sun" class="w-5 h-5 text-yellow-400 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
    <path d="M10 2a1 1 0 100 2 1 1 0 000-2zm0 14a1 1 0 100 2 1 1 0 000-2z" />
  </svg>
</button>

Writing the Toggle JavaScript

The toggle function checks whether the dark class is currently present on <html>. If it is, the function removes it and saves 'light' to localStorage. If it is not, the function adds it and saves 'dark'. This pattern is clean, reversible, and works without any external libraries or frameworks.

const toggle = document.getElementById('theme-toggle');

toggle.addEventListener('click', () => {
  const html = document.documentElement;

  if (html.classList.contains('dark')) {
    html.classList.remove('dark');
    localStorage.setItem('theme', 'light');
  } else {
    html.classList.add('dark');
    localStorage.setItem('theme', 'dark');
  }
});

Respecting OS Preference as Default

If the user has never interacted with your toggle, there is nothing in localStorage. In that case, you should fall back to the OS prefers-color-scheme media query. The window.matchMedia API lets you check this at runtime. If the OS is in dark mode and the user has not overridden it, apply the dark class so the initial experience matches their system setting.

function getInitialTheme() {
  const saved = localStorage.getItem('theme');
  if (saved) return saved;

  // No saved preference — use OS setting
  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}

if (getInitialTheme() === 'dark') {
  document.documentElement.classList.add('dark');
}

Full Toggle Implementation

Putting it all together: the inline head script handles initial load without flash, the button HTML uses dark: variants to swap icons, and the click handler toggles the class and saves to localStorage. This complete pattern is the standard approach used by most Tailwind-based apps. It requires no dependencies and works in any environment.

<!-- In <head> (anti-flash) -->
<script>
  const saved = localStorage.getItem('theme');
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  if (saved === 'dark' || (!saved && prefersDark)) {
    document.documentElement.classList.add('dark');
  }
</script>

<!-- In body JS -->
<script>
  document.getElementById('theme-toggle').addEventListener('click', () => {
    const isDark = document.documentElement.classList.toggle('dark');
    localStorage.setItem('theme', isDark ? 'dark' : 'light');
  });
</script>

Toggle Button With Smooth Transition

Add a satisfying animation to the theme switch by putting a transition-colors class on your body or root container. This makes all color changes animate smoothly instead of snapping instantly. Keep the transition duration short — around 200-300ms — to feel responsive. Apply it at the highest container level so all child elements inherit the smooth transition behavior.

<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-200">
  <!-- All children now transition smoothly between light and dark -->
</body>

Toggle in a React Component

In React, manage the dark mode state with useState and synchronize it with the DOM and localStorage using useEffect. A common pattern is a custom useDarkMode hook that encapsulates all the logic. Trigger the DOM class change inside the effect so it stays in sync with the React state.

import { useState, useEffect } from 'react';

function useDarkMode() {
  const [dark, setDark] = useState(() => localStorage.getItem('theme') === 'dark');

  useEffect(() => {
    document.documentElement.classList.toggle('dark', dark);
    localStorage.setItem('theme', dark ? 'dark' : 'light');
  }, [dark]);

  return [dark, setDark];
}

Toggle in a Next.js App

In Next.js, the anti-flash script cannot be easily placed in <head> via a component because React hydration runs after the HTML is parsed. The recommended approach is to add the inline script via next/script with strategy='beforeInteractive', or to place it directly in a custom _document.js file inside the Head component. This ensures the script runs synchronously before the page renders.

// pages/_document.js (Next.js Pages Router)
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html>
      <Head>
        <script dangerouslySetInnerHTML={{ __html: `
          const t = localStorage.getItem('theme');
          const d = window.matchMedia('(prefers-color-scheme: dark)').matches;
          if (t === 'dark' || (!t && d)) document.documentElement.classList.add('dark');
        ` }} />
      </Head>
      <body><Main /><NextScript /></body>
    </Html>
  );
}

Listening to OS Changes at Runtime

Users sometimes change their OS theme while your app is open. You can listen for these changes using window.matchMedia's change event. Only apply the OS preference change if the user has not set an explicit preference in your app — otherwise you would override their in-app choice. This provides the most respectful user experience.

const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

mediaQuery.addEventListener('change', (e) => {
  // Only auto-update if the user hasn't set an explicit preference
  if (!localStorage.getItem('theme')) {
    document.documentElement.classList.toggle('dark', e.matches);
  }
});

Toggle Accessibility Considerations

The dark mode toggle button should be keyboard accessible and have a meaningful aria-label that updates to reflect the current action. Use aria-pressed to communicate the current state to screen readers. Since only a visual icon changes, screen reader users need the label to understand what pressing the button will do.

<button
  id="theme-toggle"
  aria-label="Switch to dark mode"
  aria-pressed="false"
  class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700"
>
  <!-- icons -->
</button>

<script>
  const btn = document.getElementById('theme-toggle');
  btn.addEventListener('click', () => {
    const isDark = document.documentElement.classList.toggle('dark');
    btn.setAttribute('aria-pressed', isDark);
    btn.setAttribute('aria-label', isDark ? 'Switch to light mode' : 'Switch to dark mode');
    localStorage.setItem('theme', isDark ? 'dark' : 'light');
  });
</script>

Quick Check

Test your understanding of building a dark mode toggle with Tailwind CSS.

Lesson Recap

In this lesson you learned: an inline head script prevents the flash of wrong theme on page load, classList.toggle('dark') combined with localStorage creates a persistent user toggle, and matchMedia lets you respect the OS preference as a default when no user preference is saved. Next up we apply dark mode to complex components like cards, navbars, and modals.

คำถามที่พบบ่อย

บทเรียน “การสร้างตัวสลับโหมดมืด” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การสร้างตัวสลับโหมดมืด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Tailwind CSS Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Tailwind CSS Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การสร้างตัวสลับโหมดมืด”

สร้างตัวสลับด้วย JavaScript ที่เพิ่มและลบคลาส dark บนองค์ประกอบ html และบันทึกค่าที่เลือกไว้ใน localStorage คุณปฏิบัติ Tailwind CSS Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Tailwind CSS Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Tailwind CSS Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การสร้างตัวสลับโหมดมืด” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Tailwind CSS Academy นี้ได้ไหม

ได้ บทเรียน Tailwind CSS Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. กลยุทธ์โหมดมืด
  2. การใช้รูปแบบ Dark
  3. การสร้างตัวสลับโหมดมืด
  4. โหมดมืดสำหรับองค์ประกอบที่ซับซ้อน
← กลับไปที่ Tailwind CSS Academy