المستوى: متوسط
Critical CSS: التقنية اللي بتنزّل First Paint من 2.8 ثانية لـ 0.6 ثانية
المتصفح بيوقف رسم الصفحة لحد ما يحمّل ملف CSS كامل ويبني الـ CSSOM. لو الـ stylesheet 380KB، المستخدم بيشوف شاشة بيضا لحوالي 2 ثانية على شبكة 4G حتى لو الـ HTML جاهز. Critical CSS بيشيل التأخير ده بإنه يضمّن جزء صغير من الـ CSS inline في الـ head، وبيأجّل الباقي.
المشكلة باختصار
الـ CSS بطبيعته render-blocking. لما المتصفح يلاقي <link rel="stylesheet"> في الـ head، بيوقف عرض الصفحة لحد ما الملف يتحمّل ويتحلّل. لو الملف 89KB بعد gzip ومخدوم بـ HTTP/2 على شبكة 4G متوسطة (1.6 Mbps)، ده 1.2 ثانية تأخير قبل أول pixel، حسب قياس HTTP Archive Web Almanac 2025.
المفهوم بمثال (للمبتدئ في البداية)
تخيّل إنك دخلت مطعم وطلبت قايمة الطعام. النادل قالك: "لحظة، الكتالوج الرئيسي بتاع كل الأطباق والأسعار بيوصل من المخزن. استنى لحد ما يجي علشان أعرف أقدملك حاجة." الكتالوج 200 صفحة، لكن انت هتطلب من 3 صفحات بس قدامك. ده بالظبط اللي بيحصل في المتصفح: المستخدم بيستنى CSS لكل صفحات الموقع علشان يرسم الـ navbar والـ hero بس.
الحل: اطبع ورقة صغيرة فيها أسعار الـ 10 أطباق الأكتر طلباً، وحطها على الترابيزة. الكتالوج الكبير يفضل جاي من المخزن، لكن العميل ابتدى يقرا فوراً. ده هو Critical CSS.
تعريف دقيق
Critical CSS هو الـ subset من قواعد الـ CSS اللي بتغطي العناصر الظاهرة في أول viewport (above-the-fold) لمّا الصفحة تتفتح. الباقي (footer, modals, hover states على عناصر بعيدة) بيتأجّل تحميله بـ media swap trick أو preload async، فمبيـ block-ش الرسم.
الحل التنفيذي في 3 خطوات
- استخرج الـ Critical CSS باستخدام
penthouseأوcrittersأوbeasties. - ضمّنه inline داخل
<style>في الـ<head>. - أجّل الباقي بـ
media="print"trick أو preload async.
npm install --save-dev penthouse
node extract-critical.js// extract-critical.js
const penthouse = require('penthouse');
const fs = require('fs');
penthouse({
url: 'https://your-site.com/',
cssString: fs.readFileSync('./dist/main.css', 'utf8'),
width: 1300,
height: 900,
timeout: 30000,
renderWaitTime: 100,
blockJSRequests: false,
}).then(criticalCSS => {
fs.writeFileSync('./dist/critical.css', criticalCSS);
console.log('Critical size:', Buffer.byteLength(criticalCSS), 'bytes');
});