{"id":"l-ds-2-5","courseId":"design-systems","lang":"ar","kind":"lesson","summary":"جهّز خط إنتاج (pipeline) لبناء الـ tokens يحوّل JSON بصيغة DTCG إلى CSS custom properties لكل theme وإلى صيغ أصلية لكل منصّة، مع الحفاظ على المراجع حتى يبقى تغيير الـ theme وقت التشغيل ممكنًا.","html":"<p>بعد أن صارت الـ tokens مخزّنة كـ JSON بصيغة DTCG، يجب أن يحوّلها شيء ما إلى ما تستهلكه كل منصّة: CSS custom properties لتطبيق الويب والموقع التسويقي، وSwift لتطبيق السائقين على iOS، وKotlin أو XML لـ Android. هذا الشيء هو خط إنتاج الـ tokens (token pipeline). إنه قطعة كود صغيرة ينتهي كل فريق نظام تصميم بامتلاكها، وإتقانها مبكرًا يوفّر شهورًا من الانحراف لاحقًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">ماذا يفعل خط الإنتاج</h2>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-ds-2-5-t1\"><defs><marker id=\"arrow-l-ds-2-5-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-ds-2-5-t1\">ملفات JSON الخاصة بالـ tokens تمرّ بخطوات التحليل وحلّ المراجع والتحويل والتنسيق، فتنتج CSS للويب وSwift لـ iOS وKotlin لـ Android.</title>\n  <rect class=\"d-box-primary\" x=\"10\" y=\"85\" width=\"110\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"65\" y=\"110\" text-anchor=\"middle\">*.tokens</text>\n  <text class=\"d-label\" x=\"65\" y=\"128\" text-anchor=\"middle\">.json</text>\n  <rect class=\"d-box\" x=\"150\" y=\"85\" width=\"80\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"190\" y=\"118\" text-anchor=\"middle\">تحليل</text>\n  <rect class=\"d-box\" x=\"250\" y=\"85\" width=\"90\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"295\" y=\"118\" text-anchor=\"middle\">حلّ المراجع</text>\n  <rect class=\"d-box\" x=\"360\" y=\"85\" width=\"100\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"410\" y=\"118\" text-anchor=\"middle\">تحويل</text>\n  <rect class=\"d-box\" x=\"480\" y=\"85\" width=\"80\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"520\" y=\"118\" text-anchor=\"middle\">تنسيق</text>\n  <rect class=\"d-box-accent\" x=\"590\" y=\"20\" width=\"80\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"630\" y=\"47\" text-anchor=\"middle\">CSS</text>\n  <rect class=\"d-box-accent\" x=\"590\" y=\"91\" width=\"80\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"630\" y=\"118\" text-anchor=\"middle\">Swift</text>\n  <rect class=\"d-box-accent\" x=\"590\" y=\"162\" width=\"80\" height=\"44\" rx=\"10\"/>\n  <text class=\"d-label\" x=\"630\" y=\"189\" text-anchor=\"middle\">Kotlin</text>\n  <path class=\"d-arrow\" d=\"M120 113 L146 113\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M230 113 L246 113\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M340 113 L356 113\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M460 113 L476 113\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M560 105 L586 45\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M560 113 L586 113\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n  <path class=\"d-arrow\" d=\"M560 121 L586 183\" marker-end=\"url(#arrow-l-ds-2-5-1)\"/>\n</svg><figcaption>خط إنتاج الـ tokens: مصدر واحد، وإخراج واحد لكل منصّة</figcaption></figure>\n<ol>\n<li><strong>التحليل (parse)</strong>: يقرأ كل ملفات المصدر ويدمجها دمجًا عميقًا في شجرة واحدة.</li>\n<li><strong>حلّ المراجع (resolve)</strong>: يحلّ الأسماء المستعارة مثل <code dir=\"ltr\">{color.blue.600}</code>، ويرفض المراجع المكسورة أو الدائرية.</li>\n<li><strong>التحويل (transform)</strong>: يحوّل الأسماء والقيم لكل منصّة: يصبح <code dir=\"ltr\">color.action.bg</code> هو <code dir=\"ltr\">--nw-color-action-bg</code> في CSS و<code dir=\"ltr\">colorActionBg</code> في Swift؛ ويصبح <code dir=\"ltr\">1rem</code> هو <code dir=\"ltr\">16</code> نقطة على iOS.</li>\n<li><strong>التنسيق (format)</strong>: يكتب النتيجة في ملف: كتلة custom properties، أو enum في Swift، أو ملف موارد Android.</li>\n</ol>\n<p>هذا قلب جانب CSS، وهو صغير بما يكفي لتقرأه دفعة واحدة:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"0\" aria-label=\"شغّل هذا المثال\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>تشغيل</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> tokens</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  color: {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    $type: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'color'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    blue: { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">600</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: { $value: { colorSpace: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'srgb'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, components: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.122</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.353</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0.878</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], hex: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#1f5ae0'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> } } },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    white: { $value: { colorSpace: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'srgb'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, components: [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">], hex: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'#ffffff'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> } },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    action: {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      bg: { $value: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'{color.blue.600}'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      text: { $value: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'{color.white}'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  space: { $type: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'dimension'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">4</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: { $value: { value: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, unit: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'rem'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> } } },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Walk the tree and collect every token with its path.</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> flatten</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">node</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">path</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [], </span><span style=\"color:#953800;--shiki-dark:#FFA657\">type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> t</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> node.$type </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">??</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> type;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'$value'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> node) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [{ path, type: t, value: node.$value }];</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Object.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">entries</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(node)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">filter</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(([</span><span style=\"color:#953800;--shiki-dark:#FFA657\">key</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">key.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">startsWith</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'$'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">))</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    .</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">flatMap</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(([</span><span style=\"color:#953800;--shiki-dark:#FFA657\">key</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">child</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">]) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> flatten</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(child, [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">path, key], t));</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> cssName</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#953800;--shiki-dark:#FFA657\">path</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `--nw-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">path</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">join</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'-'</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> cssValue</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">value</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">typeof</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> value </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'string'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> &#x26;&#x26;</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> value.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">startsWith</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'{'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `var(${</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">cssName</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">value</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">slice</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">split</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'.'</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">))</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">})`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// keep the reference</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (type </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'color'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> value.hex;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (type </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'dimension'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">return</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">value</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">value</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">value</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">unit</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(value);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> lines</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> flatten</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(tokens).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">t</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `  ${</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">cssName</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">t</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">path</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}: ${</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">cssValue</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">t</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">};`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`/* Generated from tokens. Do not edit. */</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\n</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">:root {</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\n</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">lines</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">join</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\n</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\n</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>انظر إلى الإخراج. المسار يصبح الاسم، فيخرج <code dir=\"ltr\">color.blue.600</code> بالشكل <code dir=\"ltr\">--nw-color-blue-600</code>. في الدروس السابقة اختصرنا الـ primitives إلى <code dir=\"ltr\">--nw-blue-600</code> لتسهيل القراءة، لكن في نظام مولَّد يكون الاسم هو ما تقوله بنية المجموعات، وهذا سبب إضافي لتصمّم المجموعات والأسماء معًا. والأهم أن الـ semantic tokens تخرج بالشكل <code dir=\"ltr\">var(--nw-color-blue-600)</code> لا كقيمة hex. وهذا الاختيار مهمّ بما يكفي ليستحقّ قسمًا خاصًا به.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">احتفظ بالمراجع</h2>\n<p>يمكن لخط الإنتاج إما أن يحلّ الأسماء المستعارة إلى قيمها النهائية، أو أن يُبقيها مراجع في الإخراج. في CSS، أبقِها. الإخراج المحلول يسطّح طبقاتك: يرى المتصفّح <code dir=\"ltr\">--nw-color-action-bg: #1f5ae0</code>، فلا يفعل تخصيص الـ primitive وقت التشغيل شيئًا، ولا يستطيع مطوّر يفحص زرًا في DevTools أن يرى من أي primitive جاء لونه. أما الإخراج بالمراجع فيحفظ البنية التي صمّمتها بالضبط، بما فيها تغيير الـ theme على الأشجار الفرعية من درس الـ themes.</p>\n<p>المنصّات الأصلية مختلفة. الثوابت في Swift وKotlin تُحلّ عادةً إلى قيمها النهائية، لأن هذه المنصّات تتعامل مع الـ themes بآلياتها الخاصة (asset catalogs، وcolor schemes في Compose).</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">خط إنتاج حقيقي مع Style Dictionary</h2>\n<p>يمكنك أن تطوّر السكربت أعلاه إلى أداة كاملة، لكن أغلب الفرق تستخدم Style Dictionary، وهو أكثر أنظمة بناء الـ tokens مفتوحة المصدر انتشارًا. إصداراته الحديثة تقرأ ملفات DTCG مباشرة وتكتشف الصيغة تلقائيًا. وإعداد على طريقة Northwind يبني الـ theme الفاتح داخل <code dir=\"ltr\">:root</code> والـ theme الداكن تحت الـ attribute selector الخاص به:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">build-tokens.mjs</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> StyleDictionary </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'style-dictionary'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> themes</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  light: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">':root'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  dark: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'[data-theme=\"dark\"]'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">};</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">name</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">selector</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> Object.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">entries</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(themes)) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> sd</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> new</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> StyleDictionary</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    include: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'tokens/primitives.tokens.json'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">],</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    source: [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`tokens/semantic-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.tokens.json`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">],</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    platforms: {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      css: {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        transformGroup: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'css'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        prefix: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'nw'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        buildPath: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'dist/css/'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        files: [{</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">          destination: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`theme-${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">name</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">}.css`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">          format: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'css/variables'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">          filter: name </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">===</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'light'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ?</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> undefined</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span><span style=\"color:#953800;--shiki-dark:#FFA657\">token</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> token.isSource,</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">          options: { selector, outputReferences: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }],</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  });</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  await</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sd.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">buildAllPlatforms</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الأفكار الأساسية: الـ primitives تدخل عبر <code dir=\"ltr\">include</code> حتى يمكن حلّ المراجع، بينما يدخل الملف الدلالي الخاص بالـ theme عبر <code dir=\"ltr\">source</code>. وفي الـ theme الداكن يُبقي الـ <code dir=\"ltr\">filter</code> الـ tokens المصدرية وحدها، فيحتوي الملف الداكن على الـ semantic tokens المعاد ربطها لا على نسخة ثانية من كل primitive. و<code dir=\"ltr\">outputReferences: true</code> يُبقي مراجع <code dir=\"ltr\">var()</code> في الإخراج. وقد يحذّرك Style Dictionary من أن الملف الداكن يشير إلى tokens استبعدها الـ filter؛ وهذا مقصود هنا، لأن الملف الفاتح يعرّف الـ primitives على <code dir=\"ltr\">:root</code> أصلًا.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>تعديل الملفات المولَّدة</span></p>\n<p>الـ CSS المولَّد يبدو CSS عاديًا، فعاجلًا أو آجلًا سيصلح أحدهم لونًا مباشرة داخل <code dir=\"ltr\">dist/</code>. والبناء التالي يمحو الإصلاح. ضع ترويسة «مولَّد، لا تعدّل» في كل ملف إخراج، وأبقِ <code dir=\"ltr\">dist/</code> خارج فروقات مراجعة الكود، ومرّر كل تغيير عبر ملف JSON.</p>\n</aside>\n<aside class=\"callout callout-note\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg><span><span class=\"callout-kind\">ملاحظة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>افحص إصدار DTCG الذي تدعمه أداتك</span></p>\n<p>صيغة اللون ككائن جاءت مع DTCG 2025.10، والأدوات تبنّتها في أوقات مختلفة: فالـ transforms المدمجة في Style Dictionary تقرأ الألوان ككائنات منذ الإصدار 5.3، والأبعاد (dimensions) ككائنات منذ 5.4. قبل نقل ملفات Northwind إليها، شغّلنا خط الإنتاج على بعض الـ tokens التجريبية وقارنّا إخراج الـ CSS. افعل الشيء نفسه؛ وإن كان إصدارك ما زال يتوقّع نصوص hex، فأبقِ الحقل <code dir=\"ltr\">hex</code> مملوءًا أو أضف transform مخصّصًا صغيرًا.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">فحوص تعمل مع البناء</h2>\n<p>حين تصبح الـ tokens بيانات، يستطيع الـ CI أن يختبر قرارات التصميم كما يختبر الكود. يشغّل خط الإنتاج في Northwind ثلاثة فحوص على كل pull request: فحص التسمية (lint) الذي رأيته سابقًا، وفحص مراجع يفشل عند الأسماء المستعارة المكسورة أو الدائرية، وفحص تباين يحلّ كل زوج موثّق من النص والخلفية في كل theme ويفشل تحت 4.5:1. وهذا الأخير التقط تراجعًا في الوضع الداكن لعلامة Tidewater قبل أن يراه أي مصمّم.</p>\n<p>في التمرين تكمل إعداد بناء الـ theme الداكن. وبهذا تكتمل معمارية الـ tokens؛ والقسم التالي يبني المكوّنات فوقها.</p>\n","toc":[{"id":"sec-1","text":"ماذا يفعل خط الإنتاج","level":2},{"id":"sec-2","text":"احتفظ بالمراجع","level":2},{"id":"sec-3","text":"خط إنتاج حقيقي مع Style Dictionary","level":2},{"id":"sec-4","text":"فحوص تعمل مع البناء","level":2}],"takeaways":["خط إنتاج الـ tokens يحلّل ملفات المصدر، ويحلّ الأسماء المستعارة، ويحوّل الأسماء والقيم لكل منصّة، ويكتب صيغة إخراج واحدة لكل منصّة.","احتفظ بالمراجع في إخراج CSS (<code dir=\"ltr\">outputReferences</code>) حتى تبقى الـ semantic tokens تشير إلى الـ primitives وقت التشغيل، ويستمرّ تغيير الـ theme على الأشجار الفرعية في العمل.","ابنِ كل theme من الـ primitives المشتركة مع الملف الدلالي الخاص بذلك الـ theme، وأخرج الـ tokens الخاصة بالـ theme وحدها تحت الـ selector الخاص به.","الملفات المولَّدة مخرجات بناء؛ لا يعدّلها أحد يدويًا، والـ CI يعيد بناءها مع كل تغيير."],"further":[{"title":"Style Dictionary (official repository and docs)","url":"https://github.com/style-dictionary/style-dictionary"},{"title":"Design Tokens Community Group repository","url":"https://github.com/design-tokens/community-group"},{"title":"Using CSS custom properties (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties"}],"quiz":[{"q":"<p>يكتب خط الإنتاج <code dir=\"ltr\">--nw-color-action-bg: #1f5ae0;</code> بدل <code dir=\"ltr\">--nw-color-action-bg: var(--nw-color-blue-600);</code>. ما الذي ينكسر؟</p>","options":[{"text":"لا شيء؛ اللون متطابق.","why":"اللون متطابق اليوم، لكن تخصيص الـ primitive وقت التشغيل لم يعد يسري، ولم تعد الطبقات ظاهرة في الإخراج."},{"text":"يصبح الـ CSS غير صالح لأن قيم hex غير مسموحة في الـ custom properties.","why":"الـ custom properties تقبل أي سلسلة رموز صالحة، بما فيها ألوان hex."},{"text":"لا تستطيع المتصفّحات تخزين ملف الأنماط مؤقتًا.","why":"التخزين المؤقت لا يعتمد على محتوى الـ custom properties."},{"text":"تتوقّف تخصيصات الـ primitives وقت التشغيل عن الوصول إلى الـ semantic tokens، ويصعب تتبّع أي primitive يستخدمه الـ token.","why":"صحيح. مع الحفاظ على المراجع يحتفظ الإخراج ببنية الطبقات؛ ومع القيم المحلولة تُسطَّح كل الروابط."}],"answer":3},{"q":"<p>لماذا يستخدم بناء الـ theme الداكن <code dir=\"ltr\">include</code> للـ primitives و<code dir=\"ltr\">source</code> للملف الدلالي الداكن، ثم يصفّي على <code dir=\"ltr\">isSource</code>؟</p>","options":[{"text":"لأن الـ primitives لازمة لحلّ المراجع، لكن الـ semantic tokens الداكنة وحدها يجب أن تُكتب تحت الـ selector الداكن.","why":"صحيح. إخراج الـ primitives مرة أخرى تحت <code dir=\"ltr\">[data-theme=&quot;dark&quot;]</code> سيكرّر مئات الأسطر دون أن يغيّر شيئًا."},{"text":"لأن الملفات في <code dir=\"ltr\">include</code> تُحمَّل أسرع.","why":"السرعة ليست المقصود؛ الفرق هو هل تُعدّ الـ tokens tokens مصدرية لأغراض التصفية أم لا."},{"text":"لأن Style Dictionary لا يستطيع قراءة ملفين في <code dir=\"ltr\">source</code>.","why":"المصفوفة <code dir=\"ltr\">source</code> تقبل ملفات كثيرة وأنماط glob."}],"answer":0},{"q":"<p>عدّل مهندس منتج الملف <code dir=\"ltr\">dist/css/theme-dark.css</code> مباشرة ليصلح خللًا في التباين، فاختفى الإصلاح بعد الإصدار التالي. ماذا كان يجب أن تكون العملية؟</p>","options":[{"text":"عمل commit للـ CSS المولَّد حتى يُحفظ التعديل.","why":"الـ commit لا يمنع البناء التالي من الكتابة فوقه؛ فالمصدر ما زال خاطئًا."},{"text":"إصلاح الـ token في ملف JSON المصدر، وترك الـ CI يعيد البناء، ووضع ترويسة «لا تعدّل» على الملفات المولَّدة.","why":"صحيح. ملف JSON هو مصدر الحقيقة، ويجب أن تقول الملفات المولَّدة ذلك في أعلاها."},{"text":"إضافة الإصلاح إلى ملف الأنماط الخاص بالمنتج بدلًا من ذلك.","why":"هذا يخفي الخلل عن منتج واحد ويتركه عند كل مستخدم آخر للنظام."},{"text":"إيقاف خطوة بناء الـ CSS.","why":"حينها يتباعد CSS عن المنصّات الأخرى، وهذا بالضبط ما وُجد خط الإنتاج لمنعه."}],"answer":1}],"exercise":{"kind":"fill","language":"js","template":"export default {\n  include: ['tokens/primitives.tokens.json'],\n  source: ['tokens/semantic-dark.tokens.json'],\n  platforms: {\n    css: {\n      transformGroup: 'css',\n      prefix: '{{0}}',\n      buildPath: 'dist/css/',\n      files: [{\n        destination: 'theme-dark.css',\n        format: '{{1}}',\n        filter: (token) => token.{{2}},\n        options: {\n          selector: '{{3}}',\n          outputReferences: {{4}},\n        },\n      }],\n    },\n  },\n};","blanks":[{"answers":["nw"]},{"answers":["css/variables"]},{"answers":["isSource"]},{"answers":["[data-theme=\"dark\"]","[data-theme=dark]"]},{"answers":["true"]}],"title":"أكمل بناء الـ theme الداكن","prompt":"<p>املأ إعداد Style Dictionary الذي يولّد الـ theme الداكن في Northwind. يجب أن يحقّق الإخراج ما يلي:</p>\n<ul>\n<li>أن يُسمّى كل متغيّر بالبادئة <code dir=\"ltr\">nw</code> (<code dir=\"ltr\">--nw-…</code>)،</li>\n<li>أن يستخدم صيغة CSS custom properties المدمجة في Style Dictionary،</li>\n<li>أن يحتوي الـ semantic tokens الداكنة وحدها، دون الـ primitives المحمّلة عبر <code dir=\"ltr\">include</code>،</li>\n<li>أن يضعها داخل الـ attribute selector الذي يحوّل أي عنصر إلى الداكن، مكتوبًا بعلامات تنصيص مزدوجة داخل النص المحاط بعلامات مفردة،</li>\n<li>أن يُبقي مراجع <code dir=\"ltr\">var()</code> بدل القيم المحلولة.</li>\n</ul>\n","hints":["اسم الصيغة هو نفسه المستخدم في سكربت البناء في الدرس.","الـ tokens المحمّلة عبر <code dir=\"ltr\">source</code> تُعلَّم بشكل مختلف عن المحمّلة عبر <code dir=\"ltr\">include</code>. أيّ خاصية تميّز بينها؟","الـ selector هو نفسه من درس الـ themes: attribute selector وحده، دون <code dir=\"ltr\">html</code>."],"explanation":"<p><code dir=\"ltr\">prefix: 'nw'</code> يضيف نطاق الأسماء إلى كل اسم. و<code dir=\"ltr\">css/variables</code> تكتب الـ custom properties داخل الـ selector المحدّد. والتصفية على <code dir=\"ltr\">token.isSource</code> تُبقي الـ semantic tokens الداكنة وتُسقط الـ primitives التي حُمّلت فقط لكي تُحلّ المراجع. و<code dir=\"ltr\">[data-theme=&quot;dark&quot;]</code> يسمح لأي عنصر ببدء شجرة فرعية داكنة، و<code dir=\"ltr\">outputReferences: true</code> يُبقي أسطرًا مثل <code dir=\"ltr\">--nw-color-action-bg: var(--nw-color-blue-400);</code> حتى تصل بنية الطبقات سليمة إلى المتصفّح.</p>\n","checkLabels":{}},"words":664,"readMinutes":4}