Bir kullanıcı formun son alanını dolduruyorken sayfa birden kayıyor; daha önce fare altında olmayan "İptal" düğmesi tam oraya geliyor. CLS (Cumulative Layout Shift), bu tür görünmez sarsıntıları sayısal bir metriğe dönüştürüyor. "Cumulative" kelimesi artık biraz yanıltıcı (2021'deki metodoloji güncellemesinden bu yana tüm kaymalar toplanmıyor, pencereli ölçüm kullanılıyor), ama bu ayrıma gelmeden önce eşikleri netleştirmek gerekiyor.
Skoru iyileştirmeye çalışmadan önce hangi elemanın kayma ürettiğini bilmek şart; çünkü neden birden fazla, birbirinden bağımsız. Boyutsuz görsel ile font swap gecikmesinin ikisi aynı anda etkili olabilir ve birini düzeltmek diğerini gizleyebilir.
CLS eşikleri ve pencereli ölçüm
Google üç bant tanımlıyor: 0.1 ve altı iyi, 0.1-0.25 arası iyileştirme gerekli, 0.25 üzeri kötü. 0.1 sınırı net görünse de pratikte aynı sayfa farklı koşullarda çok farklı puanlar üretebiliyor. Mobil ağ koşulları, üçüncü taraf scriptlerin yüklenme sırası ve tarayıcı cache durumu bu farkı yaratan başlıca etkenler; 0.06 gösteren bir sayfa başka bir kullanıcı için 0.22 olarak ölçülebiliyor.
Pencereli ölçüm (windowed metric) şöyle çalışıyor: Chrome, sayfa yaşam döngüsünü art arda gelen 5 saniyelik pencereler olarak değerlendiriyor ve her penceredeki toplam kayma hesaplanıyor. Bu pencereler arasında en yüksek değeri alan sonuç CLS puanı olarak bildiriliyor. Uzun scroll sayfalarında sürekli küçük kaymalar artık birikmediği için haber akışı veya sayfalama kullanan arşiv sayfaları bu değişiklikten olumlu etkilendi.
Lighthouse ve CrUX aynı sayfayı ölçmüyor.
Lighthouse ölçümü ile Chrome UX Report (CrUX) verisinin neredeyse hiçbir zaman tam örtüşmemesi beklenen bir durum. Lighthouse kontrollü ortamda tek bir yükleme döngüsü ölçüyor; CrUX binlerce gerçek kullanıcı oturumundan toplanan verinin 28 günlük ortalaması. Search Console'da "kötü URL" görünürken aynı sayfa Lighthouse'da 0.04 gösterebilir. Bu çelişkide CrUX verisini temel almak daha sağlıklı; gerçek kullanıcı dağılımı lab verisinden çok daha geniş bir koşul yelpazesini kapsıyor.
Boyutsuz görseller: en sık gözden kaçan kaynak
Boyut bilgisi olmayan bir <img> etiketi tarayıcıya şunu söylüyor: dosyayı indir, ne kadar yer kaplayacağını öğren, sonra etrafındaki içeriği yeniden düzenle. Görsel inene kadar tarayıcı sıfır yükseklik rezerve ediyor; görsel geldiğinde altındaki her şey sıçrıyor.
Çözüm HTML düzeyinde iki attribute:
<img src="urun.webp" width="800" height="600" alt="Ürün görseli">
Buradaki değerler görselin görüntülenme boyutu değil, doğal (native) piksel boyutu. CSS ile farklı genişlik uygulansa bile tarayıcı 800/600 = 4:3 oranını koruyarak doğru yüksekliği önceden hesaplayabiliyor. Modern CSS'te aynı işi aspect-ratio ile de yapmak mümkün; özellikle responsive grid'lerde dinamik boyutlu container'larda tercih ediliyor:
img {
aspect-ratio: 4 / 3;
width: 100%;
height: auto;
}
Hero görsel özellikle dikkat gerektiriyor.
Viewport alanının büyük bir bölümünü kaplayan hero, küçük bir boyut hatası yapıldığında CLS puanını orantısız biçimde etkiliyor. 50 piksel hatalı yükseklik rezervasyonu, 150 kelimelik metin bloğunun tamamen ekran dışına çıkması anlamına gelebilir.
Boyut bilgisi doğru girilmiş olsa da dosya ağırlığı fazla olan görseller sorun üretiyor. Büyük dosyalar daha uzun süre boş alan göstererek kayma penceresini genişletiyor; bu nedenle görsellerin dosya boyutunu optimize etmek hem LCP hem CLS üzerinde etkili oluyor.
Font swap gecikmesi ve CLS
Web fontları yüklenene kadar tarayıcı iki seçenekten birini uyguluyor: fallback font göster (FOUT, Flash of Unstyled Text) veya metin tamamen görünmez kalsın (FOIT, Flash of Invisible Text). font-display: swap FOIT'i önlüyor ve fallback metni anında gösteriyor; ama asıl font geldiğinde karakter genişlikleri ve satır yükseklikleri değişiyor, bu değişim CLS olarak kayıt altına alınıyor.
Kaymayı büyüten faktör, fallback ve asıl font arasındaki metrik fark. Verdana ile Inter arasındaki genişlik farkı, Arial ile Roboto arasındakinden çok daha fazla. Büyük başlık boyutlarında bu fark birkaç piksel değil; bütün paragraf bloğunun yüksekliği değişebiliyor, altındaki düğmeler veya form alanları başka konumlara taşınabiliyor.
font-display: optional farklı bir trade-off sunuyor: Ağ koşulları font için gereken süreyi karşılayabiliyorsa kullanılıyor, aksi takdirde o oturum boyunca fallback'te kalınıyor. CLS bu yöntemle sıfırlanıyor; ama kullanıcı ziyaretten ziyarete farklı tipografik deneyim yaşıyor. Marka fontu kritik değilse veya fallback zaten yakın bir fontsa bu tercih mantıklı.
Preload farkı erkene çeker; yok etmez.
Preload eklemek geçişi erkene çekiyor:
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
Self-hosted fontlarda bu teknik güvenilir çalışıyor; Google Fonts üzerinden yüklenen fontlarda aynı etkiyi elde etmek için CSS @font-face bloğuna font-display: swap eklemek yetmiyor, çünkü yükleme Google'ın CDN'inden gerçekleşiyor ve preload bu durumda cross-origin kısıtlamasına takılabiliyor. Performans açısından ciddi fark kapatmak isteyenler için önerilen yol, font dosyalarını kendi sunucusuna taşımak.
Dinamik içerik enjeksiyonu
Reklam alanları, cookie banner'ları ve gecikmeli yüklenen widget'lar CLS'nin en sinsi kaynakları. Bunlar DOM'a yerleştikten sonra ekleniyor; yani ilk render sırasında tarayıcının bunları dahil etme şansı yok. Bir reklam kutusu sticky header'ın hemen altına enjekte edildiğinde, üstteki bütün içerik o kadar aşağı itiliyor.
Standart yöntem, içerik gelmeden önce yer tutma alanı ayırmak. Reklam slotlarını min-height ile önceden tanımlamak bu işi görüyor:
.reklam-alani {
min-height: 90px; /* leaderboard banner için */
width: 100%;
}
Yer tutma alanı doğru boyutlanmazsa yine kayma çıkıyor; tahmin edilen boyut gerçekten farklıysa min-height yetersiz kalıyor veya fazladan boşluk kalıyor. Bu yüzden reklam ağının belgelediği standart boyutları referans almak şart. JavaScript dosyalarını küçülterek parse süresini kısaltmak da dolaylı katkı sağlıyor: daha hızlı yürütülen script, dinamik içeriği daha erken yerleştiriyor ve kayma penceresi daralıyor.
Yer tutma alanı tahmin değil, kesin boyuttur.
Skeleton screen tekniği başka bir yaklaşım. Gerçek içerik yüklenmeden önce aynı boyutlarda gri placeholder bloklar render ediliyor. İçerik geldiğinde elemanlar aynı konumda kalıyor, yalnızca görünüm değişiyor; kayma sıfır. API'den beslenen yorumlar, kart listeleri veya öneri kutucukları gibi gecikmeli içerikler için tercih edilen yöntem bu.
CLS kaynağını teşhis etmek
CLS puanı yüksek ama neden yüksek olduğu belirsizse, Chrome DevTools'ta "Performance" sekmesiyle kayıt başlatmak ve sayfayı yeniden yüklemek ilk adım. "Experience" satırında kırmızı "Layout Shift" bloklarını görmek mümkün; tıklandığında hangi elemanın kaydığı, ne kadar kaydığı ve hangi milisaniyede gerçekleştiği detaylı gösteriliyor.
Tek kayıt karar için yeterli değil.
PageSpeed Insights ise CLS attribution bölümünde "Contributing Elements" listesi sunuyor; hangi DOM elemanının puanın ne kadarını oluşturduğunu gösteriyor. Bu liste birden fazla kaynak olduğunda önceliklendirme için değerli; zira bazen en yüksek puanı veren eleman, siz en son düzeltmek için kolları sıvadığınız değil. Sayfanın Core Web Vitals değerlerini birlikte analiz etmek bu kaynakları tek ekranda görmek için işe yarıyor; CLS ile birlikte LCP ve TBT değerlerinin birlikte değerlendirilmesi hangi optimizasyonun önce yapılması gerektiğini netleştiriyor.
Kayıt sırasında üçüncü taraf script henüz yüklenmediyse veya font preload başarıyla çalıştıysa, lab ölçümü gerçeği yansıtmayabilir. CrUX binlerce kullanıcı oturumundan toplanan 28 günlük ortalama; DevTools kaydında görünmeyen kaymalar bu veri setinde kayıtlı olabiliyor. Search Console'un "Temel Web Verileri" raporundaki URL bazlı sorunları düzenli izlemek, tek seferlik lab testine göre çok daha güvenilir sinyal veriyor.
Kaynakları doğru sırayla kapatmak önemli: boyut bilgisi eksik görseller CLS'in en yaygın nedeni olduğu için ilk bakılacak yer orası; font swap sorununu tespit etmek için DevTools "Rendering" sekmesindeki "Paint flashing" ve "Layout Shift Regions" overlay'lerini açmak birkaç saniyede nedenin görsel olarak nerede gerçekleştiğini gösteriyor.
Kullanıcı etkileşimiyle tetiklenen kaymalar
Tüm kaymalar CLS puanına eşit ağırlıkta dahil edilmiyor. Kullanıcının tıklama, dokunma veya tuşa basma eyleminden sonraki 500 milisaniye içinde gerçekleşen kaymalar ölçümün dışında tutuluyor. Bu istisna kasıtlı: kullanıcı bir accordion menüyü açtığında veya bir sekme paneline geçtiğinde altındaki içeriğin yer değiştirmesi beklenebilir bir davranış, bu yüzden puanı kirletmemeli.
Ama istisna kapsamı düşünüldüğü kadar geniş değil. 500ms penceresi yalnızca doğrudan etkileşim noktasından başlıyor; sayfa yüklendikten 3 saniye sonra otomatik açılan bir chat widget'ı bu istisnadan yararlanamıyor. Aynı şekilde, kullanıcı hiçbir şeye dokunmadan 1 saniye sonra beliren bir bildirim çubuğu tam puan kesiyor.
Animasyonlu açılma kapanma efektleri de ayrı bir tuzak. CSS transition ile bir elemanın yüksekliğini animasyonlu büyüttüğünüzde, her kare yeni bir layout shift olarak algılanabiliyor. Sayfanın performans metriklerini incelerken bu tür animasyonların CLS'e katkısını görmek için kaydı yavaş ağ koşulunda değil, normal ağda başlatmak ve animasyonu tetikleyip sonuca bakmak daha net bilgi veriyor. transform ve opacity bazlı animasyonlar layout'u etkilemediği için bu sorunun dışında kalıyor; yükseklik veya margin değiştirenler değil.