Kodax

التتالي والأولوية والوراثة — تصميم CSS

التتالي والأولوية والوراثة

أكثر سؤال يطرحه متعلمو CSS: "لماذا لا يعمل تنسيقي؟" الجواب غالباً: قاعدة أخرى تنطبق على نفس العنصر وتغلب. حرف C في CSS هو Cascading (التتالي): خوارزمية تقرر أي قاعدة تفوز عند التعارض.

مراحل القرار (بالترتيب)

عندما تحاول قاعدتان ضبط نفس الخاصية لنفس العنصر:

1. الأهمية والمصدر: !important وتنسيق المستخدم/المتصفح (نادراً ما يهمك).

2. الطبقات @layer إن استخدمتها.

3. الأولوية (Specificity): المحدد الأكثر تحديداً يفوز.

4. ترتيب الظهور: عند التساوي في كل ما سبق، القاعدة الأخيرة تفوز.

الأولوية: كيف تُحسب؟

تُحسب كثلاثة أعداد (أ,ب,ج):

| العمود | ما يُعدّ فيه |

|---|---|

| أ | المعرفات #id |

| ب | الأصناف .class، والخصائص [type="text"]، والحالات :hover |

| ج | العناصر p، والعناصر الوهمية ::before |

| المحدد | الأولوية |

|---|---|

| p | (0,0,1) |

| .card | (0,1,0) |

| .card p | (0,1,1) |

| nav .menu a:hover | (0,2,2) |

| #header | (1,0,0) |

| #header .logo | (1,1,0) |

المقارنة من اليسار: أي عدد في عمود "أ" يغلب أي عدد من الأصناف. #x يغلب .a.b.c.d.e.f.g.h.i.j.k! والمحدد العام * أولويته صفر.

style="" على العنصر يغلب كل المحددات، و !important يغلب حتى style.

مثال: لماذا الزر ليس أحمر؟

#sidebar a { color: blue; }      /* (1,0,1) */
.btn-danger { color: red; }      /* (0,1,0) — يخسر رغم أنه لاحق */

الحل الصحيح ليس !important بل تقليل أولوية الأولى: استخدم .sidebar a بدل #sidebar a. القاعدة العامة: اجعل الأولويات منخفضة ومتقاربة (صنف واحد غالباً)، فيصبح الترتيب هو الحَكَم وتبقى الأمور متوقعة.

‎:where() و :is()

:where(.card, .panel) p { margin: 0; }   /* :where أولويتها صفر — سهلة الغلبة */
:is(h1, h2, h3) a { color: inherit; }    /* :is تأخذ أولوية أقوى عنصر فيها */

!important: الملاذ الأخير

!important يقفز فوق الأولوية العادية. المشكلة: لتغلبه لاحقاً تحتاج !important آخر بأولوية أعلى — سباق تسلح. استخدامات مقبولة: أصناف مساعدة صريحة (.hidden { display: none !important; })، وقواعد الوصولية مثل تقليل الحركة.

الوراثة

بعض الخصائص تنتقل من الأب إلى الأبناء تلقائياً:

| تُورث عادة | لا تُورث |

|---|---|

| color، font-*، line-height، text-align، direction، visibility | margin، padding، border، background، width، display |

لهذا يكفي ضبط الخط واللون على body مرة واحدة. والقيم الخاصة:

button { font: inherit; }        /* الأزرار والحقول لا ترث الخط افتراضياً — أجبرها */
.reset { all: unset; }           /* إزالة كل شيء (بحذر) */
a { color: inherit; }            /* خذ لون الأب */

⚠️ أي قاعدة تنطبق مباشرة على العنصر تغلب الموروث مهما كانت أولوية قاعدة الأب. #main { color: red } لن تجعل رابطاً داخلها أحمر إذا كان للمتصفح a { color: blue } مباشرة على الرابط.

@layer: تنظيم التتالي (حديث)

@layer reset, base, components, utilities;

@layer base {
    a { color: navy; }
}
@layer components {
    .btn { color: white; }
}

الطبقات المُعلنة لاحقاً تغلب السابقة مهما كانت الأولوية داخلها. هذا يجعل تنسيقات المكتبات والأساسيات سهلة التجاوز.

كيف تشخّص في أدوات المطور

تبويب Styles يرتب القواعد من الأقوى للأضعف، ويشطب الخاسرة. مرّر المؤشر فوق المحدد لترى أولويته. تبويب Computed يعرض القيمة النهائية ومن أين جاءت.

تمرين

احسب أولوية: ul li a، .nav a:hover، #menu li.active، a[href$=".pdf"]. ثم اكتب قاعدتين متعارضتين على زر وتوقّع الفائز قبل فتح المتصفح، وتحقق في أدوات المطور.

/* افتح أدوات المطور على هذه القواعد وراقب أيها يُشطب */

/* (0,0,1) */
a {
    color: blue;
}

/* (0,1,1) — يغلب السابقة */
.nav a {
    color: darkslategray;
}

/* (0,2,1) — حالة المرور تضيف صنفاً للأولوية */
.nav a:hover {
    color: black;
}

/* (0,1,0) — يخسر أمام .nav a رغم أنه لاحق! */
.active {
    color: crimson;
}

/* (0,2,1) — الحل: أولوية مساوية أو أعلى، أو أبسط من ذلك: .nav a.active */
.nav a.active {
    color: crimson;
}

/* الوراثة: color و font تنتقل للأبناء */
body {
    color: #1f2937;
    font-family: "Tajawal", sans-serif;
    line-height: 1.7;
}

/* الأزرار لا ترث الخط افتراضياً */
button,
input,
select,
textarea {
    font: inherit;
}

/* :where أولويتها صفر — أي قاعدة لاحقة تغلبها بسهولة */
:where(.card) p {
    margin: 0;
}

/* الطبقات: components تغلب base مهما كانت الأولوية داخل base */
@layer base, components;

@layer base {
    #page .title {
        color: gray;
    }
}

@layer components {
    .title {
        color: navy;
    }
}
اختبر فهمك