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

تحسين أداء CSS

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

كيف يعالج المتصفح CSS

قبل أن تتمكن من تحسين أداء CSS، تحتاج إلى فهم كيف يعالج المتصفح CSS في المقام الأول. عندما يتلقى المتصفح مستند HTML، يبدأ بتحليل HTML لبناء DOM (نموذج كائن المستند). في الوقت نفسه، يصادف CSS من خلال وسوم <link> أو كتل <style> أو الأنماط المضمنة، ويبدأ ببناء CSSOM (نموذج كائن CSS). CSSOM هو هيكل شجري، مشابه لـ DOM، يمثل جميع قواعد CSS وقيمها المحسوبة.

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

خط أنابيب العرض

مستند HTML
    |
    v
[تحليل HTML] ---> شجرة DOM
    |
    |  (اكتشاف ملفات CSS)
    |
    v
[تنزيل CSS] ---> [تحليل CSS] ---> شجرة CSSOM
    |                                    |
    v                                    v
         [دمج DOM + CSSOM]
                |
                v
          شجرة العرض
                |
                v
           [التخطيط]  (حساب الأحجام والمواقع)
                |
                v
           [الرسم]   (ملء البكسلات)
                |
                v
         [التركيب] (تركيب الطبقات لوحدة GPU)
                |
                v
          البكسلات على الشاشة

CSS الذي يحجب العرض

بشكل افتراضي، كل ملف CSS خارجي يحجب العرض. لن يعرض المتصفح المحتوى حتى يقوم بتنزيل وتحليل جميع أوراق الأنماط المرتبطة في <head>. هذا بالتصميم -- بدون CSS، سيعرض المتصفح أولا محتوى غير منسق (ومضة المحتوى غير المنسق، أو FOUC) ثم يعيد فجأة تنسيق الصفحة، مما يخلق تجربة مستخدم سيئة.

ومع ذلك، ليس كل CSS مطلوب فوريا. ورقة أنماط تنطبق فقط على تخطيطات الطباعة، أو واحدة تنطبق فقط على عروض الشاشة الكبيرة، لا يجب أن تحجب العرض الأولي على جهاز محمول. يمكنك استخدام سمة media على وسوم <link> لإخبار المتصفح أي أوراق أنماط مهمة للسياق الحالي:

استخدام سمات الوسائط لتقليل حجب العرض

<!-- هذا يحجب العرض (افتراضي، ينطبق على الكل) -->
<link rel="stylesheet" href="main.css">

<!-- هذا يحجب العرض فقط للطباعة -->
<link rel="stylesheet" href="print.css" media="print">

<!-- هذا يحجب العرض فقط عندما يكون العرض >= 1024px -->
<link rel="stylesheet" href="desktop.css" media="(min-width: 1024px)">

<!-- هذا يحجب العرض فقط في الاتجاه العمودي -->
<link rel="stylesheet" href="portrait.css" media="(orientation: portrait)">
ملاحظة: يقوم المتصفح بتنزيل جميع أوراق الأنماط بغض النظر عن سمة الوسائط. الفرق هو أن أوراق الأنماط غير المطابقة يتم تنزيلها بأولوية منخفضة ولا تحجب العرض. هذا يعني أنه لا توجد عقوبة عرض نطاق لإضافة استعلامات وسائط إلى وسوم الارتباط -- فقط فائدة أداء العرض.

مسار CSS الحرج

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

تضمين CSS الحرج

يتم وضع CSS الحرج داخل وسم <style> في <head> المستند. هذا CSS يسافر مع استجابة HTML، لذلك يمتلكه المتصفح فورا بدون الحاجة لطلب شبكة إضافي. يجب أن يحتوي CSS الحرج فقط على الأنماط المطلوبة لتخطيط فوق الطية: الرأس، قسم البطل، التنقل، وأي محتوى مرئي بدون تمرير.

