Telefonda bir haber sitesini açıyorsunuz, okumaya başlıyorsunuz, tam o sırada üstteki reklam yükleniyor ve metin aşağı kayıyor. Ya da ürün sayfasında "Sepete Ekle" düğmesine basmak üzereyken sayfa bir zıplıyor ve parmağınız bambaşka bir bağlantıya değiyor. Bu davranışın adı düzen kaymasıdır ve ölçen metriğin adı CLS'tir. Kullanıcı bunu "sayfa açılırken içerik zıplıyor" diye tarif eder, PageSpeed raporu ise "Cumulative Layout Shift" diye yazar; ikisi aynı şeydir.
Bu yazıda CLS nedir, tarayıcı kaymayı tam olarak nasıl hesaplar ve neden bazı hareketler sayılırken bazıları sayılmaz onu netleştireceğiz. Ardından kendi sayfanızdaki kayan öğeyi tam olarak bulmak için DevTools ve konsol yöntemlerini göstereceğiz. Sonrasında pratikte CLS üreten beş ana kaynağı — boyutsuz görseller, reklam ve gömü alanları, web fontları, dinamik enjekte edilen bloklar ve geç yüklenen çerez bandı — tek tek ele alıp her biri için kopyalanabilir düzeltme kodu vereceğiz. Sahada gördüğüm en sinsi durum, laboratuvar testinde CLS 0 çıkan bir sitenin saha verisinde 0,32 almasıdır; nedenini ve nasıl yakalanacağını da anlatacağız.
CLS Nedir ve Nasıl Hesaplanır#
CLS (Cumulative Layout Shift, "kümülatif düzen kayması"), sayfa ömrü boyunca yaşanan beklenmedik düzen kaymalarının toplam şiddetini ölçen puandır. Süre değil, birimsiz bir orandır; bu yüzden saniye olarak değil "0,08" gibi bir sayı olarak raporlanır.
Tarayıcı her bir kayma için iki değeri çarpar:
Kayma puanı = etki oranı × mesafe oranı
- Etki oranı: Hareket eden öğelerin, hareketten önceki ve sonraki konumlarını kapsayan alanın görünür alana oranı.
- Mesafe oranı: En çok hareket eden öğenin kat ettiği mesafenin, görünür alanın en büyük boyutuna oranı.
Örnek: Görünür alanı 1000 piksel yüksekliğinde bir telefonda, ekranın yarısını kaplayan bir metin bloğu 100 piksel aşağı kayıyor. Etki oranı yaklaşık 0,6 (blok + kaydığı alan), mesafe oranı 0,1. Kayma puanı 0,06. Sayfa yüklenirken böyle üç olay yaşanırsa toplam CLS 0,18 olur — yani sınırın epey üstünde.
Eşikler:
| CLS değeri | Durum | Pratik karşılığı |
|---|---|---|
| 0 – 0,1 | İyi | Kullanıcı kaymayı fark etmez |
| 0,1 – 0,25 | İyileştirme gerekli | Fark edilir, rahatsız edicidir |
| 0,25 ve üzeri | Zayıf | Yanlış tıklama üretir |
Her hareket kayma sayılmaz. Tarayıcı, kullanıcı etkileşiminden sonraki 500 milisaniye içinde gerçekleşen düzen değişikliklerini "beklenen" kabul eder ve puana katmaz. Akordiyon menüyü açtığınızda içeriğin aşağı inmesi CLS üretmez. transform ve opacity ile yapılan animasyonlar da düzen hesabını tetiklemediği için sayılmaz — bu, birazdan göreceğimiz düzeltmelerin temel dayanağıdır.
Ayrıca saha verisinde CLS oturum penceresi mantığıyla hesaplanır: birbirine 1 saniyeden yakın kaymalar gruplanır, pencere en fazla 5 saniye sürer ve sayfanın toplam CLS'i en kötü pencerenin puanıdır. Yani tek uzun bir sayfada sonsuz kaydırma yapmanız puanı sürekli artırmaz.
Sayfanızdaki Kayan Öğeyi Bulma#
Optimizasyona geçmeden önce hangi öğenin kaydığını görmelisiniz; tahminle yapılan CLS düzeltmesi neredeyse hiç tutmaz.
1. DevTools Rendering paneliyle canlı görme. Chrome'da Ctrl+Shift+P → "Show Rendering" yazın → Layout Shift Regions kutusunu işaretleyin. Sayfayı yenileyin; kayan her bölge birkaç yüz milisaniye mavi-mor bir dikdörtgenle vurgulanır. Bu, sorunu gözle görmenin en hızlı yoludur ve ekran kaydı alıp yavaşlatarak tekrar izleyebilirsiniz.
2. Performance panelinde kaynağı bulma. Kayıt alın, zaman çizelgesinde Experience şeridindeki kırmızı "Layout Shift" bloklarına tıklayın. Alt panelde kayan öğenin adı, kayma puanı ve "hadRecentInput" bayrağı görünür. Bu bayrak true ise o kayma puana dahil edilmemiştir.
3. Konsola yapıştırılabilir izleyici. Sahada müşteri sitesinde en çok bunu kullanıyorum çünkü hem puanı hem öğeyi tek seferde döker:
let toplam = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.hadRecentInput) continue;
toplam += entry.value;
console.log('Kayma:', entry.value.toFixed(4), 'Toplam:', toplam.toFixed(4));
for (const kaynak of entry.sources || []) {
console.log(' Kayan öğe:', kaynak.node);
}
}
}).observe({ type: 'layout-shift', buffered: true });
Sayfayı yenileyin, sonra biraz aşağı kaydırın. Konsolda her kaymanın puanı ve DOM düğümü satır satır listelenir. sources dizisi genelde tek bir öğe verir ve bu, düzeltilecek öğedir.
⚠️ Ölçümü mutlaka mobil görünümde ve yavaş ağ simülasyonuyla yapın. Masaüstünde hızlı bağlantıda görseller o kadar hızlı iner ki kayma hiç yaşanmaz; aynı sayfa 3G'de 0,3 puan üretir. Laboratuvar-saha uçurumunun bir numaralı sebebi budur.
Kaynak 1: Boyutsuz Görseller#
Görsellerine width ve height yazmamak, Türkçe sitelerde gördüğüm CLS'in tek başına en büyük nedenidir.
Tarayıcı HTML'i okurken görselin yerini ayırmak için boyutunu bilmek zorundadır. Öznitelik yoksa görsele 0 piksel yer ayırır, metni yukarı çeker, görsel indiğinde aniden yer açar ve altındaki her şey aşağı kayar.
<!-- YANLIŞ: yer ayrılmıyor, görsel gelince alt içerik kayıyor -->
<img src="/gorsel/urun.webp" alt="Ürün">
<!-- DOĞRU: en-boy oranı baştan biliniyor -->
<img src="/gorsel/urun.webp" width="1200" height="675" alt="Ürün">
Buradaki ince nokta şudur: width ve height özniteliklerini yazmanız görselin o boyutta görüneceği anlamına gelmez. Modern tarayıcılar bu iki değerden en-boy oranını hesaplar ve CSS ile ölçeklenirken oranı korur. Responsive tasarımda şu CSS'i eklemeniz yeterlidir:
img {
max-width: 100%;
height: auto;
}
Bu ikisi birlikte çalışır: öznitelikler oranı bildirir, CSS genişliği kaba uydurur, tarayıcı doğru yüksekliği baştan ayırır ve hiç kayma olmaz.
Oranın CSS ile bilindiği ama boyutun değişken olduğu durumlarda aspect-ratio kullanın:
.kart-gorsel {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
background: #eef1f5;
}
Arka plan rengi vermek görsel inene kadar boş alanın gri bir yer tutucu gibi görünmesini sağlar; kullanıcı deneyimi açısından beyaz boşluktan iyidir.
WordPress'te tema kodunda the_post_thumbnail() ve wp_get_attachment_image() boyut özniteliklerini otomatik ekler. Sorun genelde elle yazılmış tema şablonlarında, sayfa oluşturucu bloklarında ve dış kaynaktan çekilen görsellerde çıkar. Görsel yükleme davranışının LCP tarafındaki etkisi için lcp nasıl iyileştirilir yazısına da göz atın; iki metrik burada birbirine değer.
Lazy-load ile ilişkisi. loading="lazy" kullanmak CLS üretmez — boyut öznitelikleri varsa. Ama boyutsuz bir görseli lazy-load etmek en kötü kombinasyondur: görsel kullanıcı o noktaya kaydırdığında iner ve tam okurken sayfayı zıplatır. Lazy-load ayarlarını wordpress lazy load yazısında ayrıntılı ele aldık.
Kaynak 2: Reklam, Gömü ve iframe Alanları#
Reklam slotu ve gömülü içerik, boyutu sunucudan gelene kadar bilinmediği için kaymanın klasik kaynağıdır.
Çözüm, kabı minimum yükseklikle rezerve etmektir:
.reklam-slot {
min-height: 280px; /* masaüstü banner */
display: block;
background: #f4f6fa;
}
@media (max-width: 768px) {
.reklam-slot {
min-height: 250px; /* mobil banner */
}
}
Buradaki karar şudur: rezerve edilecek yükseklik, o slotta en sık gösterilen reklam boyutu olmalıdır, en büyüğü değil. En büyüğe göre ayırırsanız çoğu zaman büyük bir boşluk kalır; en küçüğe göre ayırırsanız her büyük reklamda kayma yaşarsınız.
YouTube gömüleri için oran kutusu klasik ve hâlâ en sağlam çözümdür:
<div class="video-kutu">
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
title="Video" loading="lazy" allowfullscreen></iframe>
</div>
.video-kutu {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
background: #000;
}
.video-kutu iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
⚠️ Reklam sağlayıcısının "otomatik boyut" özelliğini kullanıyorsanız CLS'i kontrol edemezsiniz; sabit boyutlu slot tanımlamak reklam gelirini bir miktar etkileyebilir ama kullanıcı deneyimi ve arama sıralaması açısından takas genelde lehinizedir.
Kaynak 3: Web Fontları ve Metin Kayması#
Font yüklemesi kaymayı iki farklı şekilde üretir ve ikisinin çözümü farklıdır.
FOIT (Flash of Invisible Text): Tarayıcı font inene kadar metni görünmez tutar. Metin sonradan belirir, altındaki içerik kayar.
FOUT (Flash of Unstyled Text): Metin önce sistem fontuyla çizilir, özel font gelince değişir. Harf genişlikleri farklı olduğu için satır sayısı değişir ve blok yüksekliği zıplar.
font-display: swap FOIT'i çözer ama FOUT'u üretir. Kaymayı sıfırlamak için yedek fontu özel fontun metriklerine yaklaştırmak gerekir:
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-regular.woff2') format('woff2');
font-weight: 400;
font-display: swap;
}
/* Yedek fontu metrik olarak hizala */
@font-face {
font-family: 'Inter Fallback';
src: local('Arial');
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: 'Inter', 'Inter Fallback', system-ui, sans-serif;
}
size-adjust ve ascent-override değerleri yedek fontun satır yüksekliğini özel fontunkine eşitler; font değiştiğinde satır sayısı değişmez, dolayısıyla kayma da olmaz. Değerler fonta göre değişir; doğru rakamı bulmanın pratik yolu, DevTools'ta font değişimini yavaşlatıp blok yüksekliğini iki durumda ölçmek ve oranı size-adjust olarak yazmaktır.
Ayrıca fontu kendi sunucunuzdan sunun ve preload edin — dış kaynaktan gelen font ek DNS ve TLS gecikmesi demektir:
<link rel="preload" href="/fonts/inter-regular.woff2" as="font" type="font/woff2" crossorigin>
Kaynak 4: Çerez Bandı, Bildirim Çubuğu ve Geç Enjekte Bloklar#
Sayfa yüklendikten sonra JavaScript ile içeriğin üstüne eklenen her blok, altındaki her şeyi aşağı iter ve tek başına 0,15–0,30 puan üretebilir.
En yaygın suçlular: çerez onay bandı, "sitemizde kampanya var" duyuru çubuğu, kargo bedava şeridi, dil seçim bandı ve e-bülten aboneliği kutusu.
Doğru çözüm iki adımlıdır:
1. Bandı akıştan çıkarın. Sayfa akışına eklemek yerine üstte sabitleyin:
.cerez-bandi {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
transform: translateY(100%);
transition: transform .25s ease;
padding-bottom: env(safe-area-inset-bottom);
}
.cerez-bandi.acik {
transform: translateY(0);
}
position: fixed öğeyi düzen akışından çıkarır; ne kadar büyük olursa olsun altındaki içeriği itmez. Giriş animasyonunu transform ile yapmak da kritiktir çünkü transform düzen hesabını tetiklemez ve CLS üretmez. Aynı animasyonu height veya top ile yaparsanız puanı yükseltirsiniz.
2. Akıştaki bandı zorunluysa yerini önceden ayırın. Üst duyuru çubuğu tasarımın parçasıysa ve sabitlenemiyorsa, sunucu tarafında HTML'e baştan basın; JavaScript ile eklemeyin. Sunucudan gelen HTML'de zaten varsa kayma olmaz.
Çerez bandını kurarken düzen davranışına dikkat edilecek noktalar için çerez onay bandı kurulumu yazısındaki uygulama adımları işinizi görür.
Kaynak 5: Animasyon, Yazı Tipi Boyutu ve Dinamik İçerik#
Bazı kaymalar tasarımın kendisinden gelir ve kod kadar karar meselesidir.
Yanlış animasyon özelliği. Bir öğeyi top, left, width, height, margin veya padding üzerinden animasyonlamak her karede düzen hesabı tetikler ve CLS üretir. Aynı görsel etkiyi transform ile alın:
/* YANLIŞ: her karede düzen yeniden hesaplanır */
.kart:hover { margin-top: -8px; }
/* DOĞRU: düzen etkilenmez, CLS üretmez */
.kart { transition: transform .2s ease; }
.kart:hover { transform: translateY(-8px); }
Sonsuz kaydırma ve "daha fazla yükle". Yeni içerik sayfanın altına ekleniyorsa sorun yoktur. Ancak footer sabitse ve yeni içerik onu itiyorsa, ya da içerik listenin ortasına ekleniyorsa kayma olur. Yükle düğmesini listenin altına koyun ve yeni öğeleri her zaman mevcut içeriğin altına ekleyin.
Yer tutucu (skeleton) kullanımı. Veri gelene kadar gösterilen iskelet blok, gerçek içerikle aynı yüksekliğe sahip olmalıdır. Yüksekliği tutmayan iskelet, sorunu çözmek yerine iki kez kaydırır.
A/B testi ve kişiselleştirme scriptleri. Bu araçlar sayfayı yükledikten sonra DOM'u değiştirir; laboratuvar testinde çoğu zaman görünmezler ama saha verisinde CLS'i patlatırlar. Kullanıyorsanız değişikliği uygulayacağınız bölgeyi baştan sabit yükseklikle rezerve edin.
Laboratuvar Verisi Temiz Ama Saha Verisi Kötü Durumu#
Bu, CLS'te en sık karşılaşılan ve en çok kafa karıştıran durumdur: PageSpeed'in laboratuvar bölümünde CLS 0, saha bölümünde 0,28.
Nedeni şudur: laboratuvar testi sayfayı açar, birkaç saniye bekler ve kaydı kapatır. Gerçek kullanıcı ise sayfayı aşağı kaydırır. Aşağıda lazy-load ile inen boyutsuz görseller, geç gelen reklam slotları ve sonsuz kaydırma o anda devreye girer ve CLS'i üretir. Laboratuvar bu davranışı hiç görmez.
Teşhis için doğru yöntem:
- Sayfayı mobil görünümde açın, ağı "Slow 4G" yapın.
- Yukarıdaki
PerformanceObserverkodunu konsola yapıştırın. - Sayfayı yavaşça sonuna kadar kaydırın, sonra başa dönün.
- Konsoldaki "Toplam" değeri gerçek kullanıcı CLS'inize çok daha yakındır.
İkinci bir tuzak, Search Console'daki CLS raporunun URL grupları üzerinden çalışmasıdır: tek bir kötü şablon (örneğin ürün detay şablonu) yüzlerce URL'yi birden kırmızıya düşürür. Rapordaki örnek URL'yi değil, o URL'nin şablonunu düzeltmeniz gerekir. Metriklerin genel raporlanma mantığı ve düzeltme sırası için pagespeed insights skoru yükseltme yazısı iyi bir tamamlayıcıdır. WordPress özelinde tema ve eklenti kaynaklı CLS senaryolarını wordpress core web vitals yazısında topladık.
Doğrulama ve Kalıcı Kontrol#
Düzeltmeyi yaptıktan sonra üç aşamalı doğrulama uygulayın.
Aşama 1 — Görsel doğrulama. Rendering panelinde Layout Shift Regions açıkken sayfayı baştan sona kaydırın. Hiçbir mor dikdörtgen çıkmamalı.
Aşama 2 — Sayısal doğrulama. Konsol izleyicisiyle toplam değeri okuyun. Hedef 0,1 değil, mümkünse 0,05'in altıdır; saha verisi laboratuvardan her zaman kötü çıkar, pay bırakın.
Aşama 3 — Regresyon koruması. CLS düzeltmeleri en kolay bozulan iyileştirmelerdir; yeni bir eklenti, yeni bir reklam slotu veya bir tema güncellemesi tek hamlede geri getirir. Yayına aldığınız her değişiklikten sonra ana şablonları (ana sayfa, kategori, ürün/yazı detay) hızlıca tekrar ölçün.
Sunucu tarafında yapabileceğiniz destekleyici bir şey daha var: HTML'in tek parça ve hızlı gelmesi, tarayıcının düzeni tek seferde kurmasını kolaylaştırır. Parça parça, yavaş akan bir HTML yanıtı ara render adımları üretir. Barındırma katmanının bu davranışa etkisini ve paket türleri arasındaki kaynak farklarını web hosting türleri yazısında karşılaştırdık.
Sıkça Sorulan Sorular#
CLS skoru kaç olmalı#
CLS, ziyaretçilerin %75'i için 0,1 ve altında olmalıdır. 0,1 ile 0,25 arası "iyileştirme gerekli", 0,25 üzeri "zayıf" sayılır. Pratikte 0,05 altını hedeflemek daha güvenlidir çünkü saha verisi laboratuvar ölçümünden neredeyse her zaman kötü çıkar ve tek bir yeni reklam slotu sizi sınırın üstüne taşıyabilir. Puan birimsizdir, saniye değildir; kayan alanın büyüklüğü ile kayma mesafesinin çarpımından gelir.
Görsele width ve height yazınca responsive tasarım bozulur mu#
Hayır, bozulmaz. Modern tarayıcılar width ve height özniteliklerini sabit boyut olarak değil, en-boy oranı bilgisi olarak kullanır. CSS'te img { max-width: 100%; height: auto; } kuralı varsa görsel kabına uyum sağlamaya devam eder, tarayıcı ise doğru yüksekliği baştan ayırdığı için kayma olmaz. Yani öznitelikleri yazmamak responsive tasarımı korumaz, sadece CLS üretir.
CLS düzeltmesi yaptım ama Search Console hâlâ kırmızı gösteriyor#
Search Console saha verisi kullanır ve bu veri 28 günlük yuvarlanan bir pencereyle güncellenir, dolayısıyla düzeltmenizin yansıması haftalar alır. Ayrıca rapor URL'leri gruplar hâlinde değerlendirir; grubun tamamı düzelmeden durum yeşile dönmez. Doğru takip yöntemi, düzeltmeyi yaptığınız tarihi not edip Search Console'daki "Doğrulamayı başlat" akışını kullanmak ve bu arada laboratuvar ölçümünüzün temiz kaldığını haftalık kontrol etmektir.
Çerez bandı CLS'i neden bu kadar artırıyor#
Çünkü tipik çerez bandı, sayfa yüklendikten sonra JavaScript ile içeriğin üstüne veya altına eklenir ve akışa girdiği için altındaki her şeyi iter. Görünür alanın önemli bir bölümünü kaplayan bir bandın kaydırdığı içerik, kolayca 0,2 puanın üzerinde bir kayma üretir. Çözüm bandı position: fixed ile düzen akışından çıkarmak ve giriş animasyonunu transform ile yapmaktır; bu iki değişiklik bandın CLS katkısını sıfırlar.
Reklam slotuna kaç piksel yer ayırmalıyım#
O slotta en sık gösterilen reklam boyutunun yüksekliğini ayırın, en büyüğünü değil. Masaüstünde yaygın banner yükseklikleri 250 ve 280 piksel civarındadır, mobilde 250 piksel çoğu durumu karşılar. En büyük boyuta göre ayırmak sayfanızda sürekli boşluk bırakır, en küçüğe göre ayırmak ise her büyük reklamda kayma üretir. Farklı boyutlar dönüyorsa min-height ile taban belirleyip kabı arka plan rengiyle görünür kılmak en dengeli çözümdür.
Animasyonlar CLS'i etkiler mi#
Etkiler, ama hangi CSS özelliğini animasyonladığınıza bağlıdır. top, left, width, height, margin gibi düzen özelliklerini animasyonlamak her karede yeniden yerleşim tetikler ve doğrudan CLS üretir. Buna karşılık transform ve opacity animasyonları düzen hesabını hiç tetiklemez, bu yüzden puana katkı vermez. Kural basittir: bir öğeyi hareket ettireceksiniz translate, büyüteceksiniz scale kullanın.
Kullanıcı tıkladıktan sonraki kaymalar sayılıyor mu#
Hayır, kullanıcı etkileşiminden sonraki 500 milisaniye içinde gerçekleşen kaymalar puana dâhil edilmez. Akordiyon menüyü açtığınızda içeriğin aşağı inmesi, "daha fazla göster" düğmesine bastığınızda listenin uzaması gibi hareketler beklenen davranış kabul edilir. PerformanceObserver çıktısındaki hadRecentInput: true bayrağı tam olarak bunu ifade eder. Ancak 500 milisaniyeden geç gelen değişiklikler yeniden sayılmaya başlar; ağır bir isteğin sonucu geç dönüyorsa yine puan alırsınız.
Laboratuvar testinde CLS sıfır çıkıyor ama gerçek kullanıcılarda yüksek, sebebi ne#
Bunun nedeni laboratuvar testinin sayfayı kaydırmamasıdır. Test sayfayı açar, birkaç saniye bekler ve biter; oysa gerçek kullanıcı aşağı kaydırır ve o anda lazy-load ile inen boyutsuz görseller, geç yüklenen reklam slotları ve sonsuz kaydırma devreye girerek kaymayı üretir. Teşhis için sayfayı mobil görünümde ve yavaş ağ simülasyonuyla açıp konsola bir layout-shift izleyicisi yapıştırın, sonra sayfayı yavaşça sonuna kadar kaydırın. Bulacağınız toplam değer gerçek kullanıcı deneyimine çok daha yakındır.
Kapanış#
CLS, hız değil kararlılık ölçen bir metriktir ve düzeltmesi de hız optimizasyonundan farklıdır: mesele bir şeyi hızlandırmak değil, her öğeye yerini önceden ayırmaktır. Beş kaynağı sırayla kapatın — görsellere width/height yazın, reklam ve gömü alanlarına min-height veya aspect-ratio ile yer ayırın, fontları metrik uyumlu yedeklerle swap moduna alın, geç eklenen bantları position: fixed ile akıştan çıkarın ve animasyonları yalnızca transform ile yapın. Ölçümü mutlaka mobil görünümde, yavaş ağda ve sayfayı sonuna kadar kaydırarak yapın; masaüstünde temiz görünen sayfa sahada kırmızı olabilir.
Bu düzeltmelerin çoğu tema ve şablon seviyesinde yapılır, yani teknik dokunuş ister. Kendi sitenizde tema dosyalarına girmek istemiyorsanız WordPress bakım hizmeti şablon, görsel ve eklenti kaynaklı düzen sorunlarını üstlenir; site genelinde teknik SEO ve Core Web Vitals çalışması için SEO hizmeti daha kapsamlı bir denetim yapar. Sayfa kaynaklarının hızlı ve tek parça gelmesi de kararlılığı destekler; bunun için önbellekleme hazır gelen bir WordPress hosting paketi ya da trafiği artmış siteler için izole kaynak sunan bir VDS sunucu sağlam bir zemin oluşturur.