CSS3 والتصميم المتجاوب

التداخل الأصلي في CSS

25 دقيقة الدرس 55 من 60

ما هو التداخل في CSS؟

التداخل في CSS هو ميزة أصلية تتيح لك كتابة قواعد الأنماط داخل قواعد أنماط أخرى، مما يعكس البنية الهرمية لـ HTML الخاص بك. بدلا من تكرار المحدد الأب مرارا وتكرارا، تكتب القواعد الفرعية مباشرة داخل الكتلة الأب. ثم يحسب المتصفح المحدد الكامل تلقائيا بدمج المحددات الخارجية والداخلية. يؤدي هذا إلى أوراق أنماط أقصر وأكثر تنظيما وأسهل في الصيانة.

لأكثر من عقد، اعتمد المطورون على معالجات CSS المسبقة مثل Sass وLESS وStylus للحصول على قدرات التداخل. كانت هذه الأدوات تجمع الصياغة المتداخلة إلى CSS مسطح قبل إرساله إلى المتصفح. مع التداخل الأصلي في CSS، يفهم المتصفح القواعد المتداخلة مباشرة -- بدون خطوة بناء، بدون معالج مسبق، بدون تجميع. تكتب CSS متداخلا وهو ببساطة يعمل. هذه واحدة من أهم الإضافات إلى لغة CSS في السنوات الأخيرة، وتغير جذريا طريقة تأليف أوراق الأنماط.

أصبح التداخل الأصلي في CSS متاحا في جميع المتصفحات الرئيسية بدءا من عام 2023. شحن Chrome 112 وFirefox 117 وSafari 16.5 جميعها الدعم، مما يجعل استخدامه آمنا في الإنتاج للغالبية العظمى من المستخدمين اليوم. في هذا الدرس، ستتعلم كل جانب من جوانب التداخل في CSS: الصياغة، ومعامل الامبرساند، والحالات الخاصة، وأفضل الممارسات، وكيفية الانتقال من تداخل المعالجات المسبقة إلى التداخل الأصلي.

نبذة تاريخية: تداخل المعالجات المسبقة مقابل التداخل الأصلي

قبل التداخل الأصلي في CSS، كانت الطريقة الوحيدة لتداخل المحددات هي من خلال المعالجات المسبقة. قدم كل من Sass (2006) وLESS (2009) التداخل كميزة أساسية. إليك كيف يبدو تداخل المعالج المسبق في Sass:

تداخل Sass (المعالج المسبق)

/* صياغة Sass / SCSS */
.card {
    padding: 1.5rem;
    background: white;
    border-radius: 8px;

    .card-header {
        font-size: 1.25rem;
        font-weight: bold;
        margin-bottom: 1rem;
    }

    .card-body {
        color: #555;
        line-height: 1.6;
    }

    &:hover {
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }

    &--featured {
        border-left: 4px solid #3498db;
    }
}

