إطار Tailwind CSS

الوضع الداكن

20 دقيقة الدرس 24 من 35

الوضع الداكن

أصبح الوضع الداكن ميزة أساسية في تطبيقات الويب الحديثة. يتوقع المستخدمون القدرة على التبديل بين السمات الفاتحة والداكنة، ويجعل Tailwind CSS تنفيذ الوضع الداكن بسيطًا بشكل ملحوظ باستخدام متغير dark: المدمج.

في هذا الدرس، سنستكشف كيفية تنفيذ الوضع الداكن، وتكوين استراتيجيات مختلفة، والتصميم بفعالية لكلا السمتين، وإنشاء تجارب سلسة للوضع الداكن.

متغير dark:

يوفر Tailwind متغير dark: الذي يطبق الأنماط فقط عندما يكون الوضع الداكن نشطًا:

استخدام الوضع الداكن الأساسي

<!-- الوضع الفاتح: خلفية بيضاء، نص داكن -->
<!-- الوضع الداكن: خلفية داكنة، نص فاتح -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
  هذا النص يتكيف مع الوضع الفاتح والداكن
</div>

<!-- بطاقات مع دعم الوضع الداكن -->
<div class="bg-white dark:bg-gray-800 shadow-lg dark:shadow-gray-900/50
            rounded-lg p-6 border border-gray-200 dark:border-gray-700">
  <h3 class="text-gray-900 dark:text-white font-bold text-xl mb-2">
    عنوان البطاقة
  </h3>
  <p class="text-gray-600 dark:text-gray-300">
    محتوى البطاقة الذي يبدو جيدًا في كلا السمتين
  </p>
</div>

<!-- أزرار مع متغيرات الوضع الداكن -->
<button class="px-6 py-3 bg-blue-500 dark:bg-blue-600
               hover:bg-blue-600 dark:hover:bg-blue-700
               text-white rounded-lg shadow-md dark:shadow-lg">
  اضغط هنا
</button>

استراتيجيات الوضع الداكن

يدعم Tailwind استراتيجيتين لتمكين الوضع الداكن: media و class. تقوم بتكوين هذا في tailwind.config.js الخاص بك:

1. استراتيجية Media (تفضيل النظام)

تستخدم هذه الاستراتيجية تفضيل نظام التشغيل للمستخدم:

تكوين استراتيجية Media

// tailwind.config.js
module.exports = {
  darkMode: 'media', // يستخدم استعلام وسائط prefers-color-scheme

  theme: {
    // ... تكوين السمة الخاص بك
  }
}

مع استراتيجية media، يتم تمكين الوضع الداكن تلقائيًا عندما يكون لدى المستخدم الوضع الداكن مفعلاً في إعدادات نظام التشغيل الخاص به. لا تحتاج إلى أي JavaScript—إنه يعتمد على CSS بالكامل.

متى تستخدم استراتيجية media:
  • المواقع البسيطة بدون حسابات مستخدمين
  • عندما تريد التكامل التلقائي مع النظام
  • المواقع الثابتة أو الوثائق
  • عندما لا تحتاج إلى وظيفة التبديل اليدوي

2. استراتيجية Class (التبديل اليدوي)

تبحث هذه الاستراتيجية عن فئة dark على عنصر <html> أو <body>:

تكوين استراتيجية Class

// tailwind.config.js
module.exports = {
  darkMode: 'class', // يبحث عن فئة 'dark' في DOM

  theme: {
    // ... تكوين السمة الخاص بك
  }
}

مع استراتيجية class، تتحكم في الوضع الداكن عبر JavaScript بإضافة/إزالة فئة dark:

بنية HTML لاستراتيجية Class

<!-- الوضع الفاتح -->
<html>
  <!-- المحتوى -->
</html>

<!-- الوضع الداكن مفعّل -->
<html class="dark">
  <!-- المحتوى -->
</html>
يوصى باستراتيجية class لمعظم التطبيقات لأنها تمنحك التحكم الكامل في تبديل الوضع الداكن، وتسمح لك باحترام تفضيلات المستخدم مع تقديم التجاوز اليدوي، وتمكنك من الاحتفاظ باختيار المستخدم.

تنفيذ تبديل الوضع الداكن باستخدام JavaScript

دعنا نبني تبديل وضع داكن كامل مع الاستمرارية:

تنفيذ تبديل الوضع الداكن الكامل