تضمين CSS الحرج

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>صفحتي</title>

    <!-- CSS الحرج المضمن للعرض الفوري -->
    <style>
        /* إعادة تعيين وأساسي */
        *, *::before, *::after { box-sizing: border-box; margin: 0; }
        body { font-family: system-ui, sans-serif; line-height: 1.6; }

        /* الرأس والتنقل */
        .header { display: flex; align-items: center;
                  justify-content: space-between; padding: 1rem 2rem;
                  background: #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.1); }
        .header__logo { font-size: 1.5rem; font-weight: 700; color: #111; }
        .header__nav { display: flex; gap: 1.5rem; list-style: none; }

        /* قسم البطل */
        .hero { padding: 4rem 2rem; text-align: center; background: #f8fafc; }
        .hero__title { font-size: 2.5rem; color: #111; margin-bottom: 1rem; }
        .hero__subtitle { font-size: 1.125rem; color: #6b7280; max-width: 600px;
                          margin: 0 auto; }
    </style>

    <!-- ورقة الأنماط الكاملة تُحمل بشكل غير متزامن -->
    <link rel="preload" href="styles.css" as="style"
          onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="styles.css"></noscript>
</head>
<body>
    <!-- محتوى الصفحة -->
</body>
</html>

التحميل الكسول لـ CSS غير الحرج

بمجرد تضمين CSS الحرج، تحتاج استراتيجية لتحميل CSS المتبقي بدون حجب الصفحة. هناك عدة تقنيات لهذا. تقنية preload الموضحة أعلاه تستخدم rel="preload" مع as="style" لتنزيل الملف بأولوية عالية لكن بدون حجب العرض. معالج onload يحوله بعد ذلك إلى ورقة أنماط عادية بمجرد التنزيل.

تقنيات التحميل الكسول لـ CSS

/* التقنية 1: preload + onload */
<link rel="preload" href="non-critical.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">

/* التقنية 2: حيلة الوسائط (وسائط غير صالحة، تبديل عند التحميل) */
<link rel="stylesheet" href="non-critical.css"
      media="print" onload="this.media='all'">

/* التقنية 3: إدراج JavaScript */
<script>
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'non-critical.css';
    document.head.appendChild(link);
</script>

/* التقنية 4: requestIdleCallback لأدنى أولوية */
<script>
    if ('requestIdleCallback' in window) {
        requestIdleCallback(function() {
            var link = document.createElement('link');
            link.rel = 'stylesheet';
            link.href = 'analytics-styles.css';
            document.head.appendChild(link);
        });
    }
</script>
نصيحة: أدوات مثل Critical وPenthouse وCritters يمكنها استخراج CSS الحرج تلقائيا من صفحاتك. في خطوط أنابيب البناء، يمكنك دمج هذه الأدوات لتوليد CSS الحرج لكل قالب صفحة. هذا يؤتمت ما سيكون عملية يدوية مملة وعرضة للخطأ.

إزالة CSS غير المستخدم

واحدة من أكثر التحسينات تأثيرا هي إزالة CSS الذي لا تستخدمه صفحاتك فعليا. إذا كنت تستخدم إطار عمل CSS مثل Bootstrap أو نظام تصميم كبير، قد تشحن آلاف القواعد التي لا يشير إليها أي عنصر في الصفحة. كل قاعدة غير مستخدمة تضيف حجم تنزيل ووقت بناء CSSOM. PurgeCSS وأدوات مشابهة تحلل قوالب HTML وJavaScript لتحديد محددات CSS المستخدمة فعليا، ثم تزيل كل شيء آخر.

استخدام PurgeCSS

/* تثبيت PurgeCSS */
npm install purgecss --save-dev

/* purgecss.config.js */
module.exports = {
    content: [
        './src/**/*.html',
        './src/**/*.js',
        './src/**/*.vue',
        './src/**/*.jsx'
    ],
    css: ['./src/css/**/*.css'],
    output: './dist/css/',

    /* قائمة آمنة للمحددات التي قد تُضاف ديناميكيا */
    safelist: [
        'is-active',
        'is-open',
        /^modal-/,       /* تعبير نمطي: احتفظ بجميع الفئات التي تبدأ بـ modal- */
        /^tooltip/       /* تعبير نمطي: احتفظ بجميع فئات التلميحات */
    ]
};

/* قبل PurgeCSS: 250KB من CSS (Bootstrap الكامل) */
/* بعد PurgeCSS: 12KB من CSS (فقط ما تستخدمه) */
/* هذا تخفيض بنسبة 95%! */

تصغير CSS

التصغير يزيل جميع الأحرف غير الضرورية من CSS بدون تغيير وظائفه: المسافات البيضاء والتعليقات والأسطر الجديدة والفواصل المنقوطة الزائدة. CSS المصغر عادة أصغر بنسبة 20-40% من الأصلي. مع ضغط gzip أو brotli، يمكن للتصغير تقليل أحجام ملفات CSS بنسبة 80-90%. كل أداة بناء حديثة تدعم تصغير CSS مباشرة.

مثال تصغير CSS

/* قبل التصغير (قابل للقراءة، 312 بايت) */
.card {
    display: flex;
    flex-direction: column;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    background-color: #ffffff;
    /* ظل البطاقة للعمق */
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    transition: box-shadow 0.3s ease;
}

.card:hover {
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

/* بعد التصغير (175 بايت، أصغر بنسبة 44%) */
.card{display:flex;flex-direction:column;border:1px solid #e5e7eb;border-radius:12px;overflow:hidden;background-color:#fff;box-shadow:0 4px 6px rgba(0,0,0,.1);transition:box-shadow .3s ease}.card:hover{box-shadow:0 8px 16px rgba(0,0,0,.15)}

/* أدوات التصغير الشائعة: */
/* - cssnano (إضافة PostCSS) */
/* - clean-css */
/* - Lightning CSS */
/* - esbuild (تصغير CSS مدمج) */

أداء المحددات

تطابق المتصفحات محددات CSS من اليمين إلى اليسار. عندما يصادف المتصفح قاعدة مثل .sidebar .nav .link، يجد أولا جميع العناصر المطابقة لـ .link، ثم يتحقق إذا كان لكل منها سلف يطابق .nav، ثم يتحقق إذا كان لذلك السلف سلف يطابق .sidebar. بينما حسنت المتصفحات الحديثة مطابقة المحددات لتكون سريعة للغاية، فإن المحددات المعقدة بشكل مفرط لا يزال لها تكلفة قابلة للقياس عندما يكون لديك آلاف العناصر وآلاف القواعد.

المحددات من الأسرع إلى الأبطأ

ترتيب أداء المحددات

/* 1. محدد المعرّف (الأسرع، لكن تجنبه للتنسيق) */
#header { }

/* 2. محدد الفئة (سريع، النقطة المثلى) */
.header { }

/* 3. محدد الوسم (سريع للعناصر الشائعة) */
div { }

/* 4. أدوات الجمع الأخوية المجاورة (+) والابن (>) */
.nav > .link { }
.title + .subtitle { }

/* 5. أداة الجمع الأخوية العامة (~) */
.title ~ .paragraph { }

/* 6. أداة الجمع التنازلية (أبطأ، تسبب اجتياز الأسلاف) */
.sidebar .link { }

/* 7. المحدد الشامل (يطابق كل شيء) */
* { }

/* 8. محددات السمات */
[data-theme="dark"] { }
[href^="https"] { }

/* 9. الفئات الزائفة */
:nth-child(odd) { }
:not(.hidden) { }

/* 10. العناصر الزائفة */
::before { }
::after { }

/* تجنب: المحددات التنازلية المتداخلة بعمق */
.page .main .content .article .section .paragraph .link { }
/* هذا يفرض 7 مستويات من اجتياز الأسلاف لكل رابط */
نصيحة: عمليا، نادرا ما يسبب أداء المحددات تباطؤا ملحوظا في المتصفحات الحديثة. الفائدة الحقيقية للمحددات البسيطة هي قابلية الصيانة. ومع ذلك، إذا كانت لديك صفحات بأكثر من 10,000 عقدة DOM وأكثر من 5,000 قاعدة CSS (شائع في تطبيقات الويب الكبيرة)، يمكن أن يتراكم تعقيد المحددات إلى تأخيرات قابلة للقياس أثناء إعادة حساب الأنماط. استخدم محددات مسطحة ذات فئة واحدة كلما أمكن -- هنا يتألق BEM.

خصائص CSS المكلفة

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

فئات تكلفة الخصائص

/* محفزات التخطيط (الأكثر تكلفة) */
/* هذه تجبر المتصفح على إعادة حساب هندسة العناصر */
width, height
padding, margin
top, left, right, bottom
display
position
float
font-size, font-family
border-width

/* محفزات الرسم (تكلفة متوسطة) */
/* هذه تجبر المتصفح على إعادة رسم البكسلات */
color
background, background-image
border-color, border-style
border-radius
box-shadow
text-shadow
outline
visibility

/* تركيب فقط (الأرخص) */
/* هذه تؤثر فقط على التركيب، يتم على GPU */
transform
opacity
filter (في بعض الحالات)
will-change

/* أفضل ممارسة للرسوم المتحركة: */
/* فضّل هذه (تركيب فقط، 60 إطار سلس): */
.animate-good {
    transition: transform 0.3s, opacity 0.3s;
}

/* تجنب هذه (تطلق التخطيط، تسبب تقطعا): */
.animate-bad {
    transition: width 0.3s, height 0.3s, top 0.3s, left 0.3s;
}

خاصية contain

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

احتواء CSS

/* contain: layout */
/* التخطيط الداخلي للعنصر لا يؤثر على العناصر الخارجية */
.card {
    contain: layout;
}

/* contain: paint */
/* محتوى العنصر لن يُرسم خارج حدوده */
/* يمكن للمتصفح تخطي رسم هذا العنصر إذا كان خارج الشاشة */
.widget {
    contain: paint;
}

/* contain: size */
/* حجم العنصر مستقل عن أبنائه */
/* يجب تعيين عرض وارتفاع صريح */
.fixed-widget {
    contain: size;
    width: 300px;
    height: 200px;
}

/* contain: style */
/* عدادات CSS وميزات الأنماط الأخرى محدودة النطاق */
.scoped-section {
    contain: style;
}

/* contain: content (اختصار لـ layout + paint + style) */
/* الأكثر شيوعا والأكثر أمانا للاستخدام */
.article-card {
    contain: content;
}

/* contain: strict (اختصار لـ layout + paint + size + style) */
/* الأكثر عدوانية، يتطلب حجما صريحا */
.grid-item {
    contain: strict;
    width: 250px;
    height: 350px;
}

/* استخدام واقعي: قائمة بطاقات */
.product-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.5rem;
}

.product-card {
    contain: content;
    /* الآن عندما تتغير بطاقة واحدة (مثل تأثير التحويم)،
       لا يعيد المتصفح حساب التخطيط للشبكة
       بأكملها -- فقط هذه البطاقة */
}

content-visibility: auto

خاصية content-visibility هي نقلة نوعية للصفحات التي تحتوي على الكثير من المحتوى أسفل الطية. عند ضبطها على auto، تخبر المتصفح بتخطي عمل العرض (التخطيط والرسم وحل الأنماط) للعناصر خارج الشاشة. يعرض المتصفح هذه العناصر فقط عندما تكون على وشك التمرير إلى العرض. هذا يمكن أن يقلل بشكل كبير من وقت تحميل الصفحة الأولي للصفحات الطويلة.

استخدام content-visibility: auto

/* طبّق على الأقسام التي من المحتمل أن تكون أسفل الطية */
.article-section {
    content-visibility: auto;

    /* contain-intrinsic-size يوفر حجما نائبا
       حتى لا يقفز شريط التمرير عند عرض الأقسام */
    contain-intrinsic-size: auto 500px;
}

/* نمط شائع لصفحات نمط التغذية الطويلة */
.feed-item {
    content-visibility: auto;
    contain-intrinsic-size: auto 300px;
}

/* مقال مدونة بأقسام عديدة */
.blog-content section {
    content-visibility: auto;
    contain-intrinsic-size: auto 800px;
}

/* مهم: استخدم contain-intrinsic-size لمنع
   انزياحات التخطيط عند عرض المحتوى عند التمرير.
   الكلمة المفتاحية "auto" تتذكر الحجم الفعلي المعروض
   بعد أن يكون العنصر مرئيا مرة واحدة. */

/* مثال تأثير الأداء:
   صفحة بـ 100 عنصر تغذية:
   بدون content-visibility: 2.4 ثانية وقت عرض
   مع content-visibility: auto: 0.3 ثانية وقت عرض
   هذا تحسن 8 أضعاف! */
تحذير: كن حذرا عند استخدام content-visibility: auto على العناصر التي تحتوي على أهداف مرساة أو مدخلات نماذج قد تتلقى التركيز أو عناصر تستخدم position: fixed أو position: sticky. بما أن العناصر خارج الشاشة لا تُعرض، قد لا يتمكن المتصفح من التمرير إلى المراسي أو التركيز على المدخلات داخلها. اختبر بدقة، خاصة لإمكانية الوصول.

تقليل مناطق الرسم مع will-change

خاصية will-change تخبر المتصفح أن عنصرا ما سيغير خاصية معينة قريبا، مما يسمح للمتصفح بإعداد التحسينات مسبقا. عادة، يرقي المتصفح العنصر إلى طبقة مركب خاصة به (طبقة GPU)، مما يعني أن التغييرات المستقبلية لتلك الخاصية يمكن معالجتها بالكامل بواسطة GPU بدون إطلاق تخطيط أو رسم على CPU.

استخدام will-change بشكل صحيح

/* صحيح: طبّق will-change قبل بدء الرسوم المتحركة */
.card {
    transition: transform 0.3s;
}

.card:hover {
    will-change: transform;
    transform: translateY(-4px);
}

/* أفضل: طبّق عبر تحويم الأب للإشعار المسبق */
.card-container:hover .card {
    will-change: transform;
}

.card:hover {
    transform: translateY(-4px);
}

/* صحيح: طبّق عبر JavaScript عندما تكون الرسوم وشيكة */
/* element.style.willChange = 'transform';
   // ... حرّك ...
   element.style.willChange = 'auto'; // نظّف بعد ذلك */

/* خاطئ: تطبيق will-change على كل شيء بشكل دائم */
/* هذا يهدر ذاكرة GPU ويمكن أن يضر الأداء فعلا */
* {
    will-change: transform; /* لا تفعل هذا أبدا! */
}

.every-element {
    will-change: transform, opacity; /* واسع جدا، مُبذر */
}

/* خاطئ: تطبيق will-change وعدم إزالته أبدا */
.sidebar {
    will-change: transform; /* إذا لم يتحرك الشريط الجانبي أبدا، هذا يهدر الذاكرة */
}
ملاحظة: كل طبقة will-change تستهلك ذاكرة GPU. على الأجهزة المحمولة ذات ذاكرة GPU المحدودة، الإفراط في استخدام will-change يمكن أن يتسبب في طرد المتصفح لطبقات أخرى، مما يؤدي إلى أداء أسوأ بشكل عام. استخدمه باعتدال: فقط على العناصر التي ستتحرك فعلا، وأزله عند اكتمال الرسوم المتحركة. القاعدة الجيدة هي ألا يكون لديك أكثر من 10-15 طبقة will-change نشطة في وقت واحد.

الخصائص المخصصة الفعالة (متغيرات CSS)

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

أنماط أداء الخصائص المخصصة

/* غير فعال: تغيير متغير على :root يؤثر على الصفحة بأكملها */
:root {
    --primary-color: #2563eb;
    --header-height: 64px;
    --sidebar-width: 250px;
}

/* تغيير --primary-color على :root يفرض إعادة حساب
   الأنماط لكل عنصر في الصفحة */

/* فعال: حدد نطاق المتغيرات حيث تُستخدم */
.theme-toggle {
    /* محدد النطاق لقسم السمة فقط */
    --theme-bg: #fff;
    --theme-text: #111;
}

.card {
    /* محدد النطاق للبطاقات فقط */
    --card-padding: 1.25rem;
    --card-radius: 12px;
}

/* فعال: استخدم متغيرات ثابتة على :root،
   متغيرات ديناميكية على عناصر محددة */
:root {
    /* هذه نادرا ما تتغير -- آمنة على :root */
    --font-family: system-ui, sans-serif;
    --spacing-unit: 8px;
    --color-blue-500: #2563eb;
}

.sidebar {
    /* هذا يتغير عند التبديل -- محدد النطاق للشريط الجانبي */
    --sidebar-transform: translateX(0);
    transform: var(--sidebar-transform);
}

/* عند التبديل: فقط الشجرة الفرعية للشريط الجانبي تعيد الحساب */
.sidebar.is-collapsed {
    --sidebar-transform: translateX(-100%);
}

تقليل الخصوصية للأداء

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

استراتيجيات تقليل الخصوصية

/* خصوصية عالية: تتطلب تجاوزات معقدة */
#page .main-content .article .heading { color: #111; }
/* الخصوصية: 1-3-0 */

/* لتجاوز هذا، تحتاج خصوصية مساوية أو أعلى: */
#page .main-content .article .heading.special { color: #2563eb; }
/* الخصوصية: 1-4-0 */

/* خصوصية منخفضة: سهلة العمل معها */
.article-heading { color: #111; }
/* الخصوصية: 0-1-0 */

/* تجاوز بمعدّل بسيط: */
.article-heading--featured { color: #2563eb; }
/* الخصوصية: 0-1-0 (نفسها! لاحقا في المصدر يفوز) */

/* استخدام :where() لتصفير الخصوصية */
:where(.card, .widget, .panel) {
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    padding: 1rem;
}
/* الخصوصية: 0-0-0 (!) -- أي فئة يمكنها تجاوز هذا */

/* استخدام @layer لإدارة الخصوصية */
@layer base, components, utilities;

@layer base {
    a { color: #2563eb; text-decoration: underline; }
}

@layer components {
    .nav__link { color: #374151; text-decoration: none; }
    /* يفوز على طبقة base بغض النظر عن الخصوصية */
}

@layer utilities {
    .u-color-red { color: #ef4444; }
    /* يفوز على طبقة components */
}

تقسيم شفرة CSS

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

استراتيجيات تقسيم شفرة CSS

/* الاستراتيجية 1: CSS لكل صفحة */
<!-- الصفحة الرئيسية -->
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="homepage.css">

<!-- صفحة المدونة -->
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="blog.css">

<!-- صفحة المنتج -->
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="product.css">

/* الاستراتيجية 2: CSS على مستوى المكون (مع أدوات البناء) */
/* كل مكون يستورد CSS الخاص به فقط */
/* Webpack/Vite يجمع فقط ما يُستخدم لكل مسار */

/* الاستراتيجية 3: تحميل CSS الديناميكي */
<script>
    // حمّل CSS النافذة المنبثقة فقط عندما يفتحها المستخدم
    document.querySelector('.open-modal').addEventListener('click', function() {
        if (!document.getElementById('modal-css')) {
            var link = document.createElement('link');
            link.id = 'modal-css';
            link.rel = 'stylesheet';
            link.href = 'modal.css';
            document.head.appendChild(link);
        }
    });
</script>

/* الاستراتيجية 4: التقسيم المبني على المسار مع الأطر */
/* Next.js، Nuxt، SvelteKit، إلخ. تقسم CSS تلقائيا
   حسب المسار عند استخدام وحدات CSS أو
   الأنماط محدودة النطاق */

قياس أداء CSS مع أدوات المطور

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

تحليل الأداء بأدوات المطور

/* لوحة الأداء في أدوات مطور Chrome */
/* 1. افتح أدوات المطور (F12 أو Cmd+Option+I) */
/* 2. اذهب إلى تبويب Performance */
/* 3. انقر Record، تفاعل مع الصفحة، Stop */
/* 4. ابحث عن هذه في مخطط اللهب: */

/* أحداث "Recalculate Style" */
/* - كم عنصر متأثر؟ */
/* - كم من الوقت يستغرق؟ */
/* - ما الذي أطلقه؟ */

/* أحداث "Layout" */
/* - كم من الصفحة يتم ترتيبها؟ */
/* - هل هناك تخطيطات متزامنة قسرية؟ */

/* أحداث "Paint" */
/* - ما حجم منطقة الرسم؟ */
/* - هل يمكنك تقليلها بـ contain أو will-change؟ */

/* لوحة التغطية في أدوات مطور Chrome */
/* 1. افتح أدوات المطور */
/* 2. Cmd+Shift+P، ابحث عن "Coverage" */
/* 3. انقر "Start instrumenting" */
/* 4. حمّل صفحتك */
/* 5. شاهد بالضبط أي قواعد CSS مستخدمة مقابل غير مستخدمة */
/* الأشرطة الحمراء = CSS غير مستخدم (مرشح للإزالة) */
/* الأشرطة الزرقاء = CSS مستخدم */

/* لوحة العرض في أدوات مطور Chrome */
/* 1. Cmd+Shift+P، ابحث عن "Rendering" */
/* 2. فعّل "Paint flashing" -- تراكبات خضراء تظهر إعادة الرسم */
/* 3. فعّل "Layout shift regions" -- أزرق يظهر انزياحات التخطيط */
/* 4. فعّل "Layer borders" -- يظهر طبقات المركب */

/* تدقيق Lighthouse لـ CSS */
/* 1. أدوات المطور > تبويب Lighthouse */
/* 2. شغّل تدقيق الأداء */
/* 3. ابحث عن: */
/*    - "Reduce unused CSS" */
/*    - "Minify CSS" */
/*    - "Eliminate render-blocking resources" */

قائمة تدقيق أداء CSS

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

أداء التحميل

  • صغّر جميع ملفات CSS -- استخدم cssnano أو clean-css أو Lightning CSS في خط أنابيب البناء.
  • فعّل الضغط -- تأكد من تفعيل ضغط gzip أو brotli على خادمك لملفات CSS.
  • أزل CSS غير المستخدم -- استخدم PurgeCSS أو لوحة التغطية لتحديد وإزالة القواعد غير المستخدمة.
  • ضمّن CSS الحرج -- ضمّن أنماط فوق الطية في HTML وحمّل البقية بشكل غير متزامن.
  • قسّم CSS حسب الصفحة أو المسار -- تجنب تحميل ورقة أنماط عملاقة واحدة لجميع الصفحات.
  • استخدم سمات الوسائط -- أضف media="print" لأوراق أنماط الطباعة واستعلامات الوسائط لأوراق الأنماط المتجاوبة.
  • حمّل مسبقا أوراق الأنماط الرئيسية -- استخدم <link rel="preload"> لأوراق الأنماط المطلوبة قريبا لكن ليس فوريا.

أداء العرض

  • حرّك فقط transform وopacity -- هذه خصائص تركيب فقط ولا تطلق تخطيطا أو رسما.
  • استخدم contain على المكونات المستقلة -- طبّق contain: content على البطاقات وعناصر القوائم والودجتات.
  • طبّق content-visibility: auto -- استخدم على الأقسام أسفل الطية مع contain-intrinsic-size.
  • استخدم will-change باعتدال -- فقط على العناصر التي ستتحرك، وأزله بعد انتهاء الرسوم المتحركة.
  • تجنب اضطراب التخطيط -- لا تقرأ خصائص التخطيط (offsetHeight، getBoundingClientRect) بين تغييرات الأنماط.
  • قلل مناطق الرسم -- استخدم أداة Paint flashing لتحديد مناطق إعادة الرسم الكبيرة بلا داعٍ.

أداء المحددات والهندسة

  • حافظ على المحددات مسطحة -- محددات الفئة الواحدة هي الأسرع والأسهل صيانة (BEM يساعد هنا).
  • تجنب المحددات المتداخلة بعمق -- لا يجب أن يحتاج أي محدد أكثر من 3 مستويات تداخل.
  • تجنب المحدد الشامل في المواضع الرئيسية -- .container * يفرض المطابقة ضد كل عنصر.
  • حدد نطاق الخصائص المخصصة -- عرّف المتغيرات الديناميكية على عناصر محددة، وليس :root.
  • استخدم @layer لإدارة التسلسل -- يقلل الحاجة لحيل الخصوصية و!important.
  • قلل عدد القواعد الإجمالي -- قواعد أقل تعني بناء CSSOM أسرع ومطابقة أنماط أسرع.

أداء الخطوط والصور

  • استخدم font-display: swap -- يمنع النص غير المرئي أثناء تحميل خطوط الويب.
  • حمّل مسبقا الخطوط الحرجة -- استخدم <link rel="preload" as="font"> لخطوط فوق الطية.
  • فضّل خطوط النظام للنص الأساسي -- system-ui, sans-serif لا تتطلب وقت تنزيل.
  • حسّن صور الخلفية -- استخدم تنسيقات حديثة (WebP، AVIF) وأحجام مناسبة.
  • استخدم تدرجات CSS بدل الصور -- عندما يكون ممكنا، تدرجات CSS النقية أسرع من تنزيل الصور.
تمرين: افتح مشروعك في أدوات مطور Chrome وأجرِ تدقيقا شاملا لأداء CSS. أولا، استخدم لوحة التغطية لتحديد CSS غير المستخدم -- سجّل نسبة البايتات غير المستخدمة. ثانيا، استخدم لوحة الأداء لتسجيل تحميل الصفحة وابحث عن أحداث "Recalculate Style" الطويلة (أي شيء فوق 10 ميلي ثانية). ثالثا، شغّل تدقيق أداء Lighthouse وتحقق من التوصيات الخاصة بـ CSS. بناء على نتائجك، نفّذ ثلاثة تحسينات على الأقل من قائمة تدقيق هذا الدرس: ضمّن CSS الحرج، أزل القواعد غير المستخدمة، وأضف contain: content لمكونات البطاقات أو عناصر القائمة. قس أوقات ما قبل وما بعد للتحقق من التحسن.