/* يجمع Sass هذا إلى CSS مسطح: */
.card { padding: 1.5rem; background: white; border-radius: 8px; }
.card .card-header { font-size: 1.25rem; font-weight: bold; margin-bottom: 1rem; }
.card .card-body { color: #555; line-height: 1.6; }
.card:hover { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); }
.card--featured { border-left: 4px solid #3498db; }

يعمل التداخل الأصلي في CSS بشكل مشابه لكن مع بعض الاختلافات الرئيسية في الصياغة. الاختلاف الأهم هو متى ولماذا تحتاج إلى & (الامبرساند) محدد التداخل. في Sass، يُحتاج إلى الامبرساند فقط عندما تريد ربط أو إلحاق شيء مباشرة بالمحدد الأب (مثل &:hover أو &--modifier). في CSS الأصلي، قواعد متى يكون & مطلوبا مختلفة قليلا، كما سنستكشف بالتفصيل.

صياغة التداخل الأساسية

الفكرة الأساسية للتداخل في CSS بسيطة: تكتب مجموعة قواعد داخل مجموعة قواعد أخرى. يتم تفسير القاعدة الداخلية كسليل للقاعدة الخارجية. لنبدأ بمثال أساسي.

أول قاعدة CSS متداخلة

/* بدون تداخل (CSS مسطح تقليدي) */
.nav { display: flex; gap: 1rem; }
.nav a { color: #333; text-decoration: none; }
.nav a:hover { color: #0066cc; }

/* مع التداخل الأصلي في CSS */
.nav {
    display: flex;
    gap: 1rem;

    a {
        color: #333;
        text-decoration: none;

        &:hover {
            color: #0066cc;
        }
    }
}

في النسخة المتداخلة، قاعدة a مكتوبة داخل .nav. يفسر المتصفح هذا كـ .nav a. و&:hover داخل كتلة a يُفسر كـ .nav a:hover. لاحظ كيف يمثل التداخل بصريا بنية HTML، مما يسهل فهم أي أنماط تنطبق على أي عناصر.

محدد التداخل & (الامبرساند)

الامبرساند & هو محدد التداخل. يمثل صراحة المحدد الأب عند النقطة التي يظهر فيها. فهم متى تستخدم & ومتى يمكنك حذفه هو الجزء الأهم في إتقان التداخل في CSS.

متى يكون & مطلوبا

يجب عليك استخدام محدد التداخل & كلما احتاج المحدد المتداخل إلى الارتباط مباشرة بالمحدد الأب بدون أي مسافة بيضاء. الحالات الأكثر شيوعا هي الأصناف الزائفة والعناصر الزائفة والمحددات المركبة.

حالات يكون فيها & مطلوبا

/* الأصناف الزائفة: & يرتبط مباشرة */
.button {
    background: #3498db;
    color: white;
    padding: 0.75rem 1.5rem;

    &:hover {
        background: #2980b9;
    }

    &:focus {
        outline: 2px solid #3498db;
        outline-offset: 2px;
    }

    &:active {
        transform: translateY(1px);
    }

    &:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }
}
/* يحسب إلى: .button:hover, .button:focus, إلخ. */

/* العناصر الزائفة: & يرتبط مباشرة */
.quote {
    position: relative;
    padding-left: 1.5rem;

    &::before {
        content: "\201C";
        position: absolute;
        left: 0;
        font-size: 2rem;
        color: #ccc;
    }

    &::after {
        content: "";
        display: block;
        margin-top: 1rem;
        border-bottom: 2px solid #eee;
    }
}
/* يحسب إلى: .quote::before, .quote::after */

/* المحددات المركبة: & يجمع الأصناف */
.card {
    background: white;

    &.highlighted {
        border: 2px solid gold;
    }

    &.card--dark {
        background: #1a1a2e;
        color: white;
    }
}
/* يحسب إلى: .card.highlighted, .card.card--dark */
ملاحظة: بدون & في &:hover، سيفسر المتصفح ذلك كـ .button :hover (بمسافة)، والذي يحدد أي سليل ممرر عليه الماوس داخل .button بدلا من الزر نفسه عند التمرير عليه. هذا محدد مختلف تماما بسلوك مختلف. يضمن & أن الصنف الزائف يرتبط مباشرة بالمحدد الأب.

متى يكون & اختياريا

عند التداخل مع محددات الأصناف أو محددات المعرفات أو محددات السمات أو المحددات الأخرى التي تبدأ برمز (.، #، [)، يكون & اختياريا. يمكن للمتصفح استنتاج أنك تقصد محدد سليل. ومع ذلك، بالنسبة لمحددات النوع (أسماء العناصر المجردة مثل div، p، span)، كانت القواعد مختلفة سابقا لكنها خُففت منذ ذلك الحين.

حالات يكون فيها & اختياريا

/* محددات الأصناف: & اختياري */
.sidebar {
    width: 300px;

    /* كلاهما متكافئ */
    .sidebar-title { font-weight: bold; }
    & .sidebar-title { font-weight: bold; }
    /* كلاهما يحسب إلى: .sidebar .sidebar-title */
}

/* محددات المعرفات: & اختياري */
.page {
    #main-content {
        max-width: 800px;
        margin: 0 auto;
    }
    /* يحسب إلى: .page #main-content */
}

/* محددات السمات: & اختياري */
.form {
    [type="text"] {
        border: 1px solid #ccc;
        padding: 0.5rem;
    }
    /* يحسب إلى: .form [type="text"] */
}

/* المحدد الشامل: & اختياري */
.container {
    * {
        box-sizing: border-box;
    }
    /* يحسب إلى: .container * */
}

التداخل مع محددات النوع

في الأصل، كان التداخل الأصلي في CSS يتطلب & عند تداخل محددات النوع (العنصر) مثل div وp وh1 أو span. كان هذا لأن محلل المتصفح لم يستطع التمييز بين اسم خاصية واسم عنصر وقت التحليل. ومع ذلك، تم تحديث المواصفة والمتصفحات تدعم الآن تداخل محددات النوع بدون &. يُسمى هذا أحيانا صياغة "التداخل المخفف".

تداخل محددات النوع

/* المتصفحات الحديثة: يمكن تداخل محددات النوع مباشرة */
.article {
    h2 {
        font-size: 1.75rem;
        color: #333;
        margin-bottom: 0.75rem;
    }

    p {
        line-height: 1.8;
        margin-bottom: 1rem;
    }

    ul {
        padding-left: 1.5rem;

        li {
            margin-bottom: 0.5rem;
        }
    }

    img {
        max-width: 100%;
        height: auto;
        border-radius: 8px;
    }
}
/* يحسب إلى: .article h2, .article p, .article ul, إلخ. */

/* يمكنك مع ذلك استخدام & صراحة إذا كنت تفضل الوضوح */
.article {
    & h2 { font-size: 1.75rem; }
    & p { line-height: 1.8; }
}
/* نفس النتيجة: .article h2, .article p */
نصيحة: حتى لو كانت محددات النوع المجردة تعمل الآن بدون &، تختار بعض الفرق دائما استخدام & للاتساق والوضوح. هذا تفضيل أسلوبي. الأهم هو أن يتفق فريقك على اتفاقية ويطبقها باستمرار في جميع أنحاء قاعدة الكود.

تداخل الأصناف الزائفة والعناصر الزائفة

أحد الاستخدامات الأكثر شيوعا للتداخل هو تجميع الأصناف الزائفة والعناصر الزائفة مع العنصر الأب. هذا يبقي جميع الحالات والزخارف لمكون ما مرئية معا، مما يجعل ورقة الأنماط أسهل بكثير للفحص والصيانة.

تداخل شامل للأصناف الزائفة والعناصر الزائفة

.input-field {
    border: 2px solid #ddd;
    padding: 0.75rem 1rem;
    border-radius: 6px;
    transition: border-color 0.2s, box-shadow 0.2s;

    &:hover {
        border-color: #999;
    }

    &:focus {
        border-color: #3498db;
        box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
        outline: none;
    }

    &:focus-visible {
        border-color: #3498db;
        box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.4);
    }

    &::placeholder {
        color: #aaa;
        font-style: italic;
    }

    &:valid {
        border-color: #2ecc71;
    }

    &:invalid:not(:placeholder-shown) {
        border-color: #e74c3c;
    }

    &:disabled {
        background: #f5f5f5;
        color: #999;
        cursor: not-allowed;
    }

    &:read-only {
        background: #fafafa;
        border-style: dashed;
    }
}

/* محددات زائفة معقدة */
.list {
    &:first-child { margin-top: 0; }
    &:last-child { margin-bottom: 0; }
    &:nth-child(odd) { background: #f9f9f9; }
    &:not(:last-child) { border-bottom: 1px solid #eee; }
    &:empty { display: none; }

    &::marker {
        color: #3498db;
        font-weight: bold;
    }
}

تداخل استعلامات الوسائط داخل المحددات

واحدة من أقوى ميزات التداخل في CSS هي القدرة على كتابة استعلامات @media مباشرة داخل كتلة المحدد. هذا يبقي جميع أنماط المكون -- بما في ذلك تنويعاته المتجاوبة -- في مكان واحد بدلا من تفريقها عبر كتل استعلامات وسائط منفصلة في أسفل الملف.

تداخل استعلامات @media داخل المحددات

/* النهج التقليدي: استعلامات الوسائط منفصلة عن المكون */
.hero {
    padding: 2rem;
    text-align: center;
}
.hero h1 {
    font-size: 2rem;
}
@media (min-width: 768px) {
    .hero {
        padding: 4rem;
    }
    .hero h1 {
        font-size: 3rem;
    }
}
@media (min-width: 1200px) {
    .hero {
        padding: 6rem;
    }
    .hero h1 {
        font-size: 4rem;
    }
}

/* النهج المتداخل: كل شيء معا */
.hero {
    padding: 2rem;
    text-align: center;

    @media (min-width: 768px) {
        padding: 4rem;
    }

    @media (min-width: 1200px) {
        padding: 6rem;
    }

    h1 {
        font-size: 2rem;

        @media (min-width: 768px) {
            font-size: 3rem;
        }

        @media (min-width: 1200px) {
            font-size: 4rem;
        }
    }
}

لاحظ كيف تحتفظ النسخة المتداخلة بجميع أنماط .hero في كتلة واحدة. يمكنك رؤية كل تنويع نقطة توقف على الفور دون التمرير عبر الملف. هذا تحسين كبير للصيانة، خاصة في أوراق الأنماط الكبيرة التي تحتوي على عشرات المكونات.

تداخل قواعد @ الأخرى

/* يمكنك تداخل قواعد @ الأخرى أيضا */
.card {
    background: white;
    color: #333;

    /* الوضع الداكن */
    @media (prefers-color-scheme: dark) {
        background: #1a1a2e;
        color: #e0e0e0;
    }

    /* تقليل الحركة */
    @media (prefers-reduced-motion: reduce) {
        transition: none;
        animation: none;
    }

    /* أنماط الطباعة */
    @media print {
        box-shadow: none;
        border: 1px solid #ccc;
    }

    /* استعلامات الحاوية (قابلة للتداخل أيضا) */
    @container sidebar (min-width: 300px) {
        flex-direction: row;
        gap: 1rem;
    }
}
نصيحة: غالبا ما يُسمى تداخل استعلامات الوسائط داخل المحددات "استعلامات وسائط محددة النطاق للمكون". يُعتبر على نطاق واسع أفضل ممارسة لتنظيم الأنماط المتجاوبة لأنه يبقي كل منطق نقاط التوقف في نفس موقع المكون الذي يؤثر عليه. كان هذا النمط أحد الأسباب الرئيسية التي جعلت المطورين يريدون التداخل الأصلي في CSS في المقام الأول.

التداخل العميق ومخاوف القراءة

مجرد أنك تستطيع التداخل بعمق لا يعني أنه يجب عليك ذلك. أحد الأخطاء الأكثر شيوعا مع التداخل في CSS -- سواء في المعالجات المسبقة أو CSS الأصلي -- هو التداخل بعمق شديد. يؤدي التداخل العميق إلى محددات مفرطة التحديد، ويجعل الكود أصعب في القراءة، وينشئ أنماطا مرتبطة بإحكام يصعب تجاوزها أو إعادة استخدامها.

مشكلة التداخل العميق

/* سيء: تداخل عميق جدا -- صعب القراءة ومفرط التحديد */
.page {
    .main-content {
        .article {
            .article-body {
                .paragraph {
                    .link {
                        color: blue;

                        &:hover {
                            color: darkblue;
                        }
                    }
                }
            }
        }
    }
}
/* ينتج: .page .main-content .article .article-body .paragraph .link */
/* هذا المحدد محدد للغاية ويكاد يكون من المستحيل تجاوزه */

/* جيد: حافظ على التداخل ضحلا -- أقصى حد 3 مستويات */
.article-body {
    .link {
        color: blue;

        &:hover {
            color: darkblue;
        }
    }
}
تحذير: المبدأ التوجيهي المقبول على نطاق واسع هو تحديد التداخل بأقصى حد 3 مستويات عمقا. بعد ذلك، تصبح محدداتك مفرطة التحديد، وتصبح أنماطك مرتبطة بإحكام ببنية HTML، ويصبح إعادة الهيكلة مؤلما. إذا وجدت نفسك تتداخل أعمق من 3 مستويات، فهذا عادة علامة على أنه يجب عليك إعادة هيكلة محدداتك لتكون أكثر استواء أو استخدام أسماء أصناف أكثر تحديدا بدلا من الاعتماد على تسلسل DOM العميق.

إليك المشاكل العملية للتداخل العميق:

  • تحديد عال -- المحددات المتداخلة بعمق لها تحديد عال، مما يجعلها صعبة التجاوز بدون اللجوء إلى !important.
  • محددات هشة -- تنكسر الأنماط إذا غيرت بنية HTML، لأن كل مستوى تداخل يرتبط بعلاقة DOM.
  • قراءة ضعيفة -- الكود المسافة بادئة بعمق أصعب في الفحص والفهم بنظرة سريعة.
  • مخرجات ملفات كبيرة -- كل مستوى تداخل يكرر المحددات الأب، مما يؤدي إلى محددات أطول وملفات CSS أكبر.

إعادة هيكلة التداخل العميق

/* قبل: تداخل عميق */
.sidebar {
    .widget {
        .widget-header {
            .widget-title {
                font-size: 1rem;
            }
        }
        .widget-body {
            .widget-list {
                .widget-item {
                    padding: 0.5rem;
                }
            }
        }
    }
}

/* بعد: مسطح بأسماء أصناف وصفية */
.widget-title {
    font-size: 1rem;
}

.widget-item {
    padding: 0.5rem;
}

التداخل مع المجمعات

يعمل التداخل في CSS مع جميع مجمعات CSS: الطفل (>)، والأخ المجاور (+)، والأخ العام (~)، والعمود (||). تتيح لك هذه المجمعات التعبير عن علاقات DOM دقيقة ضمن قواعدك المتداخلة.

استخدام المجمعات في القواعد المتداخلة

/* مجمع الطفل */
.nav {
    > li {
        display: inline-block;
        margin-right: 1rem;

        > a {
            color: #333;
            text-decoration: none;
        }
    }
}
/* يحسب إلى: .nav > li, .nav > li > a */

/* مجمع الأخ المجاور */
.heading {
    + p {
        font-size: 1.125rem;
        color: #555;
        margin-top: 0.5rem;
    }
}
/* يحسب إلى: .heading + p */

/* مجمع الأخ العام */
.toggle-checkbox:checked {
    ~ .toggle-content {
        display: block;
        opacity: 1;
    }

    ~ .toggle-label {
        color: #3498db;
    }
}
/* يحسب إلى: .toggle-checkbox:checked ~ .toggle-content, إلخ. */

/* دمج التداخل مع المجمعات */
.form-group {
    margin-bottom: 1.5rem;

    > label {
        display: block;
        font-weight: 600;
        margin-bottom: 0.5rem;
    }

    > input {
        width: 100%;
        padding: 0.75rem;
    }

    + .form-group {
        border-top: 1px solid #eee;
        padding-top: 1.5rem;
    }
}

التحديد في القواعد المتداخلة

فهم كيف يعمل التحديد مع القواعد المتداخلة أمر بالغ الأهمية. يحسب التداخل الأصلي في CSS التحديد بدمج المحددات الخارجية والداخلية تماما كما لو كنت قد كتبتها مسطحة. يحمل محدد التداخل & تحديد المحدد الخارجي الذي يمثله.

تحديد المحددات المتداخلة

/* يتم حساب تحديد القواعد المتداخلة كالمحدد الكامل */

/* التحديد: (0, 1, 0) لـ .card */
.card {
    background: white;

    /* التحديد: (0, 1, 1) لـ .card h2 */
    h2 {
        color: #333;
    }

    /* التحديد: (0, 2, 0) لـ .card.featured */
    &.featured {
        border: 2px solid gold;
    }

    /* التحديد: (0, 2, 0) لـ .card:hover */
    &:hover {
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    }

    /* التحديد: (0, 2, 0) لـ .card .card-title */
    .card-title {
        font-size: 1.25rem;
    }
}

/* مهم: التداخل لا يضيف تحديدا إضافيا بعد
   ما سيكون عليه المحدد المسطح الكامل. هذه متكافئة: */

/* مسطح: التحديد (0, 2, 0) */
.card .card-title { font-size: 1.25rem; }

/* متداخل: أيضا التحديد (0, 2, 0) */
.card {
    .card-title { font-size: 1.25rem; }
}

/* محدد & نفسه له تحديد محدده الخارجي */
/* عندما يُستخدم & داخل :is() أو :not() أو :has()،
   يأخذ تحديد أكثر الوسائط تحديدا */
ملاحظة: هناك تفصيل دقيق للتحديد مع & عندما يكون المحدد الخارجي قائمة محددات. إذا كان المحدد الخارجي .foo, #bar، فإن & داخل قاعدة متداخلة يُعامل كـ :is(.foo, #bar)، الذي يأخذ أعلى تحديد في القائمة. في هذه الحالة، سيكون (1, 0, 0) من #bar، حتى عندما تُطبق القاعدة على العناصر المطابقة لـ .foo. هذا يطابق سلوك تحديد :is().

محدد & في مواضع مختلفة

محدد التداخل & لا يقتصر على بداية المحدد المتداخل. يمكنك وضعه في أي مكان لإنشاء علاقات محددات مختلفة. هذا مفيد للغاية لإنشاء أنماط تعتمد على السياق.

وضع & في مواضع مختلفة

/* & في البداية (الأكثر شيوعا) */
.button {
    &:hover { background: #2980b9; }
    /* .button:hover */
}

/* & في النهاية -- يعكس العلاقة */
.button {
    .dark-theme & {
        background: #444;
        color: white;
    }
    /* .dark-theme .button */
}

/* هذا مفيد بشكل لا يصدق للثيمات */
.card {
    background: white;
    color: #333;

    .theme-dark & {
        background: #2d2d2d;
        color: #f0f0f0;
    }

    .theme-high-contrast & {
        border: 2px solid black;
        color: black;
        background: white;
    }

    [dir="rtl"] & {
        text-align: right;
    }
}
/* ينتج:
   .theme-dark .card { ... }
   .theme-high-contrast .card { ... }
   [dir="rtl"] .card { ... } */

/* & في المنتصف */
.icon {
    width: 24px;
    height: 24px;

    .button & {
        width: 16px;
        height: 16px;
    }
    /* .button .icon */
}

/* & متعدد في محدد واحد */
.item {
    & + & {
        margin-top: 1rem;
    }
    /* .item + .item */
}

الانتقال من Sass إلى التداخل الأصلي في CSS

إذا كان مشروعك يستخدم حاليا Sass أو LESS للتداخل، فإن الانتقال إلى التداخل الأصلي في CSS بسيط في معظم الحالات. ومع ذلك، هناك بعض الاختلافات التي تحتاج إلى معرفتها.

الاختلافات الرئيسية بين Sass والتداخل الأصلي

/* الاختلاف 1: ربط السلاسل النصية مع & لا يعمل */

/* Sass: & يربط السلاسل النصية */
.block {
    &__element { color: red; }    /* .block__element */
    &--modifier { color: blue; }  /* .block--modifier */
}

/* CSS الأصلي: & لا يربط السلاسل النصية */
.block {
    &__element { color: red; }    /* هذا لا يعمل كما هو متوقع! */
    /* يرى المتصفح هذا كـ: .block __element (غير صالح) */
}

/* حل CSS الأصلي لأنماط شبيهة بـ BEM */
.block {
    /* استخدم أسماء الأصناف الكاملة بدلا من ذلك */
}
.block__element { color: red; }
.block--modifier { color: blue; }

/* الاختلاف 2: @extend غير موجود في CSS الأصلي */

/* Sass: @extend */
.error { color: red; }
.critical-error {
    @extend .error;
    font-weight: bold;
}

/* CSS الأصلي: لا يوجد @extend -- استخدم تركيب الأصناف في HTML بدلا من ذلك */

/* الاختلاف 3: المتغيرات */

/* Sass: $variables */
$primary: #3498db;
.button { color: $primary; }

/* CSS الأصلي: الخصائص المخصصة */
.button { color: var(--primary); }

/* الاختلاف 4: تحديد التداخل الأصلي في CSS قد يختلف قليلا */
/* ينتج Sass محددات مسطحة، بينما قد يستخدم التداخل الأصلي :is() داخليا */
تحذير: أكبر مشكلة عند الانتقال من Sass إلى CSS الأصلي هي أن & لا يدعم ربط السلاسل النصية. في Sass، &__element يربط المحدد الأب مع __element لينتج .block__element. في CSS الأصلي، & هو محدد يمثل الأب، وليس سلسلة نصية يتم ربطها. هذا يعني أن تسمية نمط BEM مع & لا تعمل بنفس الطريقة في CSS الأصلي.

مثال عملي: تنسيق المكونات بالتداخل

لنبني مكونا واقعيا باستخدام التداخل الأصلي في CSS لنرى كيف ينظم الكود عمليا.

بناء مكون بطاقة تسعير بالتداخل

.pricing-card {
    background: white;
    border-radius: 12px;
    padding: 2rem;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    transition: transform 0.2s, box-shadow 0.2s;

    &:hover {
        transform: translateY(-4px);
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    }

    &.popular {
        border: 2px solid #3498db;
        position: relative;

        &::before {
            content: "الأكثر شعبية";
            position: absolute;
            top: -12px;
            left: 50%;
            transform: translateX(-50%);
            background: #3498db;
            color: white;
            padding: 0.25rem 1rem;
            border-radius: 20px;
            font-size: 0.75rem;
            font-weight: bold;
        }
    }

    header {
        text-align: center;
        padding-bottom: 1.5rem;
        border-bottom: 1px solid #eee;

        h3 {
            font-size: 1.25rem;
            color: #333;
            margin-bottom: 0.5rem;
        }

        .price {
            font-size: 2.5rem;
            font-weight: 800;
            color: #3498db;

            span {
                font-size: 1rem;
                font-weight: 400;
                color: #999;
            }
        }
    }

    ul {
        list-style: none;
        padding: 1.5rem 0;

        li {
            padding: 0.5rem 0;
            color: #555;

            &::before {
                content: "\2713";
                color: #2ecc71;
                margin-right: 0.75rem;
                font-weight: bold;
            }

            + li {
                border-top: 1px solid #f5f5f5;
            }
        }
    }

    .cta-button {
        display: block;
        width: 100%;
        padding: 0.875rem;
        border: none;
        border-radius: 8px;
        background: #3498db;
        color: white;
        font-size: 1rem;
        font-weight: 600;
        cursor: pointer;
        transition: background 0.2s;

        &:hover {
            background: #2980b9;
        }

        &:focus-visible {
            outline: 2px solid #3498db;
            outline-offset: 2px;
        }
    }

    @media (min-width: 768px) {
        padding: 2.5rem;

        header .price {
            font-size: 3rem;
        }
    }

    @media (prefers-color-scheme: dark) {
        background: #1e1e2e;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);

        header {
            border-bottom-color: #333;

            h3 {
                color: #e0e0e0;
            }
        }

        ul li {
            color: #ccc;
            + li {
                border-top-color: #333;
            }
        }
    }
}

BEM مع التداخل الأصلي

Block Element Modifier (BEM) هي منهجية تسمية CSS شائعة. بما أن التداخل الأصلي في CSS لا يدعم ربط السلاسل النصية مع &، فأنت تحتاج نهجا مختلفا عما اعتاد عليه مطورو Sass.

أنماط BEM مع التداخل الأصلي في CSS

/* النهج 1: تداخل العناصر داخل الكتلة */
.card {
    background: white;
    border-radius: 8px;
    overflow: hidden;

    .card__header {
        padding: 1rem 1.5rem;
        border-bottom: 1px solid #eee;
    }

    .card__title {
        font-size: 1.25rem;
        font-weight: bold;
    }

    .card__body {
        padding: 1.5rem;
    }

    .card__footer {
        padding: 1rem 1.5rem;
        background: #f9f9f9;
    }
}

/* النهج 2: كتل مسطحة مع معدلات وحالات متداخلة */
.card {
    background: white;
}

.card__header {
    padding: 1rem 1.5rem;
}

.card__body {
    padding: 1.5rem;

    /* تداخل المعدلات داخل العناصر */
    &--compact {
        /* تنبيه: هذا لا ينشئ .card__body--compact */
        /* ينشئ .card__body --compact (بمسافة) وهو غير صالح */
    }
}

/* النهج 3: النهج الموصى به لـ BEM + التداخل الأصلي */
/* حافظ على الكتل والعناصر مسطحة، استخدم التداخل للحالات والاستجابة */
.card {
    background: white;

    &:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); }

    @media (min-width: 768px) {
        display: grid;
        grid-template-columns: 1fr 2fr;
    }
}

.card__header {
    padding: 1rem 1.5rem;

    &:first-child { padding-top: 1.5rem; }
}

.card--featured {
    border: 2px solid gold;
    background: #fffdf0;
}

دعم المتصفحات

التداخل الأصلي في CSS مدعوم في جميع المتصفحات الحديثة دائمة التحديث. إليك الجدول الزمني للدعم:

  • Chrome / Edge -- الإصدار 112+ (أبريل 2023). التداخل المخفف (محددات النوع المجردة) من Chrome 120+.
  • Firefox -- الإصدار 117+ (أغسطس 2023). التداخل المخفف من Firefox 120+.
  • Safari -- الإصدار 16.5+ (مايو 2023). التداخل المخفف من Safari 17.2+.

اعتبارا من أوائل 2025، يحظى التداخل في CSS بدعم عالمي للمتصفحات يزيد عن 90%. بالنسبة للنسبة الصغيرة من المستخدمين على المتصفحات القديمة، سيتم تجاهل القواعد المتداخلة ببساطة -- لن تُطبق الأنماط، لكن لن ينكسر شيء. يمكنك استخدام @supports لتوفير بدائل إذا لزم الأمر.

التحسين التدريجي مع @supports

/* البديل: CSS مسطح للمتصفحات القديمة */
.nav a { color: #333; }
.nav a:hover { color: #0066cc; }

/* المحسن: CSS متداخل للمتصفحات الحديثة */
@supports selector(&) {
    .nav {
        a {
            color: #333;

            &:hover {
                color: #0066cc;
            }
        }
    }
}

/* عمليا، عادة لا تحتاج لفحص @supports
   لأن CSS المسطح يوفر خط أساس والتداخل
   يتجاوزه فقط بنفس النتيجة */

أفضل الممارسات والأنماط المضادة

بعد العمل مع التداخل في CSS في مشاريع حقيقية، ظهرت عدة أفضل ممارسات. اتباع هذه سيبقي كودك المتداخل نظيفا وأداءه جيدا وسهل الصيانة.

أفضل الممارسات

أفضل ممارسات التداخل في CSS

/* 1. حافظ على التداخل ضحلا -- أقصى حد 3 مستويات */
.card {
    header {
        h3 { /* هذا المستوى 3 -- توقف هنا */ }
    }
}

/* 2. استخدم التداخل لنطاق المكون */
.search-form {
    display: flex;
    gap: 0.5rem;

    input {
        flex: 1;
        padding: 0.75rem;
    }

    button {
        padding: 0.75rem 1.5rem;
        background: #3498db;
        color: white;
    }
}

/* 3. تداخل استعلامات الوسائط داخل المكونات */
.sidebar {
    width: 100%;

    @media (min-width: 768px) {
        width: 300px;
        position: sticky;
        top: 2rem;
    }
}

/* 4. جمع الأصناف الزائفة والعناصر الزائفة مع عنصرها */
.link {
    color: #333;
    text-decoration: none;

    &:hover { color: #0066cc; }
    &:focus-visible { outline: 2px solid #0066cc; }
    &:active { color: #004499; }
    &::after { content: " \2192"; }
}

/* 5. استخدم & للأنماط المعتمدة على السياق */
.icon {
    width: 24px;
    height: 24px;

    .button & { width: 18px; height: 18px; }
    .heading & { width: 28px; height: 28px; }
}

الأنماط المضادة التي يجب تجنبها

أخطاء شائعة في التداخل CSS

/* النمط المضاد 1: تداخل كل شيء */
/* لا تتداخل القواعد لمجرد أنك تستطيع */
body {
    main {
        .content {
            .article {
                p {
                    a {
                        color: blue; /* 6 مستويات عمقا -- رهيب! */
                    }
                }
            }
        }
    }
}

/* النمط المضاد 2: تأهيل المحددات بشكل مفرط */
.card {
    .card-header {
        .card-title {
            /* .card .card-header .card-title محدد بشكل مفرط */
            /* .card-title وحده سيعمل بشكل جيد */
        }
    }
}

/* النمط المضاد 3: توقع ربط السلاسل النصية كما في Sass */
.block {
    &__element { } /* لا ينشئ .block__element */
    &--modifier { } /* لا ينشئ .block--modifier */
}

/* النمط المضاد 4: تداخل محددات غير مرتبطة معا */
.button {
    background: blue;

    /* هذا المحدد غير المرتبط لا ينبغي أن يكون متداخلا هنا */
    .footer-links {
        display: flex;
    }
}

/* النمط المضاد 5: تكرار الخصائص عبر مستويات التداخل */
.card {
    color: #333; /* تعيين هنا */
    h2 {
        color: #333; /* زائد -- يرث من الأب */
    }
}

التمرين 1: إعادة هيكلة CSS مسطح إلى CSS متداخل

خذ CSS المسطح التالي وأعد هيكلته باستخدام التداخل الأصلي في CSS. حافظ على التداخل بأقصى حد 3 مستويات. جمع جميع الأصناف الزائفة والعناصر الزائفة مع عنصرها الأب. تداخل استعلام الوسائط داخل كتلة المكون. CSS المسطح هو: .navbar { display: flex; align-items: center; padding: 1rem 2rem; background: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }، .navbar .logo { font-size: 1.5rem; font-weight: bold; color: #333; }، .navbar .nav-links { display: flex; gap: 1.5rem; margin-left: auto; }، .navbar .nav-links a { color: #555; text-decoration: none; padding: 0.5rem 0; border-bottom: 2px solid transparent; }، .navbar .nav-links a:hover { color: #3498db; border-bottom-color: #3498db; }، .navbar .nav-links a.active { color: #3498db; font-weight: 600; border-bottom-color: #3498db; }، @media (max-width: 768px) { .navbar { flex-direction: column; padding: 1rem; } .navbar .nav-links { margin-left: 0; margin-top: 1rem; } }. تأكد من أن نسختك المتداخلة تنتج نفس المحددات المحسوبة والأنماط تماما كالنسخة المسطحة.

التمرين 2: بناء مكون بثيمات بالتداخل

أنشئ مكون .profile-card باستخدام التداخل الأصلي في CSS يدعم ثلاثة ثيمات ألوان: الافتراضي (فاتح)، و.theme-dark، و.theme-ocean. يجب أن تحتوي البطاقة على رأس بصورة رمزية واسم ودور؛ وجسم بفقرة سيرة ذاتية؛ وتذييل بروابط اجتماعية. استخدم محدد & في مواضع مختلفة: في البداية للأصناف الزائفة والمحددات المركبة، وفي النهاية لسياق الثيم (.theme-dark &)، وفي المنتصف لعلاقات الأشقاء (& + &). تداخل استعلامات @media لنقاط التوقف المتجاوبة عند 480px و768px داخل المكون. أضف قواعد :hover و:focus-visible و::before متداخلة للروابط الاجتماعية. حافظ على جميع التداخل بأقصى حد 3 مستويات. أخيرا، أضف استعلام @media (prefers-color-scheme: dark) متداخلا يطبق أنماطا داكنة عندما لا يكون هناك صنف ثيم صريح معين.