كن مطوّر واجهات أمامية محترفا
9 دقيقة قراءة

محدّدات CSS والتخصيص والتتالي: لماذا تفوز أنماطك أو تخسر

الأساس الذي تقوم عليه CSS كلها — كل نوع محدّد ومُركِّب، والأصناف الزائفة والعناصر الزائفة، وكيف يُحسَب التخصيص فعلًا، وترتيب حلّ التتالي الكامل، والوراثة، وكلمات inherit/initial/unset/revert، والهروب من !important، وتمارين عملية مع حلولها.

كل سؤال في CSS يبدأ بـ "لماذا لا يُطبَّق هذا النمط؟" يعود إلى واحد من ثلاثة: محدّدك لا يطابق، أو قاعدة أكثر تخصيصًا تفوز، أو شيء لاحق في التتالي تجاوزه. يتعلّم معظم الناس المحدّدات بنسخ المقتطفات ولا يبنون أبدًا النموذج الذي يجيب عن تلك الأسئلة بنظرة. هذا هو ذلك النموذج — حرف C في CSS، أي التتالي (cascade) الذي يقرّر أيّ القواعد المتنافسة تُرسَم فعلًا. ثبّت هذا وتتوقّف بقية CSS عن أن تبدو عشوائية. (التخطيط يعيش في نموذج الصندوق؛ وهذه هي الطبقة تحت كل ذلك.)

حين تضبط قاعدتان الخاصية نفسها على العنصر نفسه، تختار CSS الفائز بترتيب ثابت: الأهمية ← التخصيص ← ترتيب المصدر. وكل لحظة "CSS لا تعمل" تقريبًا هي واحدة من هذه الثلاثة تُحَلّ بهدوء ضدّك.

المحدّدات: كيف تستهدف العناصر

اللبنات الأساسية. كلٌّ يطابق العناصر بطريقة مختلفة:

*              { }   /* العام — كل عنصر */
p              { }   /* النوع — كل <p> */
.card          { }   /* الصنف — كل عنصر له class="card" */
#hero          { }   /* المعرّف — العنصر الذي له id="hero" */
[type="email"] { }   /* السمة — حقول من ذلك النوع */
p.card         { }   /* مركّب — <p> له أيضًا .card */
h1, h2, h3     { }   /* تجميع — يُطبَّق على الثلاثة */

محدّدات السمات أكثر مرونة ممّا يظنّ الناس:

[href]            { }  /* تملك السمة أصلًا */
[href^="https"]   { }  /* القيمة تبدأ بـ */
[href$=".pdf"]    { }  /* القيمة تنتهي بـ */
[href*="docs"]    { }  /* القيمة تحتوي على */
[lang|="en"]      { }  /* تساوي "en" أو تبدأ بـ "en-" */

المُركِّبات: العلاقات بين العناصر

المسافات والرموز بين المحدّدات تصف كيف ترتبط العناصر:

.menu a       { }  /* سليل — أي <a> داخل .menu، على أي عمق */
.menu > a     { }  /* ابن — الأبناء المباشرون فقط */
h2 + p        { }  /* شقيق مجاور — الـ <p> مباشرةً بعد <h2> */
h2 ~ p        { }  /* شقيق عام — كل <p> بعد <h2> (نفس الأب) */

الفرق بين السليل (مسافة) والابن (>) هو ما ينبغي استيعابه — السليل يصل إلى الأسفل بالكامل، والابن يتوقّف عند مستوى واحد.

الأصناف الزائفة: مطابقة الحالة والموضع

الصنف الزائف (نقطتان مفردتان) يطابق العناصر في حالة أو موضع معيّن — أشياء ليست في HTML كسمات:

a:hover, a:focus     { }  /* تفاعل المستخدم */
input:checked        { }  /* حالة النموذج */
input:disabled       { }
input:focus-visible  { }  /* حلقة تركيز لمستخدمي لوحة المفاتيح فقط */
li:first-child       { }  /* الموضع البنيوي */
li:last-child        { }
li:nth-child(odd)    { }  /* كل عنصر فردي — رائع لتخطيط الحمار الوحشي */
li:nth-child(3n)     { }  /* كل ثالث */
:not(.active)        { }  /* النفي — كل ما ليس له .active */
form:has(:invalid)   { }  /* علائقي — انظر مقال modern-css */