<!-- زر تبديل الوضع الداكن -->
<button id="theme-toggle" class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700
                                   hover:bg-gray-300 dark:hover:bg-gray-600
                                   transition-colors duration-200">
  <!-- أيقونة الشمس (تظهر في الوضع الداكن) -->
  <svg class="w-6 h-6 text-yellow-500 hidden dark:block"
       fill="currentColor" viewBox="0 0 20 20">
    <path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd"></path>
  </svg>

  <!-- أيقونة القمر (تظهر في الوضع الفاتح) -->
  <svg class="w-6 h-6 text-gray-700 block 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"></path>
  </svg>
</button>

<script>
// منطق تبديل الوضع الداكن
const themeToggle = document.getElementById('theme-toggle');
const htmlElement = document.documentElement;

// التحقق من تفضيل السمة المحفوظة أو الافتراضي لتفضيل النظام
const getTheme = () => {
  // التحقق من localStorage أولاً
  if (localStorage.theme === 'dark') {
    return 'dark';
  }
  if (localStorage.theme === 'light') {
    return 'light';
  }
  // العودة إلى تفضيل النظام
  if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    return 'dark';
  }
  return 'light';
};

// تطبيق السمة
const applyTheme = (theme) => {
  if (theme === 'dark') {
    htmlElement.classList.add('dark');
  } else {
    htmlElement.classList.remove('dark');
  }
};

// تهيئة السمة عند تحميل الصفحة
applyTheme(getTheme());

// تبديل السمة عند النقر على الزر
themeToggle.addEventListener('click', () => {
  const currentTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

  applyTheme(newTheme);
  localStorage.theme = newTheme;
});

// الاستماع لتغييرات سمة النظام
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
  // التبديل التلقائي فقط إذا لم يقم المستخدم بتعيين تفضيل
  if (!localStorage.theme) {
    applyTheme(e.matches ? 'dark' : 'light');
  }
});
</script>

التصميم للوضع الداكن

الوضع الداكن الفعال ليس مجرد عكس الألوان—يتطلب قرارات تصميم مدروسة:

1. اعتبارات الألوان

أفضل ممارسات ألوان الوضع الداكن

