الوضع الداكن مع prefers-color-scheme
ما هي prefers-color-scheme؟
ميزة وسائط CSS prefers-color-scheme تكتشف ما إذا كان المستخدم قد طلب مظهرا فاتحا أو داكنا من خلال إعدادات نظام التشغيل. على macOS، هذا هو إعداد المظهر في تفضيلات النظام. على Windows، هو خيار "اختر لونك" في إعدادات التخصيص. على iOS وAndroid، هو مفتاح الوضع الداكن. عندما يحول المستخدم نظامه للوضع الداكن، يصبح prefers-color-scheme: dark صحيحا، ويمكن لـ CSS الاستجابة تلقائيا -- لا حاجة لـ JavaScript للكشف الأولي.
الوضع الداكن لم يعد ميزة فاخرة؛ إنه توقع. المستخدمون يفضلون الواجهات الداكنة لتقليل إجهاد العين في البيئات ذات الإضاءة المنخفضة، وانخفاض استهلاك البطارية على شاشات OLED، والتفضيل الجمالي الشخصي. باستخدام prefers-color-scheme، تحترم تفضيل المستخدم على مستوى النظام وتقدم المظهر الصحيح فورا عند تحميل الصفحة، متجنبا وميض نظام الألوان الخاطئ الذي يعاني منه العديد من تطبيقات الوضع الداكن.
اكتشاف تفضيل النظام بـ CSS
ميزة الوسائط prefers-color-scheme تعمل مثل أي استعلام وسائط CSS آخر. تقبل قيمتين: light وdark. تضع أنماط الوضع الداكن الخاصة بك داخل استعلام الوسائط، والمتصفح يطبقها تلقائيا عندما يكون النظام في الوضع الداكن.
الاستخدام الأساسي لـ prefers-color-scheme
/* أنماط الوضع الفاتح الافتراضية */
body {
background-color: #ffffff;
color: #1a1a2e;
}
a {
color: #2563eb;
}
/* تجاوزات الوضع الداكن */
@media (prefers-color-scheme: dark) {
body {
background-color: #1a1a2e;
color: #e2e8f0;
}
a {
color: #60a5fa;
}
}
/* يمكنك أيضا استهداف الوضع الفاتح صراحة */
@media (prefers-color-scheme: light) {
body {
background-color: #ffffff;
color: #1a1a2e;
}
}
/* التحقق من عدم وجود تفضيل (نادر، لكن ممكن) */
/* إذا لم يتطابق لا الفاتح ولا الداكن، تُطبق الأنماط الافتراضية */
prefers-color-scheme: dark، فأنت تتبع نهج "الفاتح أولا". بدلا من ذلك، يمكنك كتابة أنماط داكنة افتراضية وتجاوزها بـ prefers-color-scheme: light لنهج "الداكن أولا". نهج الفاتح أولا أكثر شيوعا لأنه يتوافق مع اتفاقيات تصميم الويب التقليدية ويوفر بديلا أفضل للمتصفحات التي لا تدعم ميزة الوسائط.خاصية color-scheme ووسم Meta
خاصية CSS color-scheme ووسم HTML meta المقابل يخبران المتصفح بأنظمة الألوان التي تدعمها صفحتك. هذا مهم لأن المتصفح يستخدم هذه المعلومات لتنسيق عناصر واجهة المستخدم المدمجة مثل عناصر التحكم في النماذج وأشرطة التمرير ولون الخلفية الافتراضي قبل حتى تحميل CSS.
خاصية color-scheme في CSS ووسم Meta في HTML
/* أخبر المتصفح أن هذه الصفحة تدعم الفاتح والداكن */
:root {
color-scheme: light dark;
}
/* سيقوم المتصفح تلقائيا بتعديل:
- الخلفية الافتراضية (أبيض في الفاتح، رمادي داكن في الداكن)
- لون النص الافتراضي (أسود في الفاتح، أبيض في الداكن)
- عناصر التحكم في النماذج (المدخلات، القوائم المنسدلة، مربعات الاختيار)
- ألوان شريط التمرير
- ألوان النظام مثل Canvas وCanvasText، إلخ. */
/* يمكنك أيضا تحديد نظام واحد فقط */
:root {
color-scheme: light;
/* يفرض المظهر الفاتح لجميع واجهة المتصفح */
}
:root {
color-scheme: dark;
/* يفرض المظهر الداكن لجميع واجهة المتصفح */
}
وسم HTML Meta لـ color-scheme
<!-- أضف هذا لـ <head> للتنسيق الفوري للمتصفح -->
<meta name="color-scheme" content="light dark">
<!-- هذا يضمن أن المتصفح يعرض خلفية داكنة
قبل تحميل CSS، مما يمنع وميض الأبيض
عندما يكون لدى المستخدم الوضع الداكن مفعلا.
بدون وسم meta هذا، يستخدم المتصفح افتراضيا خلفية
بيضاء أثناء تحميل الصفحة، مما يسبب وميضا
قصيرا للأبيض حتى لو كان CSS يحدد خلفية
داكنة. -->
<meta name="color-scheme"> في رأس HTML وخاصية color-scheme في CSS على :root. وسم meta يمنع وميض لون الخلفية الخاطئ أثناء تحميل الصفحة، بينما خاصية CSS تمنحك تحكما أدق ويمكن تغييرها ديناميكيا. معا، يوفران أسلس تجربة للوضع الداكن.بناء نظام رموز ألوان مع خصائص CSS المخصصة
النهج الأكثر قابلية للصيانة للوضع الداكن هو بناء نظام رموز ألوان كامل باستخدام خصائص CSS المخصصة (المتغيرات). بدلا من نثر قيم الألوان في جميع أوراق الأنماط، تحدد جميع ألوانك كمتغيرات على :root ثم تتجاوزها داخل استعلام وسائط prefers-color-scheme: dark. أنماط مكوناتك تشير فقط للمتغيرات، وليس قيم ألوان خام أبدا.
نظام رموز ألوان كامل
/* ===== رموز الوضع الفاتح (الافتراضي) ===== */
:root {
color-scheme: light dark;
/* ألوان الخلفية */
--color-bg-primary: #ffffff;
--color-bg-secondary: #f8fafc;
--color-bg-tertiary: #f1f5f9;
--color-bg-elevated: #ffffff;
--color-bg-overlay: rgba(0, 0, 0, 0.5);
/* ألوان السطح (البطاقات، اللوحات) */
--color-surface: #ffffff;
--color-surface-hover: #f8fafc;
--color-surface-active: #f1f5f9;
/* ألوان النص */
--color-text-primary: #0f172a;
--color-text-secondary: #475569;
--color-text-tertiary: #94a3b8;
--color-text-inverse: #ffffff;
--color-text-link: #2563eb;
--color-text-link-hover: #1d4ed8;
/* ألوان الحدود */
--color-border-primary: #e2e8f0;
--color-border-secondary: #cbd5e1;
--color-border-focus: #2563eb;
/* ألوان العلامة التجارية / اللهجة */
--color-accent: #2563eb;
--color-accent-hover: #1d4ed8;
--color-accent-light: #dbeafe;
/* ألوان دلالية */
--color-success: #16a34a;
--color-success-bg: #f0fdf4;
--color-warning: #d97706;
--color-warning-bg: #fffbeb;
--color-error: #dc2626;
--color-error-bg: #fef2f2;
/* الظلال */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -2px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -4px rgba(0, 0, 0, 0.1);
}
/* ===== تجاوزات رموز الوضع الداكن ===== */
@media (prefers-color-scheme: dark) {
:root {
/* ألوان الخلفية */
--color-bg-primary: #0f172a;
--color-bg-secondary: #1e293b;
--color-bg-tertiary: #334155;
--color-bg-elevated: #1e293b;
--color-bg-overlay: rgba(0, 0, 0, 0.7);
/* ألوان السطح */
--color-surface: #1e293b;
--color-surface-hover: #334155;
--color-surface-active: #475569;
/* ألوان النص */
--color-text-primary: #f1f5f9;
--color-text-secondary: #cbd5e1;
--color-text-tertiary: #64748b;
--color-text-inverse: #0f172a;
--color-text-link: #60a5fa;
--color-text-link-hover: #93bbfd;
/* ألوان الحدود */
--color-border-primary: #334155;
--color-border-secondary: #475569;
--color-border-focus: #60a5fa;
/* ألوان العلامة التجارية / اللهجة */
--color-accent: #3b82f6;
--color-accent-hover: #60a5fa;
--color-accent-light: #1e3a5f;
/* ألوان دلالية */
--color-success: #4ade80;
--color-success-bg: #052e16;
--color-warning: #fbbf24;
--color-warning-bg: #422006;
--color-error: #f87171;
--color-error-bg: #450a0a;
/* الظلال (أقوى في الوضع الداكن للرؤية) */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4),
0 2px 4px -2px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5),
0 4px 6px -4px rgba(0, 0, 0, 0.4);
}
}
استخدام رموز الألوان في المكونات
بمجرد وضع نظام الرموز، كل مكون في تطبيقك يشير للمتغيرات بدلا من قيم ألوان ثابتة. هذا يعني أن التبديل بين الوضع الفاتح والداكن يحدث بالكامل من خلال تجاوزات المتغيرات -- CSS مكوناتك لا يحتاج للتغيير أبدا.
مكونات تستخدم رموز الألوان
/* جميع المكونات تشير للرموز -- أبدا ألوان خام */
/* تخطيط الصفحة */
body {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
transition: background-color 0.3s ease, color 0.3s ease;
}
/* مكون البطاقة */
.card {
background: var(--color-surface);
border: 1px solid var(--color-border-primary);
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: var(--shadow-md);
}
.card:hover {
background: var(--color-surface-hover);
border-color: var(--color-border-secondary);
}
.card__title {
color: var(--color-text-primary);
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.card__description {
color: var(--color-text-secondary);
line-height: 1.6;
}
/* مكون الزر */
.btn-primary {
background: var(--color-accent);
color: #ffffff;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
cursor: pointer;
transition: background-color 0.2s ease;
}
.btn-primary:hover {
background: var(--color-accent-hover);
}
/* التنقل */
.nav {
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border-primary);
box-shadow: var(--shadow-sm);
}
.nav-link {
color: var(--color-text-secondary);
text-decoration: none;
padding: 0.75rem 1rem;
}
.nav-link:hover {
color: var(--color-text-primary);
}
/* مكون التنبيه / الإشعار */
.alert--success {
background: var(--color-success-bg);
color: var(--color-success);
border: 1px solid var(--color-success);
}
.alert--error {
background: var(--color-error-bg);
color: var(--color-error);
border: 1px solid var(--color-error);
}
/* حقول الإدخال */
.input {
background: var(--color-bg-primary);
color: var(--color-text-primary);
border: 1px solid var(--color-border-primary);
padding: 0.75rem 1rem;
border-radius: 0.375rem;
}
.input:focus {
border-color: var(--color-border-focus);
outline: none;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.2);
}
.input::placeholder {
color: var(--color-text-tertiary);
}
التعامل مع الصور في الوضع الداكن
تتطلب الصور اهتماما خاصا في الوضع الداكن. الصور الساطعة على خلفية داكنة قد تبدو مبهرة، والصور ذات الخلفيات الشفافة (مثل الشعارات) قد تصبح غير مرئية. هناك عدة استراتيجيات للتعامل مع الصور عبر أنظمة الألوان.
استراتيجيات التعامل مع الصور للوضع الداكن
/* الاستراتيجية 1: تقليل سطوع الصورة وزيادة التباين */
@media (prefers-color-scheme: dark) {
img:not([src*=".svg"]) {
filter: brightness(0.9) contrast(1.05);
}
}
/* الاستراتيجية 2: استخدام عنصر <picture> لصور مختلفة */
/* HTML:
<picture>
<source srcset="logo-dark.png" media="(prefers-color-scheme: dark)">
<img src="logo-light.png" alt="شعار الشركة">
</picture>
*/
/* الاستراتيجية 3: تبديل الصور باستخدام خلفية CSS */
.hero-image {
background-image: url('hero-light.jpg');
background-size: cover;
}
@media (prefers-color-scheme: dark) {
.hero-image {
background-image: url('hero-dark.jpg');
}
}
/* الاستراتيجية 4: عكس أيقونات SVG أحادية اللون */
@media (prefers-color-scheme: dark) {
.icon-dark-invert {
filter: invert(1);
}
}
/* الاستراتيجية 5: استخدام currentColor في SVGs للتنسيق التلقائي */
/* SVG مع fill="currentColor" يرث لون النص */
.icon-svg {
color: var(--color-text-primary);
/* تعبئات SVG تتطابق تلقائيا مع المظهر */
}
التعامل مع الظلال والحدود في الوضع الداكن
تتصرف الظلال والحدود بشكل مختلف في الوضع الداكن. الظلال التي تبدو خفيفة على خلفية فاتحة تصبح غير مرئية تقريبا على خلفية داكنة. الحدود التي توفر فصلا لطيفا في الوضع الفاتح قد تبدو قاسية في الوضع الداكن. يجب أن تراعي رموز تصميمك هذه الاختلافات.
تعديلات الظلال والحدود للوضع الداكن
/* الوضع الفاتح: ظلال خفيفة للعمق */
:root {
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.08),
0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-dropdown: 0 4px 12px rgba(0, 0, 0, 0.1);
--shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.15);
--border-subtle: 1px solid rgba(0, 0, 0, 0.08);
--border-default: 1px solid rgba(0, 0, 0, 0.12);
}
/* الوضع الداكن: ظلال أقوى، حدود أنعم */
@media (prefers-color-scheme: dark) {
:root {
/* الظلال تحتاج شفافية أعلى لتكون مرئية على خلفيات داكنة */
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3),
0 1px 2px rgba(0, 0, 0, 0.2);
--shadow-dropdown: 0 4px 12px rgba(0, 0, 0, 0.4);
--shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.6);
/* الحدود تستخدم ألوانا أفتح بدلا من أغمق */
--border-subtle: 1px solid rgba(255, 255, 255, 0.06);
--border-default: 1px solid rgba(255, 255, 255, 0.1);
}
}
/* بديل: استخدام ظلال "توهج" في الوضع الداكن للأسطح المرتفعة */
@media (prefers-color-scheme: dark) {
.elevated-card {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.05),
0 4px 12px rgba(0, 0, 0, 0.4);
/* الحلقة الداخلية توفر حافة ضوء خفيفة */
}
}
اعتبارات التباين وإمكانية الوصول
الوضع الداكن ليس مجرد عكس للألوان. التطبيقات السيئة للوضع الداكن غالبا ما تفشل في معايير إمكانية الوصول بإنتاج تباين غير كاف بين النص والخلفية. تتطلب إرشادات إمكانية الوصول لمحتوى الويب (WCAG) نسبة تباين لا تقل عن 4.5:1 للنص العادي و3:1 للنص الكبير. هذه المتطلبات تنطبق بالتساوي على كلا الوضعين الفاتح والداكن.
ضمان تباين يسهل الوصول إليه في الوضع الداكن
/* خطأ: أخطاء شائعة في تباين الوضع الداكن */
/* الخطأ 1: نص باهت جدا على خلفية داكنة */
.bad-dark-text {
background: #1a1a2e;
color: #555;
/* نسبة التباين: ~2.1:1 -- يفشل في WCAG AA */
}
/* الخطأ 2: نص أبيض خالص على أسود خالص -- قاسي جدا */
.harsh-contrast {
background: #000000;
color: #ffffff;
/* نسبة التباين: 21:1 -- يجتاز تقنيا لكن يسبب
إجهاد العين وتأثير الهالة (نص متوهج) */
}
/* صحيح: تباين وضع داكن متوازن */
.good-dark-mode {
background: #1e293b; /* رمادي داكن، ليس أسود خالص */
color: #e2e8f0; /* أبيض مائل، ليس أبيض خالص */
/* نسبة التباين: ~11.5:1 -- مريح ويسهل الوصول إليه */
}
/* صحيح: تباين ألوان دلالية في الوضع الداكن */
:root {
/* الوضع الفاتح: نص داكن على خلفية فاتحة */
--text-primary: #0f172a; /* رمادي داكن جدا */
--bg-primary: #ffffff; /* أبيض */
/* التباين: 18.4:1 */
}
@media (prefers-color-scheme: dark) {
:root {
/* الوضع الداكن: نص فاتح على خلفية داكنة */
--text-primary: #e2e8f0; /* رمادي فاتح (ليس أبيض خالص) */
--bg-primary: #0f172a; /* أزرق-رمادي داكن جدا (ليس أسود خالص) */
/* التباين: 12.6:1 -- ممتاز */
}
}
#000000) كخلفية الوضع الداكن. الأسود الخالص مع النص الأبيض يسبب "الهالة" -- تأثير بصري حيث يبدو النص الساطع ينزف أو يتوهج على خلفية سوداء حقيقية، مما يجعل القراءة أصعب. بدلا من ذلك، استخدم رمادي داكن جدا أو أزرق-رمادي مثل #0f172a أو #1a1a2e أو #121212. وبالمثل، تجنب النص الأبيض الخالص (#ffffff) في الوضع الداكن؛ استخدم قيم أبيض مائل مثل #e2e8f0 أو #f1f5f9 لتجربة قراءة أكثر راحة.تبديل المظهر اليدوي بـ JavaScript
بينما يتطابق prefers-color-scheme تلقائيا مع تفضيل النظام، يريد العديد من المستخدمين تجاوز هذا على أساس كل موقع. مفتاح تبديل المظهر اليدوي يتيح للمستخدمين اختيار مظهرهم المفضل بغض النظر عن إعداد النظام. التطبيق يستخدم سمة بيانات على عنصر HTML وتجاوزات خصائص CSS المخصصة.
استراتيجية تطبيق تبديل المظهر
/* الخطوة 1: تحديد المظاهر باستخدام سمة بيانات بدل استعلامات الوسائط */
/* الافتراضي: المظهر الفاتح */
:root {
color-scheme: light dark;
--color-bg: #ffffff;
--color-text: #0f172a;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
--color-accent: #2563eb;
}
/* الوضع الداكن للنظام (عند عدم وجود تجاوز يدوي) */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) {
--color-bg: #0f172a;
--color-text: #f1f5f9;
--color-surface: #1e293b;
--color-border: #334155;
--color-accent: #3b82f6;
}
}
/* تجاوز المظهر الداكن اليدوي */
[data-theme="dark"] {
--color-bg: #0f172a;
--color-text: #f1f5f9;
--color-surface: #1e293b;
--color-border: #334155;
--color-accent: #3b82f6;
color-scheme: dark;
}
/* تجاوز المظهر الفاتح اليدوي */
[data-theme="light"] {
--color-bg: #ffffff;
--color-text: #0f172a;
--color-surface: #f8fafc;
--color-border: #e2e8f0;
--color-accent: #2563eb;
color-scheme: light;
}
JavaScript لتبديل المظهر مع localStorage
/* الخطوة 2: JavaScript لزر تبديل المظهر */
// الحصول على المظهر المخزن أو الافتراضي لتفضيل النظام
function getStoredTheme() {
return localStorage.getItem('theme');
}
// تطبيق المظهر على المستند
function applyTheme(theme) {
if (theme === 'light' || theme === 'dark') {
document.documentElement.setAttribute('data-theme', theme);
} else {
// 'system' -- إزالة السمة، دع استعلام الوسائط يتعامل معها
document.documentElement.removeAttribute('data-theme');
}
}
// تهيئة المظهر عند تحميل الصفحة
function initTheme() {
const stored = getStoredTheme();
if (stored) {
applyTheme(stored);
}
// إذا لم يوجد تفضيل مخزن، استعلام وسائط CSS يتعامل معه
}
// التبديل بين فاتح، داكن، ونظام
function toggleTheme() {
const current = getStoredTheme();
const systemDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;
let next;
if (!current || current === 'system') {
next = systemDark ? 'light' : 'dark';
} else if (current === 'dark') {
next = 'light';
} else {
next = 'dark';
}
localStorage.setItem('theme', next);
applyTheme(next);
}
// التشغيل عند تحميل الصفحة
initTheme();
// الربط بزر التبديل
document.getElementById('theme-toggle')
.addEventListener('click', toggleTheme);
// الاستماع لتغييرات تفضيل النظام
window.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
if (!getStoredTheme() || getStoredTheme() === 'system') {
applyTheme('system');
}
});
حفظ تفضيل المظهر في localStorage
JavaScript أعلاه يستخدم بالفعل localStorage لحفظ اختيار المظهر. لكن هناك مشكلة حرجة: يوجد وميض للمظهر الخاطئ قبل تشغيل JavaScript. لحل هذا، تحتاج لتطبيق المظهر المخزن في أبكر وقت ممكن، بشكل مثالي مع سكريبت مضمن في <head> من مستند HTML.
منع وميض المظهر عند تحميل الصفحة
/* ضع هذا السكريبت المضمن في <head>، قبل أي CSS */
/* HTML:
<head>
<meta name="color-scheme" content="light dark">
<script>
// تطبيق المظهر المخزن فورا لمنع الوميض
(function() {
var theme = localStorage.getItem('theme');
if (theme === 'dark' || theme === 'light') {
document.documentElement.setAttribute('data-theme', theme);
}
})();
</script>
<link rel="stylesheet" href="styles.css">
</head>
*/
/* لماذا يعمل هذا:
1. السكريبت المضمن يعمل بشكل متزامن قبل تحميل CSS
2. يعيّن سمة data-theme فورا
3. عند تحميل CSS، يرى السمة ويطبق المظهر الصحيح
4. المستخدم لا يرى أبدا وميض نظام الألوان الخاطئ
وسم meta يوفر لون الخلفية الافتراضي الصحيح
حتى قبل تشغيل السكريبت، مما يعطي المتصفح تلميحا عن
نظام الألوان الذي يبدأ به. */
ميزة الوسائط prefers-contrast
مرتبطة بـ prefers-color-scheme، ميزة الوسائط prefers-contrast تكتشف ما إذا كان المستخدم قد طلب تباينا متزايدا أو منخفضا. هذا مهم بشكل خاص للوضع الداكن لأن المظاهر الداكنة أحيانا تقلل التباين لإنشاء جمالية أنعم، مما قد يكون مشكلة للمستخدمين ذوي ضعف البصر.
استخدام prefers-contrast
/* الأنماط الافتراضية */
:root {
--color-text-secondary: #64748b;
--color-border: #e2e8f0;
}
/* تفضيل تباين عالي */
@media (prefers-contrast: more) {
:root {
--color-text-secondary: #334155;
--color-border: #475569;
}
/* زيادة رؤية الحدود */
.card {
border-width: 2px;
}
/* جعل مؤشرات التركيز أكثر بروزا */
:focus-visible {
outline: 3px solid var(--color-accent);
outline-offset: 2px;
}
}
/* الجمع مع الوضع الداكن لمظهر داكن عالي التباين */
@media (prefers-color-scheme: dark) and (prefers-contrast: more) {
:root {
--color-text-primary: #ffffff;
--color-text-secondary: #e2e8f0;
--color-bg-primary: #000000;
--color-border: #94a3b8;
}
}
/* تفضيل تباين منخفض (نادر) */
@media (prefers-contrast: less) {
:root {
--color-text-secondary: #94a3b8;
--color-border: #f1f5f9;
}
}
اختبار الوضع الداكن في أدوات المطور
جميع المتصفحات الحديثة توفر أدوات لمحاكاة الوضع الداكن بدون تغيير إعدادات النظام. هذا يجعل الاختبار سريعا ومريحا أثناء التطوير.
اختبار الوضع الداكن في أدوات المطور
/* أدوات مطوري Chrome:
1. افتح أدوات المطور (F12 أو Cmd+Opt+I)
2. انقر على قائمة النقاط الثلاث (⋮) في شريط أدوات المطور
3. انقر على "أدوات إضافية" ثم "العرض"
4. ابحث عن "محاكاة ميزة وسائط CSS prefers-color-scheme"
5. اختر "prefers-color-scheme: dark" من القائمة المنسدلة */
/* أدوات مطوري Firefox:
1. افتح أدوات المطور (F12)
2. انقر على زر "تبديل محاكاة الوضع الداكن"
في شريط أدوات المفتش (أيقونة شمس/قمر) */
/* أدوات مطوري Safari:
1. افتح مفتش الويب (Cmd+Opt+I)
2. اذهب للوحة العناصر
3. انقر على أيقونة المظهر في شريط الأدوات
4. بدّل بين الوضع الفاتح والداكن */
/* قائمة تحقق لاختبار الوضع الداكن:
- جميع النصوص تفي بنسب التباين الدنيا (4.5:1 لنص الجسم)
- الصور تبدو مناسبة (ليست ساطعة/داكنة جدا)
- الحدود والفواصل مرئية
- مؤشرات التركيز مرئية بوضوح
- عناصر التحكم في النماذج منسقة بشكل صحيح
- الظلال توفر إدراكا كافيا للعمق
- لا عناصر "تختفي" على الخلفية الداكنة
- الألوان الدلالية (نجاح، خطأ، تحذير) تبقى قابلة للتمييز
- لا ألوان ثابتة تتجاوز نظام الرموز */
تطبيق الوضع الداكن الكامل
دعنا نجمع كل شيء معا في تطبيق وضع داكن كامل يتعامل مع تفضيل النظام، التبديل اليدوي، حفظ localStorage، منع الوميض، الصور، وإمكانية الوصول.
هيكل CSS كامل للوضع الداكن
/* ================================================
تطبيق الوضع الداكن الكامل
================================================ */
/* 1. تصريح color-scheme الجذري */
:root {
color-scheme: light dark;
}
/* 2. رموز المظهر الفاتح (الافتراضي) */
:root {
--bg-page: #ffffff;
--bg-surface: #ffffff;
--bg-surface-raised: #f8fafc;
--text-heading: #0f172a;
--text-body: #334155;
--text-muted: #64748b;
--interactive-primary: #2563eb;
--border-default: #e2e8f0;
--elevation-1: 0 1px 3px rgba(0, 0, 0, 0.06);
--img-brightness: 1;
--img-contrast: 1;
}
/* 3. الوضع الداكن للنظام */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg-page: #0f172a;
--bg-surface: #1e293b;
--bg-surface-raised: #334155;
--text-heading: #f1f5f9;
--text-body: #cbd5e1;
--text-muted: #94a3b8;
--interactive-primary: #3b82f6;
--border-default: #334155;
--elevation-1: 0 1px 3px rgba(0, 0, 0, 0.3);
--img-brightness: 0.9;
--img-contrast: 1.05;
}
}
/* 4. تجاوز المظهر الداكن اليدوي */
[data-theme="dark"] {
--bg-page: #0f172a;
--bg-surface: #1e293b;
--bg-surface-raised: #334155;
--text-heading: #f1f5f9;
--text-body: #cbd5e1;
--text-muted: #94a3b8;
--interactive-primary: #3b82f6;
--border-default: #334155;
--elevation-1: 0 1px 3px rgba(0, 0, 0, 0.3);
--img-brightness: 0.9;
--img-contrast: 1.05;
color-scheme: dark;
}
/* 5. تطبيق الرموز على العناصر الأساسية */
body {
background-color: var(--bg-page);
color: var(--text-body);
transition: background-color 0.3s ease, color 0.3s ease;
}
h1, h2, h3, h4, h5, h6 {
color: var(--text-heading);
}
a {
color: var(--interactive-primary);
}
/* 6. تعديل سطوع الصور */
img:not(.no-dim) {
filter: brightness(var(--img-brightness)) contrast(var(--img-contrast));
transition: filter 0.2s ease;
}
/* 7. تنسيق عناصر التحكم في النماذج */
input, textarea, select {
background-color: var(--bg-surface);
color: var(--text-body);
border: 1px solid var(--border-default);
}
/* 8. دعم التباين العالي */
@media (prefers-contrast: more) {
:root {
--text-muted: #334155;
--border-default: #475569;
}
}
@media (prefers-color-scheme: dark) and (prefers-contrast: more) {
:root:not([data-theme="light"]) {
--text-body: #f1f5f9;
--text-muted: #cbd5e1;
--border-default: #64748b;
}
}
JavaScript كامل لإدارة المظهر
/* سكريبت إدارة المظهر الكامل */
class ThemeManager {
constructor() {
this.STORAGE_KEY = 'user-theme';
this.THEMES = ['light', 'dark', 'system'];
// الاستماع لتغييرات تفضيل النظام
this.systemQuery = window.matchMedia(
'(prefers-color-scheme: dark)'
);
this.systemQuery.addEventListener('change', () => {
if (this.getStored() === 'system') {
this.apply('system');
}
});
}
getStored() {
return localStorage.getItem(this.STORAGE_KEY) || 'system';
}
getSystemPreference() {
return this.systemQuery.matches ? 'dark' : 'light';
}
getEffective() {
const stored = this.getStored();
return stored === 'system' ? this.getSystemPreference() : stored;
}
apply(theme) {
const effective = theme === 'system'
? this.getSystemPreference()
: theme;
if (theme === 'system') {
document.documentElement.removeAttribute('data-theme');
} else {
document.documentElement.setAttribute('data-theme', theme);
}
// تحديث وسم meta
const meta = document.querySelector('meta[name="color-scheme"]');
if (meta) {
meta.content = effective === 'dark' ? 'dark' : 'light';
}
// إرسال حدث للمكونات الأخرى
window.dispatchEvent(new CustomEvent('themechange', {
detail: { theme, effective }
}));
}
set(theme) {
localStorage.setItem(this.STORAGE_KEY, theme);
this.apply(theme);
}
toggle() {
const current = this.getStored();
const idx = this.THEMES.indexOf(current);
const next = this.THEMES[(idx + 1) % this.THEMES.length];
this.set(next);
return next;
}
init() {
this.apply(this.getStored());
}
}
// التهيئة
const themeManager = new ThemeManager();
themeManager.init();
// الربط بزر التبديل
document.getElementById('theme-toggle')
?.addEventListener('click', () => {
themeManager.toggle();
});
data-theme، مما يسمح لاستعلام وسائط CSS prefers-color-scheme بالعمل. هذا النهج ثلاثي الحالات يمنح المستخدمين تحكما كاملا: يمكنهم اختيار اتباع تفضيل نظامهم أو تجاوزه على أساس كل موقع.انتقالات المظهر السلسة
إضافة انتقالات CSS لتبديل المظهر تخلق شعورا مصقولا واحترافيا. ومع ذلك، تحتاج للحذر بشأن الخصائص التي تحركها ومتى. تحريك كل خاصية على كل عنصر قد يسبب مشاكل أداء وعيوب بصرية.
إضافة انتقالات مظهر سلسة
/* تحريك خصائص محددة فقط على عناصر محددة */
/* الجسم والحاويات الرئيسية */
body,
.nav,
.sidebar,
.card,
.modal {
transition: background-color 0.3s ease,
color 0.3s ease,
border-color 0.3s ease;
}
/* تجنب تحريك كل شيء -- هذا بطيء */
/* سيء: * { transition: all 0.3s ease; } */
/* تعطيل الانتقالات أثناء التحميل الأولي للصفحة */
/* أضف هذه الفئة لـ <html> وأزلها بعد التحميل */
.no-transitions * {
transition: none !important;
}
/* JavaScript لمنع الوميض وتمكين الانتقالات:
document.documentElement.classList.add('no-transitions');
// ... تطبيق المظهر ...
requestAnimationFrame(() => {
requestAnimationFrame(() => {
document.documentElement.classList.remove('no-transitions');
});
});
*/
التمرين 1: لوحة تحكم بالوضع الداكن
ابنِ صفحة لوحة تحكم كاملة مع دعم كامل للوضع الداكن. أنشئ شريط تنقل في الأعلى بشعار وروابط تنقل وزر تبديل المظهر (أيقونة شمس/قمر). أسفله، أضف شريطا جانبيا بعناصر قائمة ومنطقة محتوى رئيسية تحتوي على أربع بطاقات إحصائيات وجدول بيانات. حدد نظام رموز ألوان شامل بما لا يقل عن 15 رمز تصميم يغطي الخلفيات والنصوص والحدود واللهجات والألوان الدلالية والظلال. نفذ المظهرين الفاتح والداكن باستخدام خصائص CSS المخصصة. أضف وسم <meta name="color-scheme"> وسكريبت مضمن في الرأس لمنع وميض المظهر. استخدم JavaScript لتنفيذ تبديل ثلاثي الحالات (فاتح، داكن، نظام) يحفظ الاختيار في localStorage. تأكد من أن أيقونة زر تبديل المظهر تتحول بشكل مناسب بين الشمس والقمر. تأكد من أن جميع النصوص تفي بنسب تباين WCAG AA (4.5:1 كحد أدنى) في كلا المظهرين باختبارها بأداة فحص التباين في أدوات المطور. أضف انتقالات سلسة بين المظاهر لكن عطلها أثناء تحميل الصفحة.
التمرين 2: موقع محفظة أعمال بالوضع الداكن
أنشئ صفحة محفظة أعمال شخصية تعرض مهاراتك في الوضع الداكن. أضف قسما رئيسيا بعنوان كبير وعنوان فرعي وتدرج خلفية يتغير بين المظاهر. أضف قسم عرض مشاريع ببطاقات مشاريع تحتوي كل منها على صورة وعنوان ووصف ووسوم تقنيات. أنشئ قسم مهارات بأشرطة تقدم تستخدم ألوان لهجة مختلفة. ابنِ نموذج اتصال بمدخلات منسقة تتكيف مع كلا المظهرين. للصور، استخدم عنصر <picture> مع متغيرات مصدر فاتحة وداكنة لصورة واحدة على الأقل، وطبق تعديلات السطوع/التباين على الصور الأخرى. نفذ التعامل السليم مع الظلال حيث تصبح الظلال أكثر وضوحا في الوضع الداكن. أضف استعلام وسائط prefers-contrast: more يزيد عرض الحدود وتباين النص في كلا المظهرين. أضف زر تبديل مظهر عائم مع انتقال متحرك سلس بين حالتي الشمس والقمر باستخدام حركات الإطارات المفتاحية CSS. احفظ اختيار المظهر وامنع الوميض عند إعادة التحميل. أخيرا، أضف قسم مقتطفات كود يستخدم كتل كود بتسليط بناء جملة بلوحات ألوان مختلفة للوضع الفاتح والداكن.