{"id":"l-css-1-4","courseId":"css-mastery","lang":"ar","kind":"lesson","summary":"استخدم الـ custom properties كواجهة عامة للـ component، وافهم متى يفشل الاستبدال في var()، وسجّل خصائص ذات نوع محدّد بـ @property لتُفحص صحّتها وتتحرّك بسلاسة.","html":"<p>تأتي بطاقات الجلسات في Waypoint في أربعة مسارات (Design وPerformance وAccessibility وPlatform)، ولكل مسار لونه الخاص للحدّ الجانبي والشارة وحالة المرور فوق العنوان. النسخة الأولى كانت فيها قاعدة لكل مسار لكل عنصر: اثنتا عشرة قاعدة، وثالثة عشرة تُضاف كلما أضاف فريق التسويق مسارًا.</p>\n<p>الـ custom properties (الخصائص المخصّصة، أو «متغيّرات CSS») تحوّل ذلك إلى قاعدة واحدة لكل مسار، وبطاقة تقرأ لونها من مقبض واحد.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">component بمقبض واحد</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">session-card.css</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:#0550AE;--shiki-dark:#79C0FF\">.session-card</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  border-inline-start</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">6</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">px</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> solid</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">--track-color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#64748b</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:#0550AE;--shiki-dark:#79C0FF\">.session-card</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> .badge</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">--track-color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#64748b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">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>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.track-design</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#953800;--shiki-dark:#FFA657\">--track-color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#db2777</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.track-perf</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">   { </span><span style=\"color:#953800;--shiki-dark:#FFA657\">--track-color</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#0891b2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">; }</span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">HTML</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:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"session-card track-design\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>…&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>أمران يجعلان هذا يعمل. الأول أن الـ custom properties <strong>موروثة</strong>: ضبط <code dir=\"ltr\">--track-color</code> على البطاقة يجعلها مرئية للشارة داخلها. والثاني أن <code dir=\"ltr\">var()</code> تُحسم <strong>عند العنصر الذي يستخدمها</strong>، في وقت حساب القيمة، فتستبدل كل بطاقة قيمتها الخاصة. والوسيط الثاني في <code dir=\"ltr\">var()</code> هو القيمة البديلة، ولا تُستخدم إلا حين لا تكون الخاصية مضبوطة إطلاقًا.</p>\n<p>المكان الذي تضبط فيه الـ custom property مهم. الـ design tokens العامة (ألوان العلامة التجارية وسلّم المسافات ومجموعات الخطوط) مكانها <code dir=\"ltr\">:root</code>، ليرثها كل عنصر. أما tokens الـ component مثل <code dir=\"ltr\">--track-color</code> فمكانها الـ component نفسه، لتبقى محلية وتستطيع بطاقة أن تختلف عن جارتها. وتغيير خاصية على <code dir=\"ltr\">:root</code> من JavaScript يجبر المتصفّح على إعادة حساب الأنماط للشجرة كلها التي تستخدمها، لذا فإن ما يتغيّر كثيرًا، مثل موضع المؤشّر، اضبطه على أصغر عنصر يحتاج إليه.</p>\n<p>فكّر في <code dir=\"ltr\">--track-color</code> على أنها الواجهة العامة (API) للبطاقة. أيّ شخص يستطيع تغيير ثيم البطاقة من الخارج، في قالب CMS أو صفحة حملة لمرة واحدة، دون أن يعرف تفاصيلها الداخلية.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">حين يسوء الاستبدال</h2>\n<p>الـ custom properties غير المسجّلة مجرد سلاسل من الرموز. المتصفّح لا يعرف أن <code dir=\"ltr\">--track-color</code> يُفترض أن تكون لونًا، لذا فإن <code dir=\"ltr\">--track-color: 12px</code> تصريح صالح تمامًا. المشكلة تظهر لاحقًا، حين تُسقط <code dir=\"ltr\">var()</code> القيمة <code dir=\"ltr\">12px</code> في لون <code dir=\"ltr\">border-inline-start</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>توقّع أن ترجع CSS إلى التصريح السابق</span></p>\n<p>مع خطأ إملائي عادي مثل <code dir=\"ltr\">color: bleu</code>، يتخلّص المتصفّح من التصريح وقت التحليل ويبقى التصريح السابق منطبقًا. أما مع <code dir=\"ltr\">var()</code> فيكون الـ cascade قد اختار التصريح الفائز قبل أن تُعرف القيمة. وحين يُنتج الاستبدال قيمة بلا معنى، تصبح الخاصية <strong>غير صالحة في وقت حساب القيمة</strong> وتأخذ قيمتها الموروثة أو الابتدائية، لا قيمة قاعدتك السابقة. والأمر أسوأ مع الخصائص المختصرة (shorthands): <code dir=\"ltr\">border-inline-start: 6px solid 12px</code> غير صالحة بأكملها، فيُعاد ضبط السُّمك والنمط واللون جميعًا، والنمط الابتدائي هو <code dir=\"ltr\">none</code>.</p>\n</aside>\n<p>وهكذا أطلقت Waypoint بطاقة بلا حدّ للمسار إطلاقًا وبشارة شفّافة: كتب الـ CMS القيمة <code dir=\"ltr\">12px</code> في حقل اللون، ولم يعترض شيء.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">سجّل الخاصية بـ @property</h2>\n<p>تخبر <code dir=\"ltr\">@property</code> المتصفّح بماهية الـ custom property:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">CSS</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\">@property</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> --track-color {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  syntax</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"&#x3C;color>\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  inherits</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: true;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  initial-value</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#64748b</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>الآن تتغيّر ثلاثة أشياء:</p>\n<ol>\n<li><strong>فحص الصحة.</strong> القيمة التي ليست <code dir=\"ltr\">&lt;color&gt;</code> تجعل الخاصية تسقط إلى بديل آمن: تتصرّف كأنها غير مضبوطة، فتأخذ الخاصية الموروثة قيمة العنصر الأب، وتنتهي السلسلة عند <code dir=\"ltr\">initial-value</code> إن لم يضبطها أحد. فتحصل البطاقة المعطوبة على حدّ رمادي مائل إلى الزرقة بدل ألّا يكون لها حدّ.</li>\n<li><strong>القيم المحسوبة لها نوع.</strong> <code dir=\"ltr\">getComputedStyle(card).getPropertyValue('--track-color')</code> تُرجع <code dir=\"ltr\">rgb(219, 39, 119)</code>، لا النص الخام الذي كتبته.</li>\n<li><strong>الانتقال التدريجي.</strong> المتصفّح يعرف كيف يمزج بين لونين، فيمكن للخاصية أن تدخل في transition.</li>\n</ol>\n<p>يقبل الواصف <code dir=\"ltr\">syntax</code> أنواعًا مثل <code dir=\"ltr\">&lt;color&gt;</code> و<code dir=\"ltr\">&lt;length&gt;</code> و<code dir=\"ltr\">&lt;length-percentage&gt;</code> و<code dir=\"ltr\">&lt;number&gt;</code> و<code dir=\"ltr\">&lt;percentage&gt;</code> و<code dir=\"ltr\">&lt;angle&gt;</code> و<code dir=\"ltr\">&lt;time&gt;</code> و<code dir=\"ltr\">&lt;integer&gt;</code>، إضافةً إلى <code dir=\"ltr\">&quot;*&quot;</code> لأيّ شيء. و<code dir=\"ltr\">initial-value</code> مطلوبة ما لم تكن الصيغة <code dir=\"ltr\">&quot;*&quot;</code>، ويجب أن تكون مستقلة حسابيًا: <code dir=\"ltr\">10px</code> مقبولة، أما <code dir=\"ltr\">2em</code> فلا.</p>\n<h2 id=\"sec-4\" tabindex=\"-1\">تحريك ما كان يقفز</h2>\n<p>هذا تأثير مرور لا يمكن تحقيقه إلا بالتسجيل. الحافة العلوية للبطاقة تدرّج لوني زاويته custom property:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">CSS</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\">@property</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> --sheen-angle {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  syntax</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"&#x3C;angle>\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  inherits</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: false;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  initial-value</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">90</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">deg</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:#0550AE;--shiki-dark:#79C0FF\">.session-card</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  background</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">linear-gradient</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">--sheen-angle</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">#f5f3ff</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">white</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 60</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:#0550AE;--shiki-dark:#79C0FF\">  transition</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: --sheen-angle </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">400</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">ms</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> ease</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:#0550AE;--shiki-dark:#79C0FF\">.session-card:hover</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">  --sheen-angle</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">160</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">deg</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>دون <code dir=\"ltr\">@property</code> لا يستطيع المتصفّح الانتقال تدريجيًا بين سلاسل رموز، فيقفز التدرّج من زاوية إلى أخرى. ومعها تتحرّك الزاوية بسلاسة. و<code dir=\"ltr\">inherits: false</code> هي الصواب هنا: الزاوية تخصّ خلفية هذه البطاقة، ولا ينبغي أن تصل إلى العناصر الأبناء.</p>\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>الدعم</span></p>\n<p><code dir=\"ltr\">@property</code> ضمن Baseline 2024 (متاحة حديثًا): يدعمها Chrome وEdge وSafari وFirefox، وهذا الأخير منذ الإصدار 128 في يوليو 2024. في المتصفّحات الأقدم تظل الخاصية تعمل كخاصية غير مسجّلة، لذا تعامل مع التسجيل على أنه تحسين تدريجي (progressive enhancement). ويمكنك أيضًا التسجيل من JavaScript بـ <code dir=\"ltr\">CSS.registerProperty({ name, syntax, inherits, initialValue })</code>.</p>\n</aside>\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>سمِّ الـ tokens بدورها لا بقيمتها</span></p>\n<p><code dir=\"ltr\">--track-color</code> تنجو من إعادة تصميم الهوية؛ أما <code dir=\"ltr\">--pink-600</code> فلا. سمِّ الـ custom properties التي يقرؤها الـ component بحسب ما تفعله، واربطها بقيم لوحة الألوان الخام في مكان واحد.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">دورك الآن</h2>\n<p>يعيد التمرين إنتاج خطأ الـ CMS: قيمة <code dir=\"ltr\">--track-color</code> في إحدى البطاقات هي <code dir=\"ltr\">12px</code>. سجّل الخاصية لتسقط القيم الخاطئة إلى الرمادي المائل إلى الزرقة (slate)، وتأكّد من أن ألوان المسارات الحقيقية ما زالت تظهر. ويواصل الدرس التالي العمل على هذه البطاقات بالـ nesting الأصلي و<code dir=\"ltr\">:has()</code>، الذي يتيح للبطاقة أن تتفاعل مع ما بداخلها.</p>\n","toc":[{"id":"sec-1","text":"component بمقبض واحد","level":2},{"id":"sec-2","text":"حين يسوء الاستبدال","level":2},{"id":"sec-3","text":"سجّل الخاصية بـ @property","level":2},{"id":"sec-4","text":"تحريك ما كان يقفز","level":2},{"id":"sec-5","text":"دورك الآن","level":2}],"takeaways":["الـ custom properties موروثة افتراضيًا وتُستبدل حيث تُستخدم <code dir=\"ltr\">var()</code>، لذا فإن ضبط واحدة منها على العنصر الأب يغيّر ثيم كل عنصر داخله يقرؤها.","إذا أنتجت <code dir=\"ltr\">var()</code> قيمة غير صالحة تصبح الخاصية غير صالحة في وقت حساب القيمة؛ فيستخدم المتصفّح القيمة الموروثة أو الابتدائية، لا تصريحك السابق.","الـ custom property غير المسجّلة سلسلة من الرموز بلا نوع، لذا لا يستطيع المتصفّح فحص صحّتها ولا الانتقال بين قيمها تدريجيًا في transition.","<code dir=\"ltr\">@property</code> تعطي الـ custom property صيغةً وقيمةً ابتدائية وعلامةً للوراثة، فتسقط القيم الخاطئة إلى بديل آمن، وتصبح الخاصية قابلة للتحريك."],"further":[{"title":"Using CSS custom properties (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Cascading_variables/Using_custom_properties"},{"title":"@property (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@property"},{"title":"@property: giving superpowers to CSS variables (web.dev)","url":"https://web.dev/articles/at-property"}],"quiz":[{"q":"<p>ما الـ padding الذي يأخذه <code dir=\"ltr\">.card</code>؟</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">CSS</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:#0550AE;--shiki-dark:#79C0FF\">.card</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">  --space</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">blue</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  padding</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">24</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">px</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  padding</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">--space</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","options":[{"text":"24px، لأن المتصفّح يرجع إلى التصريح الصالح السابق.","why":"الرجوع إلى التصريح السابق لا يحدث إلا مع القيم غير الصالحة وقت التحليل. أما <code dir=\"ltr\">var()</code> فتُفحص لاحقًا، بعد أن يكون الـ cascade قد اختار التصريح الأخير."},{"text":"0، القيمة الابتدائية لـ padding.","why":"صحيح. <code dir=\"ltr\">padding: var(--space)</code> يفوز في الـ cascade، ثم يتبيّن أنه غير صالح وقت حساب القيمة، فيرجع الـ padding إلى قيمته الابتدائية."},{"text":"يتجاهل المتصفّح <code dir=\"ltr\">--space</code> ويستخدم القيمة البديلة في <code dir=\"ltr\">var()</code>، وهي فارغة.","why":"لا توجد قيمة بديلة في <code dir=\"ltr\">var(--space)</code>، والقيم البديلة لا تُستخدم إلا حين تكون الخاصية غير موجودة (غير صالحة حتمًا)، لا حين تكون قيمتها من نوع خاطئ."}],"answer":1},{"q":"<p>سجّلت <code dir=\"ltr\">@property --angle { syntax: '&lt;angle&gt;'; inherits: false; initial-value: 0deg; }</code>. ما الذي يعطيك إيّاه هذا ولا تعطيه <code dir=\"ltr\">--angle</code> غير المسجّلة؟</p>","options":[{"text":"القدرة على استخدام <code dir=\"ltr\">--angle</code> داخل <code dir=\"ltr\">var()</code>.","why":"الـ custom properties غير المسجّلة تعمل داخل <code dir=\"ltr\">var()</code> أيضًا؛ التسجيل ليس شرطًا للاستبدال."},{"text":"رسمًا أسرع، لأن الخصائص ذات النوع تتخطّى الـ cascade.","why":"الخصائص المسجّلة تمرّ عبر الـ cascade كغيرها؛ التسجيل يغيّر التحليل وفحص الصحة والانتقال التدريجي، لا تكلفة الـ cascade."},{"text":"transitions وحركات سلسة لـ <code dir=\"ltr\">--angle</code>، لأن المتصفّح يعرف كيف ينتقل بين الزوايا تدريجيًا.","why":"صحيح. الخاصية بلا نوع لا تتغيّر إلا قفزًا؛ أما ذات النوع فتنتقل تدريجيًا، فيمكن لتدرّج لوني يقوده <code dir=\"ltr\">--angle</code> أن يدور بسلاسة."}],"answer":2},{"q":"<p>أين يجب ضبط لون التمييز في بطاقة جلسة بحيث تستخدم بطاقة <code dir=\"ltr\">.track-design</code> وكل ما بداخلها اللون الوردي؟</p>","options":[{"text":"<code dir=\"ltr\">.track-design { --accent: #db2777; }</code>، مع قواعد البطاقة التي تقرأ <code dir=\"ltr\">var(--accent)</code>.","why":"صحيح. الـ custom properties موروثة، فتلتقط البطاقة وعنوانها وشارتها وحدّها القيمة كلها."},{"text":"<code dir=\"ltr\">:root { --accent: #db2777; }</code> داخل media query خاص بـ <code dir=\"ltr\">.track-design</code>.","why":"الـ media queries تختبر الـ viewport لا العنصر، و<code dir=\"ltr\">:root</code> سيعيد تلوين كل بطاقة في الصفحة."},{"text":"على كل عنصر ابن بمفرده، لأن الـ custom properties لا تُورَث.","why":"الـ custom properties غير المسجّلة موروثة دائمًا، والمسجّلة موروثة ما لم تضبط <code dir=\"ltr\">inherits: false</code>."}],"answer":0},{"q":"<p>خاصية مسجّلة لها <code dir=\"ltr\">syntax: '&lt;color&gt;'; inherits: true; initial-value: gray</code>، وبطاقة تضبط <code dir=\"ltr\">--accent: 12px</code>، والعنصر الأب لا يضبط <code dir=\"ltr\">--accent</code>. ما القيمة التي تنتهي إليها البطاقة؟</p>","options":[{"text":"12px، لأن الـ custom properties تقبل أيّ رمز.","why":"هذا صحيح فقط في الخصائص غير المسجّلة. التسجيل يجعل المتصفّح يفحص القيمة مقابل <code dir=\"ltr\">&lt;color&gt;</code>."},{"text":"gray، موروثة من العنصر الأب الذي يحمل القيمة الابتدائية.","why":"صحيح. القيمة غير الصالحة تجعل الخاصية تتصرّف كأنها غير مضبوطة، وهذا يعني في الخاصية الموروثة أن ترث، والعنصر الأب يحمل القيمة الابتدائية."},{"text":"لا شيء؛ تُسقَط قاعدة البطاقة كلها.","why":"التصريح غير الصالح وحده هو المتأثّر، ويتعافى المتصفّح بمعاملته كأنه غير مضبوط."}],"answer":1}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<section class=\"schedule\">\n  <article class=\"session-card track-design\" id=\"s1\">\n    <h2>Designing in oklch()</h2>\n    <span class=\"badge\">Design</span>\n  </article>\n  <article class=\"session-card track-perf\" id=\"s2\">\n    <h2>The Cost of a Frame</h2>\n    <span class=\"badge\">Performance</span>\n  </article>\n  <article class=\"session-card track-cms\" id=\"s3\">\n    <h2>Lightning Talks</h2>\n    <span class=\"badge\">Misc</span>\n  </article>\n</section>","css":"/* Register --track-color here */\n\n.session-card {\n  color: #1f2937;\n  margin-block-end: 12px;\n  padding: 12px 16px;\n  border-inline-start: 6px solid var(--track-color, #64748b);\n}\n.session-card h2 { margin: 0 0 8px; font-size: 1.1rem; }\n.badge {\n  display: inline-block;\n  padding: 0.1em 0.6em;\n  border-radius: 999px;\n  color: white;\n  background: var(--track-color, #64748b);\n}\n\n.track-design { --track-color: #db2777; }\n.track-perf { --track-color: #0891b2; }\n/* Written by the CMS: someone typed a size into the color field */\n.track-cms { --track-color: 12px; }\n"},"solution":{"html":"<section class=\"schedule\">\n  <article class=\"session-card track-design\" id=\"s1\">\n    <h2>Designing in oklch()</h2>\n    <span class=\"badge\">Design</span>\n  </article>\n  <article class=\"session-card track-perf\" id=\"s2\">\n    <h2>The Cost of a Frame</h2>\n    <span class=\"badge\">Performance</span>\n  </article>\n  <article class=\"session-card track-cms\" id=\"s3\">\n    <h2>Lightning Talks</h2>\n    <span class=\"badge\">Misc</span>\n  </article>\n</section>","css":"@property --track-color {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: #64748b;\n}\n\n.session-card {\n  color: #1f2937;\n  margin-block-end: 12px;\n  padding: 12px 16px;\n  border-inline-start: 6px solid var(--track-color, #64748b);\n}\n.session-card h2 { margin: 0 0 8px; font-size: 1.1rem; }\n.badge {\n  display: inline-block;\n  padding: 0.1em 0.6em;\n  border-radius: 999px;\n  color: white;\n  background: var(--track-color, #64748b);\n}\n\n.track-design { --track-color: #db2777; }\n.track-perf { --track-color: #0891b2; }\n/* Written by the CMS: someone typed a size into the color field */\n.track-cms { --track-color: 12px; }\n"},"checks":[{"id":"design-pink","test":"return style('#s1', 'border-left-color') === 'rgb(219, 39, 119)' && style('#s1 .badge', 'background-color') === 'rgb(219, 39, 119)';"},{"id":"perf-cyan","test":"return style('#s2', 'border-left-color') === 'rgb(8, 145, 178)';"},{"id":"bad-value-falls-back","test":"return style('#s3', 'border-left-color') === 'rgb(100, 116, 139)' && style('#s3 .badge', 'background-color') === 'rgb(100, 116, 139)';"},{"id":"registered-color","test":"return style('#s1', '--track-color').trim() === 'rgb(219, 39, 119)';"}],"title":"سجّل لون المسار","prompt":"<p>كتب الـ CMS القيمة <code dir=\"ltr\">12px</code> في <code dir=\"ltr\">--track-color</code> الخاصة ببطاقة <strong>Lightning Talks</strong>، ففقد حدّها وشارتها لونهما. سجّل <code dir=\"ltr\">--track-color</code> بـ <code dir=\"ltr\">@property</code> على أنها <code dir=\"ltr\">&lt;color&gt;</code> <strong>موروثة</strong> وقيمتها الابتدائية <code dir=\"ltr\">#64748b</code> (رمادي مائل إلى الزرقة). يجب أن تحتفظ بطاقتا Design (<code dir=\"ltr\">#db2777</code>) وPerformance (<code dir=\"ltr\">#0891b2</code>) بلونيهما، وأن تسقط البطاقة المعطوبة إلى الرمادي المائل إلى الزرقة.</p>\n","hints":["تأخذ <code dir=\"ltr\">@property</code> ثلاثة واصفات: <code dir=\"ltr\">syntax</code> و<code dir=\"ltr\">inherits</code> و<code dir=\"ltr\">initial-value</code>.","صيغة اللون هي السلسلة <code dir=\"ltr\">&quot;&lt;color&gt;&quot;</code> مع علامتي التنصيص.","@property --track-color { syntax: “<color>”; inherits: true; initial-value: #64748b; }"],"explanation":"<p>بمجرد تسجيل <code dir=\"ltr\">--track-color</code> على أنها <code dir=\"ltr\">&lt;color&gt;</code>، يفحص المتصفّح كل قيمة مقابل هذا النوع. القيمة <code dir=\"ltr\">12px</code> تفشل، فتتصرّف الخاصية في بطاقة Lightning Talks كأنها غير مضبوطة؛ ولأنها موروثة تأخذ قيمة العنصر الأب، والعنصر الأب لا يحمل إلا القيمة الابتدائية <code dir=\"ltr\">#64748b</code>. ألوان المسارات الصالحة تمرّ وتُخزَّن الآن كألوان حقيقية، ولهذا تُرجع <code dir=\"ltr\">getComputedStyle</code> القيمة <code dir=\"ltr\">rgb(219, 39, 119)</code> بدل النص <code dir=\"ltr\">#db2777</code>. ودون التسجيل كانت <code dir=\"ltr\">12px</code> تُستبدل داخل الخاصية المختصرة <code dir=\"ltr\">border-inline-start</code>، فيصبح التصريح كله غير صالح في وقت حساب القيمة؛ وتعود كل خاصية فرعية إلى قيمتها الابتدائية، ومنها <code dir=\"ltr\">border-style: none</code>، فيختفي الحدّ.</p>\n","checkLabels":{"design-pink":"بطاقة Design وردية","perf-cyan":"بطاقة Performance سماوية (cyan)","bad-value-falls-back":"البطاقة ذات القيمة الخاطئة تسقط إلى الرمادي المائل إلى الزرقة","registered-color":"–track-color مسجّلة على أنها لون"}},"words":701,"readMinutes":4}