EduFlow

الوحدة 2 · الـ Design Tokens

الـ Themes والـ Modes

22 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

ابنِ themes فاتحة وداكنة وعلامات تجارية وmodes للكثافة بإعادة ربط الـ semantic tokens تحت data attributes، حتى تظهر المكوّنات نفسها بشكل صحيح في كل تركيبة.

  1. 1الـ theme إعادة ربط
  2. 2تصميم لوحة الألوان الداكنة
  3. 3اتّباع تفضيل النظام
  4. 4الـ modes محاور مستقلة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

ظلّ مستخدمو المناوبة الليلية في تطبيق الـ dispatch يطلبون الوضع الداكن سنتين. وكان كل تقدير يعود بـ «ستة أسابيع لكل منتج»، لأن الألوان كانت مكتوبة يدويًا في مئات المكوّنات. ومع الطبقة الدلالية من الدرس السابق، استغرق الوضع الداكن في Northwind تسعة أيام، معظمها مراجعة تصميم. ولم تتغيّر المكوّنات إطلاقًا.

الـ theme إعادة ربط

الـ theme يغيّر الـ primitive الذي يشير إليه كل semantic token. ولا شيء غير ذلك.

CSS
: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. في الوضع الفاتح يرتبط بالأبيض على صفحة رمادية فاتحة؛ وفي الوضع الداكن يرتبط برمادي أفتح قليلًا. الاسم نفسه، والدور نفسه، والقيم مختلفة.

اتّباع تفضيل النظام

احترم إعداد نظام التشغيل افتراضيًا، واسمح للمستخدمين بتجاوزه:

CSS
@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"] */
  }
}
JavaScript
// 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 الأسطح والنص. العلامة، Northwind أو Tidewater، تملك تدرّج ألوان العلامة. الكثافة، مريحة أو مضغوطة، تملك المسافات وارتفاع العناصر. والثلاثة تغذّي المكوّنات نفسها. data-theme light | dark الأسطح، النص، الدرجات data-brand northwind | tidewater تدرّج العلامة، الخطوط data-density comfortable | compact المسافات، ارتفاع العناصر المكوّنات نفسها
ثلاثة محاور مستقلة، يملك كلٌّ منها مجموعة مختلفة من الـ tokens

الحيلة التي تحفظ استقلالها هي الملكية: كل محور يعيد ربط الـ tokens الخاصة به، ولا يلمس tokens محور آخر أبدًا. الكثافة لا تلمس إلا المسافات:

CSS
: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 يختار الدرجة.

CSS
: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 الداكن حتى تتطابق الأجزاء التي يرسمها المتصفّح، مثل عناصر النماذج وأشرطة التمرير.

قراءات إضافية

تحقّق من فهمك

3 أسئلة

السؤال 1 من 3

لدى Northwind نظاما ألوان، وعلامتان، ومستويا كثافة. كم ملف theme مكتوبًا يدويًا يجب أن تحتاج إن كانت المحاور مستقلة؟

السؤال 2 من 3

تضع الواجهة الرئيسية في الموقع التسويقي data-theme="dark" على الـ <section> الخاص بها، لكن بطاقاتها تبقى بيضاء. الكتلة الداكنة مكتوبة هكذا html[data-theme="dark"] { … }. ما الخطأ؟

السؤال 3 من 3

في الوضع الداكن تستخدم أزرار Northwind اللون --nw-blue-400 بدل --nw-blue-600، وأزرار Tidewater تستخدم تدرّجًا فيروزيًا. أيّ بنية تتعامل مع الحالتين بنظافة؟

تدريب

بيئة تجريب الكود

أضف theme داكنًا وmode مضغوطًا

الـ theme الفاتح والمكوّنات جاهزة. أضف اثنين من الـ modes بإعادة ربط الـ semantic tokens فقط (لا تلمس المكوّنات):

  • theme داكن لأي عنصر يحمل data-theme="dark"، لا <html> وحده: السطح ← --nw-gray-950، والنص ← --nw-gray-100، وخلفية الإجراء ← --nw-blue-400، ونص الإجراء ← --nw-gray-950. وحدّد أيضًا color-scheme: dark.
  • كثافة مضغوطة لـ data-density="compact": الحشوة العمودية للعناصر ← --nw-space-1، والأفقية ← --nw-space-3.

قسم الواجهة الرئيسية يحمل data-theme="dark" مسبقًا، فيجب أن يصبح داكنًا بمجرّد أن يعمل الـ theme الخاص بك.

<main class="page">
  <article class="card" id="app-card">
    <h3>Route 18 · 6 stops</h3>
    <button class="nw-button" id="app-btn">Dispatch</button>
  </article>
  <section class="hero" data-theme="dark">
    <article class="card" id="hero-card">
      <h3>Ship smarter with Northwind</h3>
      <button class="nw-button">Book a demo</button>
    </article>
  </section>
</main>جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

المعاينة

الـ console

(لا مخرجات)

الفحوص

  • تبقى الصفحة فاتحة افتراضيًا
  • بطاقة الواجهة الرئيسية (داخل data-theme="dark") تستخدم السطح والنص الداكنين
  • الأزرار داخل الواجهة الرئيسية الداكنة تستخدم الأزرق الأفتح مع نص داكن
  • وضع data-theme="dark" على <html> يجعل الصفحة كلها داكنة ويحدّد color-scheme: dark
  • data-density="compact" يقلّص حشوة الزر إلى 4px في 12px