الوحدة 1 · الـ Cascade كما هو فعلًا
الـ Custom Properties و@property
18 دقراءة 4 دبيئة تجريب الكود
الدرس في لمحة
استخدم الـ custom properties كواجهة عامة للـ component، وافهم متى يفشل الاستبدال في var()، وسجّل خصائص ذات نوع محدّد بـ @property لتُفحص صحّتها وتتحرّك بسلاسة.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
تأتي بطاقات الجلسات في Waypoint في أربعة مسارات (Design وPerformance وAccessibility وPlatform)، ولكل مسار لونه الخاص للحدّ الجانبي والشارة وحالة المرور فوق العنوان. النسخة الأولى كانت فيها قاعدة لكل مسار لكل عنصر: اثنتا عشرة قاعدة، وثالثة عشرة تُضاف كلما أضاف فريق التسويق مسارًا.
الـ custom properties (الخصائص المخصّصة، أو «متغيّرات CSS») تحوّل ذلك إلى قاعدة واحدة لكل مسار، وبطاقة تقرأ لونها من مقبض واحد.
component بمقبض واحد
.session-card {
border-inline-start: 6px solid var(--track-color, #64748b);
}
.session-card .badge {
background: var(--track-color, #64748b);
color: white;
}
.track-design { --track-color: #db2777; }
.track-perf { --track-color: #0891b2; }<article class="session-card track-design">…</article>أمران يجعلان هذا يعمل. الأول أن الـ custom properties موروثة: ضبط --track-color على البطاقة يجعلها مرئية للشارة داخلها. والثاني أن var() تُحسم عند العنصر الذي يستخدمها، في وقت حساب القيمة، فتستبدل كل بطاقة قيمتها الخاصة. والوسيط الثاني في var() هو القيمة البديلة، ولا تُستخدم إلا حين لا تكون الخاصية مضبوطة إطلاقًا.
المكان الذي تضبط فيه الـ custom property مهم. الـ design tokens العامة (ألوان العلامة التجارية وسلّم المسافات ومجموعات الخطوط) مكانها :root، ليرثها كل عنصر. أما tokens الـ component مثل --track-color فمكانها الـ component نفسه، لتبقى محلية وتستطيع بطاقة أن تختلف عن جارتها. وتغيير خاصية على :root من JavaScript يجبر المتصفّح على إعادة حساب الأنماط للشجرة كلها التي تستخدمها، لذا فإن ما يتغيّر كثيرًا، مثل موضع المؤشّر، اضبطه على أصغر عنصر يحتاج إليه.
فكّر في --track-color على أنها الواجهة العامة (API) للبطاقة. أيّ شخص يستطيع تغيير ثيم البطاقة من الخارج، في قالب CMS أو صفحة حملة لمرة واحدة، دون أن يعرف تفاصيلها الداخلية.
حين يسوء الاستبدال
الـ custom properties غير المسجّلة مجرد سلاسل من الرموز. المتصفّح لا يعرف أن --track-color يُفترض أن تكون لونًا، لذا فإن --track-color: 12px تصريح صالح تمامًا. المشكلة تظهر لاحقًا، حين تُسقط var() القيمة 12px في لون border-inline-start.
وهكذا أطلقت Waypoint بطاقة بلا حدّ للمسار إطلاقًا وبشارة شفّافة: كتب الـ CMS القيمة 12px في حقل اللون، ولم يعترض شيء.
سجّل الخاصية بـ @property
تخبر @property المتصفّح بماهية الـ custom property:
@property --track-color {
syntax: "<color>";
inherits: true;
initial-value: #64748b;
}الآن تتغيّر ثلاثة أشياء:
- فحص الصحة. القيمة التي ليست
<color>تجعل الخاصية تسقط إلى بديل آمن: تتصرّف كأنها غير مضبوطة، فتأخذ الخاصية الموروثة قيمة العنصر الأب، وتنتهي السلسلة عندinitial-valueإن لم يضبطها أحد. فتحصل البطاقة المعطوبة على حدّ رمادي مائل إلى الزرقة بدل ألّا يكون لها حدّ. - القيم المحسوبة لها نوع.
getComputedStyle(card).getPropertyValue('--track-color')تُرجعrgb(219, 39, 119)، لا النص الخام الذي كتبته. - الانتقال التدريجي. المتصفّح يعرف كيف يمزج بين لونين، فيمكن للخاصية أن تدخل في transition.
يقبل الواصف syntax أنواعًا مثل <color> و<length> و<length-percentage> و<number> و<percentage> و<angle> و<time> و<integer>، إضافةً إلى "*" لأيّ شيء. وinitial-value مطلوبة ما لم تكن الصيغة "*"، ويجب أن تكون مستقلة حسابيًا: 10px مقبولة، أما 2em فلا.
تحريك ما كان يقفز
هذا تأثير مرور لا يمكن تحقيقه إلا بالتسجيل. الحافة العلوية للبطاقة تدرّج لوني زاويته custom property:
@property --sheen-angle {
syntax: "<angle>";
inherits: false;
initial-value: 90deg;
}
.session-card {
background: linear-gradient(var(--sheen-angle), #f5f3ff, white 60%);
transition: --sheen-angle 400ms ease;
}
.session-card:hover {
--sheen-angle: 160deg;
}دون @property لا يستطيع المتصفّح الانتقال تدريجيًا بين سلاسل رموز، فيقفز التدرّج من زاوية إلى أخرى. ومعها تتحرّك الزاوية بسلاسة. وinherits: false هي الصواب هنا: الزاوية تخصّ خلفية هذه البطاقة، ولا ينبغي أن تصل إلى العناصر الأبناء.
دورك الآن
يعيد التمرين إنتاج خطأ الـ CMS: قيمة --track-color في إحدى البطاقات هي 12px. سجّل الخاصية لتسقط القيم الخاطئة إلى الرمادي المائل إلى الزرقة (slate)، وتأكّد من أن ألوان المسارات الحقيقية ما زالت تظهر. ويواصل الدرس التالي العمل على هذه البطاقات بالـ nesting الأصلي و:has()، الذي يتيح للبطاقة أن تتفاعل مع ما بداخلها.
أهم النقاط
- الـ custom properties موروثة افتراضيًا وتُستبدل حيث تُستخدم
var()، لذا فإن ضبط واحدة منها على العنصر الأب يغيّر ثيم كل عنصر داخله يقرؤها. - إذا أنتجت
var()قيمة غير صالحة تصبح الخاصية غير صالحة في وقت حساب القيمة؛ فيستخدم المتصفّح القيمة الموروثة أو الابتدائية، لا تصريحك السابق. - الـ custom property غير المسجّلة سلسلة من الرموز بلا نوع، لذا لا يستطيع المتصفّح فحص صحّتها ولا الانتقال بين قيمها تدريجيًا في transition.
@propertyتعطي الـ custom property صيغةً وقيمةً ابتدائية وعلامةً للوراثة، فتسقط القيم الخاطئة إلى بديل آمن، وتصبح الخاصية قابلة للتحريك.