:nth-child() و:not() وحدهما يغنيان عن قدر هائل من إضافة الأصناف يدويًّا. (:has() و:is() و:where() تُشرَح بالكامل في CSS الحديثة.)

العناصر الزائفة: تنسيق أجزاء من العنصر

العنصر الزائف (نقطتان مزدوجتان) ينسّق جزءًا من عنصر، أو يحقن محتوى مولَّدًا:

.note::before  { content: "💡 "; }      /* حقن محتوى قبل النصّ */
.note::after   { content: ""; }          /* يُستخدم غالبًا لأشكال زخرفية */
p::first-line  { font-weight: 600; }      /* أول سطر مرسوم */
p::first-letter{ font-size: 3em; }        /* حرف استهلالي كبير */
::selection    { background: gold; }      /* النصّ المظلّل */
input::placeholder { color: gray; }       /* نصّ العنصر النائب */

::before/::after يحتاجان خاصية content كي يظهرا أصلًا (حتى لو كانت فارغة). إنهما صندوقان حقيقيان تستطيع تحجيمهما وتموضعهما.

التخصيص: كيف تُكسَر التعادلات

حين تطابق عدّة قواعد العنصر نفسه وتضبط الخاصية نفسها، يفوز الأكثر تخصيصًا. يُحسَب التخصيص في ثلاثة دلاء — تخيّله نتيجة من ثلاثة أعمدة (A, B, C):

  • A — محدّدات المعرّف (#hero)
  • B — الأصناف ومحدّدات السمات والأصناف الزائفة (.card، [type]، :hover)
  • C — محدّدات النوع والعناصر الزائفة (div، ::before)

تعدّ كم من كلٍّ يحوي المحدّد وتقارن من اليسار إلى اليمين:

#hero               /* (1,0,0) */
.card.active        /* (0,2,0) */
nav ul li a         /* (0,0,4) */
a:hover             /* (0,1,1) */
#hero .card a       /* (1,1,1) */

(1,0,0) يهزم (0,2,0) يهزم (0,0,4) — معرّف واحد يفوق أيّ عدد من الأصناف، الذي يفوق أيّ عدد من محدّدات النوع. والأعمدة لا "تُرحَّل": 11 صنفًا (0,11,0) ما زالت تخسر أمام معرّف واحد (1,0,0).

شيئان يفاجئان الناس:

*            { }  /* (0,0,0) — المحدّد العام لا يضيف شيئًا */
:where(.a.b) { }  /* (0,0,0) — :where() يساهم دائمًا بتخصيص صفر */
:is(.a, #b)  { }  /* يأخذ أكثر وسائطه تخصيصًا → (1,0,0) هنا */

سمة style="..." السطرية أعلى حتى من أي محدّد (تخيّلها عمودًا رابعًا إلى اليسار)، و!important يتفوّق على كل شيء — وهذا بالضبط سبب ألم الصيانة الذي يسبّبانه.

التتالي: ترتيب الحلّ الكامل

التخصيص هو فقط كاسر التعادل الأوسط. يقرّر المتصفح فعليًّا التصريح الفائز بهذا الترتيب، من الأعلى للأسفل:

  1. الأصل والأهمية!important للمؤلّف > عادي المؤلّف > افتراضيات المتصفح (تقريبًا). أنماط وكيل المستخدم هي الأضعف.
  2. التخصيص — نتيجة (A,B,C) أعلاه.
  3. ترتيب المصدر — إذا تعادل كل ما سبق، تفوز القاعدة الأخيرة.

تلك القاعدة الثالثة هي سبب أن إعادة ترتيب ورقة أنماطك، أو قاعدة لاحقة بتخصيص مساوٍ، قد تغيّر النتيجة:

.btn { color: blue; }
.btn { color: red; }   /* نفس التخصيص → هذه تفوز، لأنها لاحقة */

هذا هو السبب الكامل لأهمية ترتيب الروابط، ولماذا "يعمل" تجاوزك بشكل غامض بعد أن تنقله للأسفل، ولماذا تهتمّ أُطر أصناف المنفعة كثيرًا بترتيب شحن القواعد.

الوراثة: ما ينزل عبر الشجرة

بعض الخصائص تُورَّث من الأب إلى الابن تلقائيًّا؛ ومعظمها لا. الخصائص المتعلّقة بالنصّ تُورَّث (color، font-family، font-size، line-height، text-align)؛ وخصائص الصندوق والتخطيط لا (margin، padding، border، width، background):

body { color: #333; font-family: system-ui; }
/* كل سليل يأخذ ذلك اللون والخط مجّانًا — ما لم يُتجاوز */

.card { border: 1px solid; }
/* الحدّ لا يتتالى للأبناء — خصائص التخطيط لا تُورَّث */

لهذا تضبط الطباعة في الأعلى (على body) وتدعها تتدفّق للأسفل، لكن عليك ضبط الحدود والتباعد على كل عنصر يحتاجها.

inherit وinitial وunset وrevert

أربع كلمات تمنحك تحكّمًا صريحًا في قيمة أي خاصية:

.child {
  color: inherit;   /* خذ بالقوّة القيمة المحسوبة للأب */
  margin: initial;  /* أعد الضبط إلى افتراضي مواصفة الخاصية (margin → 0) */
  color: unset;     /* ورِّث إن كانت الخاصية تُورَّث عادةً، وإلا initial */
  all: revert;      /* تراجَع إلى قيمة ورقة أنماط المتصفح الافتراضية */
}

all: revert فتحة هروب مفيدة — تجرّد أنماط مؤلّفك عن عنصر وتدع افتراضيات المتصفح تظهر، دون أن تسرد كل خاصية.

الهروب من !important

!important يفوز بالقوّة الغاشمة، وحالما تستخدمه لتهزم قاعدة، يحتاج التجاوز التالي مطرقة أكبر — حرب تصاعدية. الجأ إليه نادرًا جدًّا. البدائل المنظّمة:

  • اخفض تخصيص الشيء الذي تحاربه، أو ارفع تخصيصك عمدًا — لكن بقدر الحاجة فقط.
  • استخدم @layer لتقرّر الفائزين بـ ترتيب الطبقات بدل التخصيص (مشروح في CSS الحديثة) — هذا هو الحلّ الحديث لـ "تجاوزي لا يصل".
  • استخدم :where() لكتابة أنماط أساس بتخصيص صفر يسهُل تجاوزها لاحقًا بسهولة بالغة.

العقلية الصحّية: التخصيص شيء تُبقيه منخفضًا ومسطّحًا، لا شيء تفوز به بالتسلّق.

الأخطاء الشائعة

  • افتراض أن قاعدة "لا تعمل" بينما الحقيقة أن قاعدة أكثر تخصيصًا تفوز — تفقّد التتالي في DevTools (يُظهر القواعد المتجاوَزة مشطوبة).
  • الظنّ أن مزيدًا من الأصناف يهزم معرّفًا — لا يستطيع؛ (0,99,0) ما زالت تخسر أمام (1,0,0).
  • اللجوء إلى محدّدات #id في أوراق الأنماط، ثم العجز عن تجاوزها لاحقًا — فضّل الأصناف للتنسيق.
  • استخدام !important لإصلاح مشكلة تخصيص، فتبدأ حربًا تصاعدية.
  • نسيان أنه عند تساوي التخصيص، ترتيب المصدر يقرّر — فتفوز قاعدة لاحقة بصمت.
  • توقّع أن margin/padding/border تُورَّث — لا تفعل؛ اضبطها حيث تحتاج فقط.
  • كتابة ::before/::after بلا خاصية content والتساؤل لماذا لا يظهر شيء.
  • الخلط بين السليل (.a .b) والابن (.a > .b) ومطابقة عناصر أكثر (أو أقلّ) ممّا قُصد.

تمارين

جرّب كلًّا منها قبل فتح الحل.

تمرين 1 — مَن يفوز؟

#main .btn { color: blue; }
.btn.primary { color: red; }

بمعطى <a id="x" class="btn primary"> داخل #main، ما لون النصّ؟

إظهار الحل

أزرق. #main .btn تساوي (1,1,0) (معرّف واحد، صنف واحد)؛ و.btn.primary تساوي (0,2,0) (صنفان). المعرّف في المحدّد الأول يجعله يفوز — معرّف واحد يهزم أي عدد من الأصناف، بصرف النظر عن ترتيب المصدر.

تمرين 2 — تخطيط الحمار الوحشي، بلا أصناف إضافية

أعطِ كل صفّ زوجي في جدول خلفية رمادية فاتحة، دون إضافة أي أصناف إلى HTML.

إظهار الحل
tr:nth-child(even) { background: #f4f4f4; }

:nth-child(even) يطابق الموضع البنيوي مباشرةً — بلا مسك دفاتر class="odd/even"، ويبقى صحيحًا مع إضافة الصفوف أو حذفها.

تمرين 3 — نسّق كل شيء عدا واحد

اخفت كل .tag ليست .tag--active.

إظهار الحل
.tag:not(.tag--active) { opacity: 0.5; }

:not() يعكس المطابقة. لاحظ أنه يضيف تخصيص وسيطه (صنف هنا)، فهذا يساوي (0,2,0).

تمرين 4 — اجعل تجاوزًا يصل بلا !important

إطار يشحن .btn { background: blue } وخاصّتك .btn-danger { background: red }، لكن الأزرق يبقى يفوز. لا تستطيع تغيير CSS الإطار. أصلِحها بطريقتين مختلفتين.

إظهار الحل
/* الخيار A — ارفع التخصيص عمدًا إلى (0,2,0) */
.btn.btn-danger { background: red; }

/* الخيار B — صرّح بطبقة تتالٍ لاحقة (مفضّل، يتوسّع أفضل) */
@layer framework, overrides;
@layer overrides {
  .btn-danger { background: red; }   /* الطبقة اللاحقة تفوز بصرف النظر عن التخصيص */
}

الخيار A يفوز لأن الأزرق كان على الأرجح (0,1,0)؛ ومضاعفة الصنف تجعل خاصّتك (0,2,0). الخيار B يتجنّب التخصيص كليًّا — @layer لاحقة تهزم دائمًا أسبق منها.

تمرين 5 — تنبّأ بالوراثة

body  { color: navy; }
.card { color: inherit; border: 1px solid; }

هل يصير نصّ .card كحليًّا (navy)؟ وهل يأتي لون حدّه من مكان خاص؟

إظهار الحل

النصّ كحليّcolor: inherit يأخذ صراحةً color المحسوب للأب، الذي تتالى من body. أما الحدّ، المكتوب كـ 1px solid بلا لون، فيستخدم currentColor افتراضيًّا — فهو أيضًا كحليّ، لأن border-color يرتدّ إلى color العنصر.

النموذج الذهني الذي تحتفظ به

تحلّ CSS الأنماط المتنافسة بترتيب ثابت: الأهمية ← التخصيص ← ترتيب المصدر، ثم تملأ الوراثة أيّ شيء لم تضبطه. المحدّدات تطابق (النوع، الصنف، المعرّف، السمة، المُركِّبات، الأصناف الزائفة، العناصر الزائفة)؛ والتخصيص يكسر التعادلات بنتيجة الأعمدة الثلاثة (A,B,C) حيث معرّف واحد يهزم أي كومة أصناف، التي تهزم أي كومة أنواع؛ وعند تعادل التخصيص، تفوز القاعدة الأخيرة. أبقِ التخصيص منخفضًا ومسطّحًا — فضّل الأصناف على المعرّفات، وتجنّب !important، والجأ إلى @layer و:where() حين تحتاج التحكّم في الفائزين بنيويًّا. وحالما تستطيع النظر إلى قاعدتين وقول أيّهما تُرسَم ولماذا، يقف كل موضوع آخر في CSS — التخطيط، والتنسيق، والحركة — على أرض صلبة.