{"id":"l-ds-1-2","courseId":"design-systems","lang":"ar","kind":"lesson","summary":"أجرِ جردًا للواجهة (interface inventory) يشمل الشاشات والكود، واحسب القرارات المكرّرة مثل الألوان والأزرار، ثم حوّل الأرقام إلى قائمة مرتّبة بما يجب أن يملكه النظام أولًا.","html":"<p>قبل أن تصمّم token واحدًا، تحتاج أن تعرف ما هو موجود بالفعل. تتخطّى الفرق هذه الخطوة عادةً لأنها تشعر أنها تعرف أصلًا أن المنتج غير متّسق. لكن الشعور لا يكفي. تحتاج إلى أرقام يقبلها مدير المنتج وقائد الهندسة ونائب الرئيس جميعًا، وتحتاج إليها لتقرّر ما الذي يملكه النظام أولًا.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">جرد لقطات الشاشة</h2>\n<p>جرد الواجهة (interface inventory) سهل الوصف ومملّ التنفيذ: تلتقط صورة لكل نسخة من كل عنصر في واجهات منتجاتك، وتجمعها حسب النوع. كل الأزرار على لوحة، وكل حقول الإدخال على لوحة ثانية، وكل النوافذ المنبثقة على ثالثة.</p>\n<p>في Northwind أجريته مع مصمّمَي منتجات خلال أربعة أيام، على تطبيق الـ dispatch والموقع التسويقي. استخدمنا ملف Figma مشتركًا بصفحة لكل فئة: الأزرار، حقول الإدخال، القوائم المنسدلة، الأيقونات، الألوان، الطباعة، البطاقات، النوافذ المنبثقة، الجداول، الحالات الفارغة، و«أخرى». وحملت كل لقطة رابط مصدرها حتى يتحقّق منها أيّ أحد.</p>\n<p>كانت النتيجة أكثر شيء مقنع صنعته في حياتي. أحد عشر شكلًا للأزرار جنبًا إلى جنب تُنهي أي نقاش حول وجود مشكلة اتّساق. لا أحد يجادل أمام جدار من الأزرار بدرجات أزرق تكاد تكون متطابقة.</p>\n<aside class=\"callout callout-tip\" 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=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>حدّد له وقتًا</span></p>\n<p>أعطِ الجرد ميزانية ثابتة، مثل ثلاثة إلى خمسة أيام. تريد تغطية كافية لإظهار الأنماط، لا متحفًا لكل بكسل. توقّف حين لم تعد اللقطات الجديدة تعلّمك شيئًا جديدًا.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">تدقيق الكود</h2>\n<p>تفوت لقطات الشاشة ما لا يراه المستخدم على الشاشة التي أمامك: حالات الـ hover، وحالات الخطأ، وصفحات الإدارة التي لا يعرضها أحد في العروض التوضيحية. الكود يلتقط كل ذلك. وأسرع تدقيق عالي القيمة هو إحصاء قيم الألوان الخام في ملفات الأنماط.</p>\n<p>الفخّ الأول هو الصيغة. <code dir=\"ltr\">#FFF</code> و<code dir=\"ltr\">#ffffff</code> و<code dir=\"ltr\">#FfFfFf</code> لون واحد. وحّد الصيغة قبل العدّ، وإلا ستكون أرقامك خاطئة وسيلاحظ أحدهم ذلك.</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:#6E7781;--shiki-dark:#8B949E\">// A slice of Northwind's CSS, pulled from three repos.</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> css</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> `</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .btn { background: #1F5AE0; color: #FFF; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .btn:hover { background: #1b50c9; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .card { border: 1px solid #DADFE4; color: #1f2933; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .meta { color: #5f6b7a; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .footer { color: #66737F; border-top: 1px solid #dde1e6; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .title { color: #222B35; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .banner { background: #ffffff; color: #1F2933; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">  .link { color: #1f5ae0; }</span></span>\n<span class=\"line\"><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">`</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\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> expand</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">hex</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\"> h</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> hex.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">slice</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:#8250DF;--shiki-dark:#D2A8FF\">toLowerCase</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:#1F2328;--shiki-dark:#E6EDF3\"> h.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ===</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 3</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '#'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">h].</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\">c</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> c).</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">join</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 style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> '#'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> h;</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\"> counts</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\"> Map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></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:#0550AE;--shiki-dark:#79C0FF\"> match</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> of</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> css.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">match</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/#</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">[0-9a-fA-F]</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{3,6}\\b</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">/</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">g</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\"> value</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> expand</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(match);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  counts.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">set</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(value, (counts.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">get</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(value) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">??</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 1</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\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ranked</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">counts].</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">sort</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b[</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a[</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</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\">`${</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">ranked</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">length</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">} distinct colors`</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></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\">value</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">n</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\"> ranked) console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(value, n);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p>شغّله وستحصل على تسع قيم مختلفة من ثماني قواعد قصيرة. لاحظ <code dir=\"ltr\">#1f2933</code> و<code dir=\"ltr\">#222b35</code>: لونا نص لن يفرّق بينهما أحد. ولاحظ <code dir=\"ltr\">#dadfe4</code> و<code dir=\"ltr\">#dde1e6</code>: رماديّان للحدود. هذه القيم شبه المكرّرة هي الاكتشاف الحقيقي. لم يختر أحد تسعة ألوان؛ بل اختار عدّة أشخاص لونًا واحدًا كلٌّ على حدة.</p>\n<p>في كود حقيقي تطبّق الفكرة نفسها على كل ملف <code dir=\"ltr\">.css</code> و<code dir=\"ltr\">.scss</code> وكل styled-component، وتضيف تحليل <code dir=\"ltr\">rgb()</code> و<code dir=\"ltr\">hsl()</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>الموقع التسويقي وأداة الإدارة وقوالب البريد الإلكتروني هي حيث يختبئ الانحراف، لأنها تنال أقل اهتمام من المصمّمين. في Northwind كان الموقع التسويقي وحده مسؤولًا عن 23 من درجاتنا الرمادية الأربع والستين. إن كان النظام سيخدم واجهة ما، فدقّق تلك الواجهة.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">من الأرقام إلى الأولويات</h2>\n<p>الأرقام الخام لا تخبرك بما تفعل. ما يخبرك رقمان لكل عنصر:</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>العنصر</th>\n<th>عدد الشاشات التي تستخدمه</th>\n<th>عدد النسخ المختلفة</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>الزر</td>\n<td>140</td>\n<td>11</td>\n</tr>\n<tr>\n<td>حقل النص</td>\n<td>96</td>\n<td>6</td>\n</tr>\n<tr>\n<td>الرمادي (بأي استخدام)</td>\n<td>في كل مكان</td>\n<td>64</td>\n</tr>\n<tr>\n<td>منتقي التاريخ</td>\n<td>3</td>\n<td>2</td>\n</tr>\n<tr>\n<td>جدول البيانات</td>\n<td>31</td>\n<td>5</td>\n</tr>\n</tbody>\n</table></div><p>يجب أن يملك النظام أولًا ما هو <strong>شائع وغير متّسق</strong>. الأزرار وحقول الإدخال والرماديات تفوز؛ ومنتقي التاريخ يمكنه الانتظار، رغم أنه الأكثر إيلامًا في البناء. المكوّن المستخدم في ثلاث شاشات مشكلة محلية. أما الرمادي المستخدم في كل شاشة فمشكلة الجميع.</p>\n<p>ثم أضف التكلفة. لكل عنصر عالي الأولوية، اسأل فريق الهندسة كم تنفيذًا منفصلًا يصونون. كان في كود Northwind أحد عشر مكوّن زر، لكلٍّ منها خلل خاص في نمط التركيز (focus). جملة «نحن نصلح الخطأ نفسه إحدى عشرة مرة» هي التي جعلت الإدارة توافق على التوظيف. كثيرًا ما يقدّم المصمّمون الجرد كحجّة عن الجودة؛ أما القادة فيموّلونه كحجّة عن التكلفة.</p>\n<aside class=\"callout callout-why\" 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\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">لماذا يهمّ هذا</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>لماذا يهمّ هذا</span></p>\n<p>الجرد هو أيضًا خطّ الأساس الذي تقيس منه. بعد ستة أشهر تعيد تشغيل سكربت الألوان نفسه وتُعلن: «من 64 رماديًا إلى 9». رقم «قبل وبعد» هذا هو طريقتك لإثبات أن النظام يعمل، وهو موضوع الدرس الأخير في هذه الدورة.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">ما الذي تسلّمه</h2>\n<p>الجرد الجيد ينتهي بثلاثة مخرجات: لوحات Figma، وجدول بيانات تدقيق الكود، وصفحة واحدة بقائمة مرتّبة لما سيملكه النظام في أول ربعين من السنة. احتفظ باللوحات؛ ستعيد استخدامها لعرض التقدّم. في التمرين التالي تنفّذ أول عملية توحيد بنفسك: تحوّل سبع درجات رمادي شبه متطابقة إلى ثلاث قيم لها أسماء. وبعدها تحدّد المبادئ وشكل الفريق اللذين يمنعان هذه القرارات من الانحراف مرة أخرى.</p>\n","toc":[{"id":"sec-1","text":"جرد لقطات الشاشة","level":2},{"id":"sec-2","text":"تدقيق الكود","level":2},{"id":"sec-3","text":"من الأرقام إلى الأولويات","level":2},{"id":"sec-4","text":"ما الذي تسلّمه","level":2}],"takeaways":["جرد الواجهة يجمع كل نسخة من كل عنصر في الواجهة جنبًا إلى جنب، فيصبح التكرار حقيقة لا يمكن الجدال فيها.","دقّق الكود كما تدقّق الشاشات؛ فالسكربت الذي يحصي قيم الألوان يكشف انحرافًا تخفيه لقطات الشاشة.","وحّد صيغة القيم قبل العدّ، وإلا ظهر <code dir=\"ltr\">#FFF</code> و<code dir=\"ltr\">#ffffff</code> كأنهما قراران مختلفان.","ابدأ بالعناصر الشائعة وغير المتّسقة في آن واحد؛ فهي التي تعطي أكبر عائد حين يملكها النظام."],"further":[{"title":"CSS color values (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/color_value"},{"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\">#FFF</code> و<code dir=\"ltr\">#ffffff</code> و<code dir=\"ltr\">rgb(255, 255, 255)</code> كثلاث قيم منفصلة. ماذا تفعل قبل عرض الأرقام؟</p>","options":[{"text":"أوحّد كل القيم في صيغة واحدة، ثم أعيد العدّ.","why":"صحيح. إنها القرار نفسه مكتوبًا بثلاث طرق؛ وعدّها منفصلة يضخّم الانحراف ويهزّ الثقة في تدقيقك."},{"text":"أعرضها كثلاث قيم، لأن المهندسين كتبوها بطرق مختلفة.","why":"التدقيق يقيس قرارات التصميم لا طريقة الكتابة؛ وعرض ثلاث درجات أبيض يجعل أرقامك سهلة الرفض."},{"text":"أحذف قيمة <code dir=\"ltr\">rgb()</code> من الكود أولًا.","why":"تعديل الكود قبل انتهاء التدقيق يخلط التنظيف بالقياس؛ وحّد الصيغة داخل السكربت بدلًا من ذلك."}],"answer":0},{"q":"<p>يُظهر الجرد منتقيَي تاريخ مستخدمَين في 3 شاشات، و11 شكلًا للأزرار مستخدمة في 140 شاشة. من أين يجب أن يبدأ Northwind UI؟</p>","options":[{"text":"من منتقيات التاريخ، لأنها أصعب مكوّن في البناء.","why":"الصعوبة لا تجعل الشيء عاجلًا؛ ثلاث شاشات مساحة صغيرة وتكلفتها المشتركة قليلة."},{"text":"من الأزرار، لأنها شائعة وغير متّسقة معًا.","why":"صحيح. حيث يجتمع الاستخدام الكثير مع التباين الكبير، يوفّر التوحيد أكبر قدر من الوقت ويصلح أوضح تناقض."},{"text":"من الاثنين معًا، ليُطلَق النظام مكتملًا.","why":"توزيع فريق صغير على كل شيء يؤخّر المكسب الأعلى قيمة؛ النظام يكسب الثقة حين يعالج الألم الشائع أولًا."},{"text":"من لا شيء منهما؛ يجب أن يبدأ النظام بالتوثيق.","why":"توثيق بلا شيء يوثّقه لا يعطي الفرق شيئًا تتبنّاه."}],"answer":1},{"q":"<p>يقول مدير منتج إن لقطات الجرد «مجرّد تدقيق مبالغ فيه من المصمّمين». أيّ إضافة تجعل رفض التدقيق أصعب ما يمكن؟</p>","options":[{"text":"لوحة إلهام (mood board) تُظهر شكل المنتج بعد إعادة التصميم.","why":"الرؤية مفيدة لاحقًا، لكنها تُقرأ كذوق شخصي، وهو بالضبط الاعتراض الذي تردّ عليه."},{"text":"قائمة أطول بكل تناقض وجدته.","why":"مزيد من الأمثلة من النوع نفسه نادرًا ما يغيّر رأي المتشكّك؛ التكلفة هي التي تغيّره."},{"text":"وقت الهندسة المصروف على صيانة كل نسخة مكرّرة، مثل 11 تنفيذًا للأزرار لكلٍّ منها إصلاحات أخطاء منفصلة.","why":"صحيح. تحويل التكرار إلى ساعات وأخطاء يجعل الشكوى الجمالية تكلفةً تدفعها الشركة أصلًا."}],"answer":2}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<article class=\"card\">\n  <h3 class=\"card-title\">Shipment NW-4471</h3>\n  <p class=\"card-meta\">Rotterdam to Hamburg · 2 pallets</p>\n  <p class=\"card-status\">In transit</p>\n  <footer class=\"card-footer\">Updated 4 min ago</footer>\n</article>","css":"/* Seven greys found in the audit. Collapse them into three tokens. */\n.card {\n  max-width: 320px;\n  padding: 16px;\n  border: 1px solid #dadfe4;\n  border-radius: 8px;\n}\n.card-title { margin: 0 0 4px; color: #1f2933; }\n.card-meta { margin: 0 0 12px; color: #5f6b7a; }\n.card-status {\n  margin: 0;\n  padding-top: 12px;\n  color: #222b35;\n  border-top: 1px solid #d7dce1;\n}\n.card-footer {\n  margin-top: 12px;\n  padding-top: 8px;\n  font-size: 14px;\n  color: #66737f;\n  border-top: 1px solid #dde1e6;\n}\n","js":""},"solution":{"html":"<article class=\"card\">\n  <h3 class=\"card-title\">Shipment NW-4471</h3>\n  <p class=\"card-meta\">Rotterdam to Hamburg · 2 pallets</p>\n  <p class=\"card-status\">In transit</p>\n  <footer class=\"card-footer\">Updated 4 min ago</footer>\n</article>","css":":root {\n  --nw-gray-900: #1f2933;\n  --nw-gray-600: #616e7c;\n  --nw-gray-200: #d9dee3;\n}\n.card {\n  max-width: 320px;\n  padding: 16px;\n  border: 1px solid var(--nw-gray-200);\n  border-radius: 8px;\n}\n.card-title { margin: 0 0 4px; color: var(--nw-gray-900); }\n.card-meta { margin: 0 0 12px; color: var(--nw-gray-600); }\n.card-status {\n  margin: 0;\n  padding-top: 12px;\n  color: var(--nw-gray-900);\n  border-top: 1px solid var(--nw-gray-200);\n}\n.card-footer {\n  margin-top: 12px;\n  padding-top: 8px;\n  font-size: 14px;\n  color: var(--nw-gray-600);\n  border-top: 1px solid var(--nw-gray-200);\n}\n","js":""},"checks":[{"id":"root-tokens","test":"const rules = [...document.styleSheets].flatMap((s) => [...s.cssRules]).filter((r) => r.selectorText); const root = rules.filter((r) => r.selectorText.trim() === ':root'); const names = new Set(); for (const r of root) for (const p of r.style) if (p.startsWith('--')) names.add(p); return names.size >= 3;"},{"id":"no-raw-colors","test":"const rules = [...document.styleSheets].flatMap((s) => [...s.cssRules]).filter((r) => r.selectorText); const own = rules.filter((r) => /card/.test(r.selectorText)); return own.length > 0 && own.every((r) => !/#[0-9a-f]{3,8}\\b|rgba?\\(|hsla?\\(/i.test(r.style.cssText));"},{"id":"three-greys","test":"const used = new Set(); for (const el of [$('.card'), ...$$('.card *')]) { const cs = getComputedStyle(el); if (el !== $('.card')) used.add(cs.color); if (parseFloat(cs.borderTopWidth) > 0) used.add(cs.borderTopColor); } return used.size <= 3;"},{"id":"hierarchy","test":"const lum = (c) => { const [r, g, b] = c.match(/[\\d.]+/g).map(Number); return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; const t = lum(style('.card-title', 'color')); return t < lum(style('.card-meta', 'color')) && t < lum(style('.card-footer', 'color')) && lum(style('.card-meta', 'color')) < lum(style('.card-status', 'border-top-color'));"}],"title":"اختصر سبع درجات رمادي في ثلاثة tokens","prompt":"<p>بطاقة الشحنة هذه شريحة من تدقيق Northwind: سبع درجات رمادي مكتوبة يدويًا هي في الحقيقة ثلاثة قرارات. عرّف <strong>ثلاث خصائص مخصّصة رمادية على الأقل</strong> (custom properties) على <code dir=\"ltr\">:root</code> (مثل <code dir=\"ltr\">--nw-gray-900</code> و<code dir=\"ltr\">--nw-gray-600</code> و<code dir=\"ltr\">--nw-gray-200</code>)، ثم أعد كتابة كل قواعد <code dir=\"ltr\">.card</code> لتستخدمها عبر <code dir=\"ltr\">var()</code>، دون أن تبقى فيها أي قيمة hex أو <code dir=\"ltr\">rgb()</code> أو <code dir=\"ltr\">hsl()</code>. حافظ على التدرّج: العنوان والحالة بأغمق رمادي، ونص التفاصيل والتذييل برمادي متوسط، وكل الحدود بأفتح رمادي.</p>\n","hints":["جمّع القيم السبع حسب وظيفتها: نص داكن، ونص ثانوي، وحدود.","اختر قيمة واحدة لكل مجموعة، مثل ‎#1f2933 و‎#616e7c و‎#d9dee3، وعرّفها في قاعدة <code dir=\"ltr\">:root { … }</code>.","استبدل كل لون في قواعد <code dir=\"ltr\">.card</code> بـ <code dir=\"ltr\">var(--nw-gray-900)</code> أو <code dir=\"ltr\">var(--nw-gray-600)</code> أو <code dir=\"ltr\">var(--nw-gray-200)</code>."],"explanation":"<p>تنكمش القيم السبع إلى ثلاث لأنها لم تعبّر يومًا إلا عن ثلاث نوايا: نص أساسي، ونص ثانوي، وفواصل. وتعريفها مرة واحدة على <code dir=\"ltr\">:root</code> يحوّلها إلى primitives يستطيع باقي النظام الإشارة إليها. في المرة القادمة التي يحتاج فيها أحد رماديًا للحدود سيختار اسمًا بدل أن يلتقط قيمة hex بأداة القطّارة، وهكذا بالضبط ظهرت الدرجات الأربع والستون أصلًا. في القسم التالي تضيف طبقة semantic فوق هذه الـ primitives.</p>\n","checkLabels":{"root-tokens":"يعرّف <code dir=\"ltr\">:root</code> ثلاث خصائص مخصّصة على الأقل","no-raw-colors":"لا تحتوي قواعد <code dir=\"ltr\">.card</code> على ألوان مكتوبة يدويًا بصيغة hex أو rgb()‎ أو hsl()‎","three-greys":"تستخدم البطاقة ثلاث درجات رمادي على الأكثر للنصوص والحدود","hierarchy":"العنوان أغمق من نص التفاصيل والتذييل، وهما أغمق من الحدود"}},"words":650,"readMinutes":4}