الوحدة 2 · الـ Design Tokens
الـ Themes والـ Modes
22 دقراءة 4 دبيئة تجريب الكود
الدرس في لمحة
ابنِ themes فاتحة وداكنة وعلامات تجارية وmodes للكثافة بإعادة ربط الـ semantic tokens تحت data attributes، حتى تظهر المكوّنات نفسها بشكل صحيح في كل تركيبة.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
ظلّ مستخدمو المناوبة الليلية في تطبيق الـ dispatch يطلبون الوضع الداكن سنتين. وكان كل تقدير يعود بـ «ستة أسابيع لكل منتج»، لأن الألوان كانت مكتوبة يدويًا في مئات المكوّنات. ومع الطبقة الدلالية من الدرس السابق، استغرق الوضع الداكن في Northwind تسعة أيام، معظمها مراجعة تصميم. ولم تتغيّر المكوّنات إطلاقًا.
الـ theme إعادة ربط
الـ theme يغيّر الـ primitive الذي يشير إليه كل semantic token. ولا شيء غير ذلك.
:root {
--nw-color-surface: var(--nw-gray-0);
--nw-color-text: var(--nw-gray-900);
--nw-color-action-bg: var(--nw-blue-600);
--nw-color-action-text: var(--nw-gray-0);
}
[data-theme="dark"] {
color-scheme: dark;
--nw-color-surface: var(--nw-gray-950);
--nw-color-text: var(--nw-gray-100);
--nw-color-action-bg: var(--nw-blue-400);
--nw-color-action-text: var(--nw-gray-950);
}هناك تفصيلتان مهمّتان. الأولى أن الـ selector هو [data-theme="dark"] لا html[data-theme="dark"]، فيستطيع أي عنصر بدء شجرة فرعية داكنة: الواجهة الرئيسية في الموقع التسويقي داكنة على صفحة فاتحة في باقيها. والثانية أن color-scheme: dark تخبر المتصفّح أن يرسم واجهته الخاصة، مثل أشرطة التمرير ومربّعات الاختيار وحقول التاريخ، بألوان داكنة أيضًا.
لاحظ أن أزرق الإجراء يغيّر درجته، من 600 إلى 400. الخلفيات الداكنة تحتاج ألوان تمييز أفتح لتحافظ على التباين، لذا فالوضع الداكن مهمّة تصميم، لا قلبٌ للألوان.
تصميم لوحة الألوان الداكنة
ثلاث قواعد وفّرت على Northwind أسابيع من المراجعة. افحص التباين لكل زوج من النص والخلفية في الـ theme الداكن على حدة، لأن الزوج الذي ينجح على الأبيض قد يفشل على ما يقارب الأسود. وتجنّب الأسطح السوداء تمامًا؛ فالرمادي الداكن جدًا مثل #111827 أريح للعين ويترك مجالًا لظلال أغمق. وعبّر عن الارتفاع (elevation) بأسطح أفتح لا بظلال أثقل، لأن الظلال بالكاد تظهر على الخلفيات الداكنة: البطاقة المرتفعة في الوضع الداكن أفتح بدرجة من الصفحة التي خلفها.
هذه القاعدة الأخيرة مثال جيد على حاجة الطبقة الدلالية إلى أسماء مثل --nw-color-surface-raised. في الوضع الفاتح يرتبط بالأبيض على صفحة رمادية فاتحة؛ وفي الوضع الداكن يرتبط برمادي أفتح قليلًا. الاسم نفسه، والدور نفسه، والقيم مختلفة.
اتّباع تفضيل النظام
احترم إعداد نظام التشغيل افتراضيًا، واسمح للمستخدمين بتجاوزه:
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--nw-color-surface: var(--nw-gray-950);
/* …same remapping as [data-theme="dark"] */
}
}// Inline in <head>, before the CSS paints, so there is no flash of the wrong theme.
const saved = localStorage.getItem('nw-theme');
if (saved === 'light' || saved === 'dark') {
document.documentElement.dataset.theme = saved;
}تكرار الكتلة الداكنة داخل الـ media query هو ثمن دعم الطريقتين معًا. خط إنتاج الـ tokens في Northwind يولّد النسختين من مصدر واحد، وستجهّزه بعد درسين. وللألوان البسيطة ذات الوضعين يمكنك أيضًا استخدام الدالة light-dark() في CSS مع color-scheme: light dark، لكنها لا تبدّل إلا الألوان (والصور أيضًا في المتصفّحات الأحدث) بين وضعين فقط، فلا تغني عن إعادة ربط الـ tokens للعلامات أو الكثافة.
الـ modes محاور مستقلة
لدى Northwind ثلاثة محاور، يُحدَّد كلٌّ منها بسمة خاصة به:
الحيلة التي تحفظ استقلالها هي الملكية: كل محور يعيد ربط الـ tokens الخاصة به، ولا يلمس tokens محور آخر أبدًا. الكثافة لا تلمس إلا المسافات:
:root {
--nw-space-control-y: var(--nw-space-2);
--nw-space-control-x: var(--nw-space-4);
}
[data-density="compact"] {
--nw-space-control-y: var(--nw-space-1);
--nw-space-control-x: var(--nw-space-3);
}أما العلامة ونظام الألوان فيتفاعلان فعلًا، لأن فيروزي Tidewater يحتاج درجة أفتح في الوضع الداكن تمامًا مثل أزرق Northwind. والحلّ مستوى واحد من الوساطة: العلامة تعرّف تدرّجًا من primitives العلامة، والـ theme يختار الدرجة.
:root { /* Northwind is the default brand */
--nw-brand-400: var(--nw-blue-400);
--nw-brand-600: var(--nw-blue-600);
}
[data-brand="tidewater"] {
--nw-brand-400: var(--nw-teal-400);
--nw-brand-600: var(--nw-teal-600);
}
:root { --nw-color-action-bg: var(--nw-brand-600); }
[data-theme="dark"] { --nw-color-action-bg: var(--nw-brand-400); }الآن يعمل <html data-brand="tidewater" data-theme="dark" data-density="compact"> دون selector مركّب واحد. ملاحظة واحدة: هذه النسخة تفترض أن data-brand موجودة على <html>، وهذا صحيح في Northwind لأن الصفحة تنتمي إلى علامة واحدة. فالربط على :root يُحسب مرة واحدة عند الجذر، وإن كان يمكن للعلامة أن تتغيّر في منتصف الصفحة فستكرّر الروابط الدلالية تحت [data-brand] أيضًا، للسبب نفسه الذي يجعل الـ component tokens تعيش على الـ selectors الخاصة بالمكوّنات. العلامة تقرّر أيّ الألوان، والـ theme يقرّر مدى الفتح، والكثافة تقرّر مدى الإحكام. ثماني تركيبات، وست كتل صغيرة.
في التمرين تضيف theme داكنًا يعمل على أي عنصر، وmode كثافة مضغوطة. وبعدها تنقل هذه الـ tokens من CSS مكتوب يدويًا إلى صيغة تقرؤها كل الأدوات.
أهم النقاط
- الـ theme أو الـ mode إعادةُ ربط للـ semantic tokens؛ أما المكوّنات والـ primitives فتبقى كما هي.
- عامل نظام الألوان والعلامة والكثافة كمحاور مستقلة، يملك كلٌّ منها مجموعة tokens خاصة به، حتى لا تتضاعف أعمالك مع التركيبات.
- الـ selectors مثل
[data-theme="dark"]تعمل على أي عنصر، فيمكن لقسم من الصفحة أن يستخدم theme مختلفًا عن بقيتها. - حدّد
color-schemeمع الـ theme الداكن حتى تتطابق الأجزاء التي يرسمها المتصفّح، مثل عناصر النماذج وأشرطة التمرير.