Critical CSS: sayfa yüklenirken ilk fold’un render’ı için gerekli olan CSS. Ana stylesheet yerine inline olarak ‘de, yoksa tarayıcı stylesheet’i indirene kadar beyaz ekran gösteriyor.
Teoride 100 ms FCP iyileştirmesi. Pratikte 300-600 ms iyileştirme görüyorum. Uygulaması sanıldığından daha hassas.
Sorunun kaynağı
Modern site’ların CSS bundle’ı 100-500 KB. Browser HTML’i parse ediyor, görüyor, CSS’i fetch ediyor, fetch bitene kadar render-blocking. Bu süre içinde sayfa boş.
3G / mobile’da CSS fetch 500ms-1.5s kolay. FCP 1+ saniye gecikmesi demek.
Critical CSS çözümü: above-the-fold için gerekli CSS’i inline yap, ana stylesheet’i async yükle.
Temel pattern
<head>
<style>
/* inline critical CSS: 5-15 KB */
body{margin:0;font-family:system-ui,...}
.header{...}
.hero{...}
</style>
<link rel="preload" href="/main.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/main.css"></noscript>
</head>Critical CSS immediately parsed, first paint’e yetiyor. Main CSS async yükleniyor, fold altı için yetişiyor.
Critical CSS nasıl çıkarılır
İki yaklaşım:
1. Manuel. Tasarımcı/developer above-fold component’ları seçer, CSS’lerini izole dosyada tutar. Temiz ama manuel bakım.
2. Automated extraction. Bir tool sayfayı headless browser’da render ediyor, viewport içinde hangi CSS rule’ları kullanılıyor tespit ediyor, onları çıkarıyor.
Tool’lar:
– critical (npm paketi, Addy Osmani): popüler, penthouse tabanlı
– Penthouse: Chrome headless kullanıyor
– CriticalCSS API: cloud-based hizmet
– Critters (Google): webpack plugin, development time
Ben critical paketini tercih ediyorum. Node.js-based, CLI ve programmatic API.
Critical (npm) nasıl çalışıyor
const critical = require('critical');
await critical.generate({
src: 'dist/index.html',
target: 'dist/index-critical.html',
css: ['dist/main.css'],
width: 1300,
height: 900,
inline: true
});Tool şunu yapıyor:
1. HTML’i headless Chrome’da render ediyor
2. Viewport’u (1300×900) set ediyor
3. Render edilen page’de kullanılan CSS selector’larını tespit ediyor
4. Main CSS’ten bu selector’ların rule’larını çıkarıyor
5. HTML’e inline ediyor, main CSS’i async yapıyor
Ideal viewport size’ları:
– Desktop: 1300×900
– Mobile: 375×667 (iPhone SE) veya 390×844 (iPhone 14)
– Tablet: 768×1024
Farklı viewport’lar için ayrı critical CSS üretmek mümkün ama pratikte mobile + desktop yeterli.
Build pipeline entegrasyonu
Critical CSS build-time generate ediliyor. Static site veya build-based framework için basit:
"scripts": {
"build": "webpack && critical-css-extract"
}Dynamic site (WordPress, PHP) için daha karmaşık. 3 yaklaşım:
A. Per-template critical CSS. Homepage, single post, archive için ayrı critical CSS üret, build-time kaydet, template render sırasında inline et.
WordPress’te:
if (is_front_page()) {
echo '<style>' . file_get_contents(get_template_directory() . '/critical-home.css') . '</style>';
} elseif (is_single()) {
echo '<style>' . file_get_contents(get_template_directory() . '/critical-single.css') . '</style>';
}B. Per-URL generation. CDN veya build service her URL için critical CSS üretiyor. Cloudflare APO, Netlify build plugin, vs. Maliyetli ama otomatik.
C. Runtime generation. Request anında generate etmek (hacky, yavaş). Genelde önerilmez.
Ben A’yı tercih ediyorum. Pragmatic, kontrollü, bakımı kolay.
Dinamik içerik problemi
Dynamic page’de critical CSS üretmek zor. Aynı homepage farklı kullanıcılara farklı görünebiliyor (A/B test, personalization, logged-in state).
Çözüm: kullanıcıdan bağımsız “shell” CSS üret. Header, navigation, hero structure, footer base style’ları. Personalization layer async yükleniyor.
Critical CSS = structure. Dynamic content = normal CSS.
Boyut limiti
Critical CSS ne kadar olmalı? Pratik üst sınır ~14 KB.
Neden 14 KB: ilk TCP packet’i network üzerinde genelde ~14KB data taşıyor. Bu boyuta sığan HTML + critical CSS tek round trip’te geliyor, extra RTT olmadan.
15 KB aşan critical CSS zararlı olabiliyor. Çok büyükse fold’un üzerinde render blocking var demektir.
Modern HTTP/3 + QUIC bu 14 KB limitini biraz esnetiyor ama genel kural hala geçerli.
Measurement
Critical CSS etkisini ölçmek için:
Lighthouse. FCP metrik’i doğrudan etkileniyor. Pre/post ölçüm.
WebPageTest. Film strip görünümü ile “sayfa ne zaman görünür hale geldi” görselleştiriliyor.
Real-User Monitoring. CrUX, web-vitals library ile gerçek kullanıcı FCP metrics.
Bir projede critical CSS eklenince:
– FCP p75: 1.8s → 1.1s (-39%)
– LCP p75: 2.4s → 1.9s (-21%)
– CLS: etkilenmedi
Potansiyel tuzaklar
1. Above-fold yanlış hesaplandı. Viewport size yanlışsa critical CSS eksik çıkıyor, ilk paint FOUC (flash of unstyled content) yaşıyor.
2. Custom fonts. Critical CSS font-face’i içermiyorsa font async yüklenirken FOIT (flash of invisible text) veya FOUT (flash of unstyled text). Font-display: swap + preload combinasyonu gerekli.
3. JavaScript-dependent styling. Style JS ile eklenen element’lere uygulanıyorsa critical CSS tool’u bunu yakalayamıyor. Manuel maintenance.
4. Dark mode / theme switching. prefers-color-scheme ile dynamic theme’ler critical CSS’e girmeli, yoksa kullanıcı yanlış tema gördüğü split second yaşıyor.
5. Critical CSS regeneration disiplini. Site tasarımı değişiyor, yeni component ekleniyor, eski critical CSS stale. Her deploy’da regenerate etmezseniz FOUC dönüyor.
CI pipeline entegrasyonu
Critical CSS’i CI pipeline’a ekleyin:
- name: Build
run: npm run build
- name: Extract Critical CSS
run: node scripts/extract-critical.js
- name: Test FCP
run: lighthouse-ci --assert performanceHer deploy fresh critical CSS ile gidiyor. Regression alarmda yakalanıyor.
Alternatif: server push veya early hints
HTTP/2 server push (şimdi deprecated) ve HTTP/3 early hints (103 Early Hints) CSS preload’ı daha erken bildirmek için kullanılabiliyor.
Critical CSS kadar radikal iyileştirme yok ama implementation daha basit. Ufak siteler için 103 Early Hints yeterli olabilir, critical CSS gerekmeyebilir.
Büyük site’larda critical CSS + early hints kombinasyon işe yarıyor.
Son tavsiye
Critical CSS FCP’yi ciddi iyileştiren, implementasyonu orta karmaşıklıktaki tekniklerden biri. Otomatize edilebiliyor, per-template generation pattern’ı dynamic site’lar için de uygulanabiliyor.
Performance audit yaparken FCP yüksekse ilk bakılacak yer. 1.5 saniyenin üzerinde FCP varsa critical CSS extraction büyük fark yaratıyor. 800ms altındaysa diğer optimization’ları öncele.