@extend والوراثة
فهم @extend والوراثة في SASS
توجيه @extend هو أحد أقوى ميزات SASS لمشاركة قواعد CSS بين المحددات. بدلاً من تكرار الأنماط عبر محددات متعددة أو إنشاء قوائم محددات مفصولة بفواصل يدوياً، يتيح لك @extend وراثة الخصائص من محدد إلى آخر، مما ينتج عنه أوراق أنماط أكثر كفاءة وقابلية للصيانة.
ما يفعله @extend
يخبر @extend محدد SASS بأن محدداً واحداً يجب أن يرث جميع أنماط محدد آخر. عندما يقوم SASS بتجميع الكود الخاص بك، فإنه يجمع المحددات معاً في مخرجات CSS، بدلاً من تكرار الخصائص. هذا ينتج عنه ملفات CSS أصغر وأكثر كفاءة.
مثال أساسي على @extend
// SASS
.message {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.success {
@extend .message;
border-color: #28a745;
background-color: #d4edda;
color: #155724;
}
.error {
@extend .message;
border-color: #dc3545;
background-color: #f8d7da;
color: #721c24;
}
.warning {
@extend .message;
border-color: #ffc107;
background-color: #fff3cd;
color: #856404;
}
مخرجات CSS المجمعة
/* لاحظ كيف يتم تجميع المحددات معاً */
.message, .success, .error, .warning {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.success {
border-color: #28a745;
background-color: #d4edda;
color: #155724;
}
.error {
border-color: #dc3545;
background-color: #f8d7da;
color: #721c24;
}
.warning {
border-color: #ffc107;
background-color: #fff3cd;
color: #856404;
}
صيغة @extend
الصيغة لـ @extend بسيطة. ما عليك سوى استخدام @extend متبوعاً بالمحدد الذي تريد الوراثة منه:
أنماط صيغة @extend
// توسيع فئة
.child {
@extend .parent;
}
// توسيع محددات متعددة
.child {
@extend .parent;
@extend .another-parent;
}
// توسيع محددات متداخلة
.container {
.parent {
font-size: 16px;
}
.child {
@extend .parent;
color: blue;
}
}
// توسيع مع محددات معقدة
.button {
padding: 10px 20px;
&:hover {
opacity: 0.8;
}
}
.submit-button {
@extend .button;
background: green;
color: white;
}
محددات العنصر النائب (%)
واحدة من أقوى ميزات @extend هي القدرة على استخدام محددات العناصر النائبة. هذه هي المحددات التي تبدأ بـ % بدلاً من . أو #. لا تظهر محددات العناصر النائبة في CSS المجمع إلا إذا تم توسيعها، مما يجعلها مثالية لإنشاء أنماط قابلة لإعادة الاستخدام دون إنشاء CSS غير ضروري.
مثال على محدد العنصر النائب
// تعريف عنصر نائب (لن يظهر في CSS بمفرده)
%button-base {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
}
// توسيع العنصر النائب
.primary-button {
@extend %button-base;
background-color: #007bff;
color: white;
&:hover {
background-color: #0056b3;
}
}
.secondary-button {
@extend %button-base;
background-color: #6c757d;
color: white;
&:hover {
background-color: #545b62;
}
}
.outline-button {
@extend %button-base;
background-color: transparent;
border: 2px solid #007bff;
color: #007bff;
&:hover {
background-color: #007bff;
color: white;
}
}
CSS المجمع (لا يوجد %button-base في المخرجات)
.primary-button, .secondary-button, .outline-button {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
text-align: center;
text-decoration: none;
cursor: pointer;
transition: all 0.3s ease;
}
.primary-button:hover, .secondary-button:hover, .outline-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.primary-button {
background-color: #007bff;
color: white;
}
.primary-button:hover {
background-color: #0056b3;
}
.secondary-button {
background-color: #6c757d;
color: white;
}
.secondary-button:hover {
background-color: #545b62;
}
.outline-button {
background-color: transparent;
border: 2px solid #007bff;
color: #007bff;
}
.outline-button:hover {
background-color: #007bff;
color: white;
}
تسلسل التوسيعات
يسمح لك SASS بتسلسل التوسيعات، مما يعني أن المحدد يمكن أن يوسع محدداً آخر يوسع بدوره محدداً آخر. يتعامل SASS مع جميع علاقات الوراثة ويجمعها بشكل صحيح.
مثال على تسلسل @extend
%typography-base {
font-family: 'Inter', sans-serif;
line-height: 1.6;
color: #333;
}
%heading-base {
@extend %typography-base;
font-weight: 700;
margin-bottom: 1rem;
letter-spacing: -0.02em;
}
%large-heading {
@extend %heading-base;
font-size: 2.5rem;
line-height: 1.2;
}
h1 {
@extend %large-heading;
margin-top: 2rem;
}
.hero-title {
@extend %large-heading;
color: #007bff;
text-transform: uppercase;
}
%small-heading {
@extend %heading-base;
font-size: 1.25rem;
font-weight: 600;
}
h3 {
@extend %small-heading;
}
.section-subtitle {
@extend %small-heading;
color: #6c757d;
}
قيود @extend
بينما @extend قوي، لديه بعض القيود المهمة التي تحتاج إلى معرفتها:
1. لا يمكن التوسيع عبر استعلامات الوسائط
واحدة من أهم القيود هي أنه لا يمكنك توسيع محدد من خارج استعلام وسائط إلى استعلام وسائط، أو العكس. هذا لأن استعلامات الوسائط تنشئ سياقات منفصلة في CSS.
قيد استعلام الوسائط (هذا لن يعمل)
// هذا سيسبب خطأ
.button {
padding: 10px;
background: blue;
}
@media (min-width: 768px) {
.large-button {
@extend .button; // خطأ: لا يمكن التوسيع عبر استعلامات الوسائط
font-size: 18px;
}
}
// النهج الصحيح: التعريف ضمن نفس السياق
.button {
padding: 10px;
background: blue;
}
@media (min-width: 768px) {
.button {
font-size: 16px;
}
.large-button {
@extend .button; // هذا يعمل
font-size: 18px;
}
}
2. يمكن أن ينشئ سلاسل محددات معقدة
عند توسيع المحددات المتداخلة، ينشئ SASS جميع التركيبات الممكنة، مما قد يؤدي إلى محددات غير متوقعة ومعقدة للغاية في المخرجات.
مثال على محدد معقد
// SASS
.container {
.item {
padding: 10px;
}
}
.sidebar {
.special {
@extend .item;
color: red;
}
}
// CSS المجمع (ينشئ محددات غير متوقعة)
.container .item, .container .sidebar .special, .sidebar .container .special {
padding: 10px;
}
.sidebar .special {
color: red;
}
@extend مقابل الـ Mixins: متى تستخدم كل منهما
كل من @extend والـ mixins تسمح لك بإعادة استخدام الأنماط، لكنهما يعملان بشكل مختلف ومناسبان لحالات استخدام مختلفة:
@extend - يشارك المحددات
%card {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.product-card {
@extend %card;
background: white;
}
.user-card {
@extend %card;
background: #f8f9fa;
}
// المخرجات: يتم تجميع المحددات
.product-card, .user-card {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
Mixin - يكرر الخصائص
@mixin card {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.product-card {
@include card;
background: white;
}
.user-card {
@include card;
background: #f8f9fa;
}
// المخرجات: يتم تكرار الخصائص
.product-card {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background: white;
}
.user-card {
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background: #f8f9fa;
}
دليل القرار:
- استخدم @extend عندما:
- لديك مجموعة من الأنماط المشتركة حقاً عبر محددات متعددة
- لا تحتاج الأنماط إلى وسائط أو تخصيص
- تعمل ضمن سياق استعلام وسائط نفسه
- تريد مخرجات CSS أصغر (محددات مجمعة)
- استخدم الـ mixins عندما:
- تحتاج إلى تمرير وسائط أو تخصيص المخرجات
- تعمل عبر استعلامات وسائط مختلفة
- تحتاج إلى منطق شرطي أو حلقات
- تريد تجنب سلاسل المحددات المعقدة
- تقوم بإنشاء بادئات موردي أو أنماط CSS معقدة
الجدل حول @extend
بينما يبدو @extend فعالاً للوهلة الأولى، فقد أثار نقاشاً في مجتمع CSS. إليك المخاوف الرئيسية:
1. ترتيب محددات غير قابل للتنبؤ
يعتمد ترتيب المحددات في CSS المجمع على المكان الذي تم فيه تعريف المحدد الأصلي، وليس المكان الذي تم فيه توسيعه. هذا يمكن أن يؤدي إلى مشاكل خصوصية غير متوقعة.
2. انفجار المحددات
توسيع المحددات المتداخلة أو المعقدة يمكن أن يخلق انفجاراً تجميعياً للمحددات في المخرجات، والعديد منها لن يتطابق أبداً مع أي عناصر.
3. ضغط Gzip
بينما ينشئ @extend CSS أصغر غير مضغوط، فإن ضغط gzip الحديث فعال جداً في ضغط الأنماط المتكررة (مثل تلك التي تم إنشاؤها بواسطة الـ mixins)، لذا فإن ميزة حجم الملف غالباً ما تكون ضئيلة بعد الضغط.
4. تحديات الصيانة
عندما يتم توسيع محدد في أماكن عديدة، فإن تغيير المحدد الأساسي يمكن أن يكون له تأثيرات بعيدة المدى يصعب تتبعها.
أفضل الممارسات لـ @extend
// ✅ جيد: استخدام محددات العناصر النائبة على المستوى الجذر
%btn {
padding: 10px 20px;
border: none;
cursor: pointer;
}
.btn-primary {
@extend %btn;
background: blue;
}
// ❌ سيء: توسيع محددات فئة متداخلة
.nav {
.item {
padding: 10px;
}
}
.sidebar {
.link {
@extend .item; // ينشئ محددات معقدة
}
}
// ✅ جيد: الحفاظ على التوسيعات بسيطة وقابلة للتنبؤ
%reset-list {
margin: 0;
padding: 0;
list-style: none;
}
.menu {
@extend %reset-list;
}
.breadcrumb {
@extend %reset-list;
}
// ❌ سيء: التوسيع عبر استعلامات الوسائط
%card {
padding: 20px;
}
@media (min-width: 768px) {
.special-card {
@extend %card; // خطأ
}
}
// ✅ جيد: استخدام الـ mixins للأنماط المستجيبة
@mixin card {
padding: 20px;
}
.special-card {
@include card;
@media (min-width: 768px) {
@include card;
padding: 30px;
}
}
مثال عملي من الواقع
بناء نظام مكونات واجهة المستخدم باستخدام @extend
// محددات عناصر نائبة أساسية
%reset {
margin: 0;
padding: 0;
box-sizing: border-box;
}
%clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
%visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
%focus-ring {
&:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
}
// أنماط أساسية للمكونات
%interactive-element {
@extend %focus-ring;
cursor: pointer;
user-select: none;
transition: all 0.2s ease;
&:hover {
opacity: 0.9;
}
&:active {
transform: scale(0.98);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// مكونات محددة
.button {
@extend %interactive-element;
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 16px;
font-weight: 600;
text-align: center;
}
.link {
@extend %interactive-element;
@extend %focus-ring;
color: #007bff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
.checkbox {
@extend %interactive-element;
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 3px;
&:checked {
background-color: #007bff;
border-color: #007bff;
}
}
// نص لقارئ الشاشة فقط
.sr-only {
@extend %visually-hidden;
}
// حاوية مع clearfix
.container {
@extend %clearfix;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
تمرين 1: نظام مكون الرسائل
أنشئ نظام مكون رسائل باستخدام @extend. حدد محدد عنصر نائب أساسي لأنماط الرسائل المشتركة، ثم أنشئ متغيرات رسالة النجاح والخطأ والتحذير والمعلومات عن طريق توسيع القاعدة. يجب أن يحتوي كل نوع رسالة على:
- لون حدود مناسب
- لون خلفية
- لون نص
- عنصر نائب لأيقونة على اليسار (استخدم ::before)
استخدم محددات العناصر النائبة للتأكد من أن الأنماط الأساسية لا تظهر في CSS المجمع إلا إذا تم توسيعها.
تمرين 2: نظام تخطيط البطاقات
قم ببناء نظام مكون بطاقة باستخدام @extend:
- أنشئ عنصر نائب %card-base مع أنماط البطاقة المشتركة (padding، border-radius، box-shadow)
- أنشئ عنصر نائب %card-header لرؤوس البطاقات
- أنشئ عنصر نائب %card-body لمحتوى البطاقة
- أنشئ عنصر نائب %card-footer لتذييلات البطاقات
- أنشئ ثلاثة متغيرات بطاقة: .product-card و .profile-card و .article-card، كل منها يوسع العناصر النائبة المناسبة ويضيف أنماطه الفريدة
تمرين 3: مقارنة @extend مقابل Mixin
أنشئ نسختين من نظام الأزرار:
- الإصدار أ: استخدم @extend مع محدد عنصر نائب لإنشاء ثلاثة متغيرات زر (primary، secondary، danger)
- الإصدار ب: استخدم mixin لإنشاء نفس متغيرات الأزرار الثلاثة
- قارن مخرجات CSS المجمعة من كلا الإصدارين. أيهما ينتج CSS أصغر؟ أيهما أكثر قابلية للصيانة؟
- أضف استعلام وسائط يغير حشوة الزر على الشاشات الأوسع من 768 بكسل. أي نهج (extend أو mixin) يعمل بشكل أفضل لهذا المتطلب؟