<!-- لا تستخدم الأسود النقي (#000000) -->
<div class="dark:bg-black">قاسٍ جدًا!</div>

<!-- استخدم الرماديات الداكنة للخلفيات -->
<div class="dark:bg-gray-900">أفضل للعيون</div>

<!-- لا تستخدم نص أبيض نقي على الداكن -->
<p class="dark:text-white">تباين كثير!</p>

<!-- استخدم ألوان نص مخففة قليلاً -->
<p class="dark:text-gray-100">أكثر راحة</p>

<!-- ضبط الألوان للوضع الداكن -->
<div class="bg-blue-500 dark:bg-blue-600">
  <!-- الأزرق الأكثر سطوعًا قليلاً يعمل بشكل أفضل في الوضع الداكن -->
</div>

<!-- تقليل تشبع اللون في الوضع الداكن -->
<button class="bg-green-500 dark:bg-green-600
               text-white hover:bg-green-600 dark:hover:bg-green-700">
  زر النجاح
</button>

2. الارتفاع والظلال

الظلال في الوضع الداكن

<!-- ظلال الوضع الفاتح -->
<div class="shadow-lg dark:shadow-gray-900/50">
  <!-- ظلال أغمق للوضع الداكن -->
</div>

<!-- استخدم الحدود للفصل بدلاً من الظلال -->
<div class="shadow-md dark:shadow-none
            dark:border dark:border-gray-700">
  بطاقة مرتفعة
</div>

<!-- خلفيات أفتح للارتفاع في الوضع الداكن -->
<div class="bg-white dark:bg-gray-800">
  <!-- عنصر مرتفع متداخل -->
  <div class="bg-gray-50 dark:bg-gray-700 p-4">
    ارتفاع أعلى = أفتح في الوضع الداكن
  </div>
</div>

3. ألوان الحدود والفواصل

الحدود في الوضع الداكن

<!-- حدود دقيقة تعمل في كلا الوضعين -->
<div class="border border-gray-200 dark:border-gray-700">
  المحتوى
</div>

<!-- الفواصل -->
<hr class="border-gray-200 dark:border-gray-700" />

<!-- حدود الجدول -->
<table class="border-collapse">
  <tr class="border-b border-gray-200 dark:border-gray-700">
    <td class="border-r border-gray-200 dark:border-gray-700">خلية</td>
  </tr>
</table>

الصور في الوضع الداكن

تحتاج الصور إلى اهتمام خاص في الوضع الداكن:

التعامل مع الصور في الوضع الداكن

<!-- تقليل سطوع الصور في الوضع الداكن -->
<img src="photo.jpg" class="dark:opacity-80" alt="صورة" />

<!-- صور مختلفة لأوضاع مختلفة -->
<img src="logo-light.png" class="block dark:hidden" alt="الشعار" />
<img src="logo-dark.png" class="hidden dark:block" alt="الشعار" />

<!-- إضافة خلفية دقيقة للصور الشفافة -->
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg">
  <img src="transparent-icon.png" alt="أيقونة" />
</div>

<!-- عكس الأيقونات التي يجب أن تكون فاتحة في الوضع الداكن -->
<img src="icon.svg" class="dark:invert" alt="أيقونة" />

<!-- حلقة لتسليط الضوء على الصور في الوضع الداكن -->
<img src="avatar.jpg"
     class="rounded-full ring-2 ring-gray-200 dark:ring-gray-700"
     alt="الصورة الرمزية" />

اكتشاف تفضيل النظام

يمكنك اكتشاف تفضيل نظام الألوان للمستخدم والاستجابة له:

اكتشاف تفضيل النظام

<script>
// التحقق مما إذا كان المستخدم يفضل الوضع الداكن
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');

console.log('المستخدم يفضل الوضع الداكن:', prefersDark.matches);

// الاستماع للتغييرات في تفضيل النظام
prefersDark.addEventListener('change', (e) => {
  console.log('تغيرت سمة النظام إلى:', e.matches ? 'داكن' : 'فاتح');

  // التطبيق فقط إذا لم يقم المستخدم بتعيين تفضيل يدوي
  if (!localStorage.getItem('theme')) {
    applySystemTheme(e.matches);
  }
});

function applySystemTheme(isDark) {
  if (isDark) {
    document.documentElement.classList.add('dark');
  } else {
    document.documentElement.classList.remove('dark');
  }
}
</script>

مثال كامل للوضع الداكن

إليك تنفيذ شامل للوضع الداكن لتطبيق ويب:

تطبيق الوضع الداكن الكامل

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>تطبيق الوضع الداكن</title>
  <script src="https://cdn.tailwindcss.com"></script>

  <!-- تهيئة السمة قبل عرض الصفحة (يمنع الوميض) -->
  <script>
    if (localStorage.theme === 'dark' ||
        (!localStorage.theme &&
         window.matchMedia('(prefers-color-scheme: dark)').matches)) {
      document.documentElement.classList.add('dark');
    }
  </script>
</head>
<body class="bg-gray-50 dark:bg-gray-900 transition-colors duration-200">

  <!-- الرأس -->
  <header class="bg-white dark:bg-gray-800 border-b border-gray-200
                 dark:border-gray-700 shadow-sm">
    <div class="max-w-7xl mx-auto px-4 py-4 flex justify-between items-center">
      <h1 class="text-2xl font-bold text-gray-900 dark:text-white">
        تطبيقي
      </h1>

      <!-- زر تبديل السمة -->
      <button id="theme-toggle"
              class="p-2 rounded-lg bg-gray-100 dark:bg-gray-700
                     hover:bg-gray-200 dark:hover:bg-gray-600
                     transition-colors duration-200">
        <svg class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
          <path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0z" clip-rule="evenodd"></path>
        </svg>
        <svg class="w-5 h-5 block 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"></path>
        </svg>
      </button>
    </div>
  </header>

  <!-- المحتوى الرئيسي -->
  <main class="max-w-7xl mx-auto px-4 py-8">
    <!-- شبكة البطاقات -->
    <div class="grid grid-cols-1 md:grid-cols-3 gap-6">

      <!-- البطاقة 1 -->
      <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md
                  dark:shadow-gray-900/50 p-6 border border-gray-200
                  dark:border-gray-700 hover:shadow-lg transition-all">
        <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-3">
          الميزة الأولى
        </h2>
        <p class="text-gray-600 dark:text-gray-300 mb-4">
          هذا وصف يبدو جيدًا في كل من الوضع الفاتح والداكن.
        </p>
        <button class="px-4 py-2 bg-blue-500 dark:bg-blue-600
                       hover:bg-blue-600 dark:hover:bg-blue-700
                       text-white rounded-md transition-colors">
          اعرف المزيد
        </button>
      </div>

      <!-- البطاقة 2 -->
      <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md
                  dark:shadow-gray-900/50 p-6 border border-gray-200
                  dark:border-gray-700">
        <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-3">
          الميزة الثانية
        </h2>
        <p class="text-gray-600 dark:text-gray-300 mb-4">
          ميزة أخرى مع تصميم الوضع الداكن المناسب.
        </p>
        <button class="px-4 py-2 bg-green-500 dark:bg-green-600
                       hover:bg-green-600 dark:hover:bg-green-700
                       text-white rounded-md transition-colors">
          ابدأ
        </button>
      </div>

      <!-- البطاقة 3 -->
      <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md
                  dark:shadow-gray-900/50 p-6 border border-gray-200
                  dark:border-gray-700">
        <h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-3">
          الميزة الثالثة
        </h2>
        <p class="text-gray-600 dark:text-gray-300 mb-4">
          تنفيذ الوضع الداكن الذي يحترم تفضيلات المستخدم.
        </p>
        <button class="px-4 py-2 bg-purple-500 dark:bg-purple-600
                       hover:bg-purple-600 dark:hover:bg-purple-700
                       text-white rounded-md transition-colors">
          استكشف
        </button>
      </div>

    </div>
  </main>

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

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

      if (isDark) {
        html.classList.remove('dark');
        localStorage.theme = 'light';
      } else {
        html.classList.add('dark');
        localStorage.theme = 'dark';
      }
    });
  </script>

</body>
</html>

منع وميض المحتوى غير المصمم

لمنع الصفحة من إظهار السمة الخاطئة لفترة وجيزة عند التحميل، أضف هذا السكريبت في <head>:

منع وميض السمة (FOUC)

<head>
  <!-- ضع هذا قبل تحميل Tailwind CSS -->
  <script>
    // التحقق من localStorage وتفضيل النظام
    if (localStorage.theme === 'dark' ||
        (!'theme' in localStorage &&
         window.matchMedia('(prefers-color-scheme: dark)').matches)) {
      document.documentElement.classList.add('dark');
    } else {
      document.documentElement.classList.remove('dark');
    }
  </script>

  <!-- ثم حمّل Tailwind CSS -->
  <link href="styles.css" rel="stylesheet">
</head>

تمرين تطبيقي

المهمة: ابنِ لوحة تحكم للوضع الداكن:

  1. أنشئ شريط تنقل مع شعار ومفتاح الوضع الداكن
  2. أضف شريط جانبي مع روابط التنقل (الرئيسية، الملف الشخصي، الإعدادات)
  3. ابنِ منطقة محتوى رئيسية مع 4 بطاقات إحصائيات تعرض المقاييس
  4. أضف جدولاً بألوان صفوف متناوبة تعمل في كلا الوضعين
  5. قم بتضمين عناصر النموذج (input، select، button) مع متغيرات الوضع الداكن
  6. نفذ وظيفة التبديل مع استمرارية localStorage
  7. تأكد من أن جميع النصوص لها تباين مناسب في كلا الوضعين

تمرين التحدي

المهمة المتقدمة: أنشئ مدونة كاملة بالوضع الداكن:

  1. رأس مع التنقل، شريط البحث، ومفتاح السمة مع انتقالات سلسة
  2. قسم البطل مع صورة خلفية تخفت في الوضع الداكن
  3. شبكة منشورات المدونة مع بطاقات (صورة، عنوان، مقتطف، وقت القراءة، الوسوم)
  4. أنظمة ألوان مختلفة لفئات الوسوم تعمل في كلا الوضعين
  5. تذييل مع أيقونات اجتماعية تنعكس في الوضع الداكن
  6. قسم التعليقات مع ردود متداخلة وارتفاع مناسب
  7. مفتاح ثلاثي الحالات: فاتح، داكن، النظام (يتبع تفضيل OS)
  8. أضف انتقالات سلسة بين تغييرات السمة
  9. اختبر مع نسب تباين WCAG لإمكانية الوصول
نصيحة احترافية: اختبر دائمًا وضعك الداكن في بيئة مظلمة فعلية. ما يبدو جيدًا على شاشة مضيئة قد يكون ساطعًا جدًا أو به تباين غير كافٍ في غرفة مظلمة. استخدم أدوات المطور في المتصفح لمحاكاة الوضع الداكن للاختبار.

الملخص

في هذا الدرس، تعلمت:

  • كيفية استخدام متغير dark: لتصميم الوضع الداكن
  • الفرق بين استراتيجيات media و class
  • تنفيذ تبديل الوضع الداكن الكامل مع JavaScript
  • أفضل ممارسات التصميم: اختيارات الألوان، الظلال، الحدود
  • التعامل مع الصور بفعالية في الوضع الداكن
  • اكتشاف تفضيلات النظام والاستجابة لها
  • منع وميض المحتوى غير المصمم (FOUC)
  • إنشاء تجارب الوضع الداكن التي يمكن الوصول إليها

الوضع الداكن أكثر من مجرد عكس الألوان—إنه يتعلق بإنشاء تجربة متماسكة ومريحة في كلا السمتين. في الدرس التالي، سنستكشف إضافات Tailwind وكيفية إنشاء أدوات مخصصة.