Satır Kırma ve Taşma: white-space, overflow-wrap, word-break, hyphens, text-wrap: balance/pretty ve Çok Satırlı Kesme
Uzun URL ve ISBN için overflow-wrap: anywhere, kullanıcı metninde white-space: pre-line, başlıkta text-wrap: balance, çok satırlı kesmede ise hâlâ -webkit-line-clamp kalıbı kullanılır; Türkçe metinde hyphens: auto'ya güvenilmez. Bookstore'un kitap detayında break-word ile anywhere'in min-content farkını, Türkçe hecelemenin iki motorda da çalışmadığını, balance ve pretty sonuçlarını ve WebKit'in clamp'li kutuda ::first-letter çizmemesini ölçüyoruz.
Sürüm notu: Bu yazı CSS Text Module Level 3/4, CSS Overflow Level 4 ve CSS Inline Layout Level 3 spesifikasyonlarına ve Baseline'ın Kasım 2026 durumuna (web-features 3.46.0) göre yazıldı; örnekler Chromium 158 ve WebKit 27.2 (Playwright 1.65) ile denenmiştir, Firefox desteği web-features ve MDN'in (Mozilla'nın web dokümantasyon projesi) browser-compat-data 8.1.9 verisinden alınmıştır.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) için kitap detayına müşteri yorumlarını ekledik. Bir müşteri yorumuna uzun bir URL yapıştırmış ve yorum kutusu sağa taşmış. Taşmayı önlemek için iki adayımız var: overflow-wrap: break-word ve overflow-wrap: anywhere. İkisini de içeriğe göre boyutlanan (min-content) bir kutuya koyduk:
.row { display: flex; gap: 1rem; align-items: start; }
.fit { inline-size: min-content; }
.break-word { overflow-wrap: break-word; }
.anywhere { overflow-wrap: anywhere; }
<div class="row">
<p class="fit break-word">https://bookstore.test/books/9780141439549/reviews</p>
<p class="fit anywhere">https://bookstore.test/books/9780141439549/reviews</p>
</div>
İki kutunun genişliği aynı mı olur? break-word zaten "kelimenin ortasından kır" demek değil mi? Cevabını not et; ilk ölçüm bununla ilgili.
overflow-wrap ve word-break: Uzun URL, ISBN ve E-posta
Tarayıcı satırı normalde yalnız izin verilen yerlerden kırar: boşluklar, tireler ve dilin kurallarına göre bazı noktalama işaretleri. Boşluk içermeyen uzun bir URL, ISBN (International Standard Book Number; kitapların uluslararası kimlik numarası) ya da e-posta adresi kutudan daha genişse dışarı taşar. Bu durum için üç araç var ve davranışları farklı:
node scripts/03-wrap.mjs
fit break-word box width 386.8px
fit anywhere box width 11.8px
narrow break-word 4 lines (collapse / wrap)
narrow anywhere 4 lines (collapse / wrap)
narrow break-all 3 lines (collapse / wrap)
Giriş sorusunun cevabı ilk iki satırda. break-word kutusu 386.8px: URL'in tamamı kadar geniş. anywhere kutusu ise 11.8px, yani tek bir karakter genişliğinde. İkisi de taşmayı engellemek için kelimenin ortasından kırar, ama yalnız anywhere bu kırma noktalarını kutunun min-content genişliği hesaplanırken hesaba katar. min-content, fit-content, Flexbox'ta min-width: auto ve Grid'de auto track'ler gibi içeriğe göre boyutlanan her yerde fark ortaya çıkar: break-word ile flex item URL kadar genişler ve kartı iter, anywhere ile daralabilir. Intrinsic size kavramının kendisi sitedeki CSS serisinin (CSS3: Flexbox, Grid ve Modern CSS) 03. yazısında anlatıldı.
Sabit genişlikli bir kolonda ise break-word ve anywhere aynı sonucu veriyor (4 satır), çünkü genişlik dışarıdan geliyor. word-break: break-all farklı: her kelimeyi gerektiğinde ortasından kırar, taşma olmasa bile. Satırlar tam dolduğu için 3 satıra iniyor, ama "See" ve "for" gibi normal kelimeler de satır sonunda bölünebilir hâle geliyor:
.narrow { inline-size: 12em; }
.break-all { word-break: break-all; }
Bookstore'da yorum metni için overflow-wrap: anywhere seçiyoruz: normal kelimelere dokunmuyor, sadece sığmayan parçayı kırıyor ve kartın daralmasına izin veriyor. word-break: break-all'u Latin alfabeli metinde neredeyse hiç kullanmıyorum; yerini bir kolonda karakter karakter dizilen kod ya da hash gibi veriler. Eski kodlarda görülen word-break: break-word değeri standart dışı ve deprecated; tarayıcılar onu overflow-wrap: anywhere gibi yorumluyor.
white-space ve Longhand'leri: Kullanıcının Satır Sonlarını Korumak
HTML'de art arda gelen boşluklar ve satır sonları tek bir boşluğa iner. Müşteri yorumunu üç satır olarak yazdıysa ekranda tek paragraf görünür:
<p class="review">Great translation.
Shipping took two days.
Would buy again.</p>
<p class="review pre">Great translation.
Shipping took two days.
Would buy again.</p>
.review { inline-size: 24em; }
.review.pre { white-space: pre-wrap; }
review 2 lines (collapse / wrap)
review pre 3 lines (preserve / wrap)
Normal hâlde metin 2 satıra sarıyor, pre-wrap ile müşterinin yazdığı 3 satır korunuyor. Parantez içindeki değerler white-space'in yeni longhand'leri: white-space artık white-space-collapse (boşluklar birleşsin mi, korunsun mu) ve text-wrap-mode (satır sarılsın mı) property'lerinin shorthand'i. pre-wrap, preserve ve wrap'e açılıyor; nowrap ise collapse ve nowrap'e. white-space-collapse Eylül 2026'dan beri Baseline widely available, yani longhand'i tek başına yazmak da güvenli.
Değerlerin kısa özeti: normal boşlukları ve satır sonlarını birleştirir ve sarar; nowrap birleştirir ama sarmaz; pre her şeyi korur ve sarmaz; pre-wrap her şeyi korur ve sarar; pre-line satır sonlarını korur, boşlukları birleştirir ve sarar; break-spaces pre-wrap gibidir ama satır sonundaki boşluklar da yer kaplar. Kullanıcının yazdığı metinde pre-line'ı tercih ediyorum: satır sonları kalıyor, yanlışlıkla basılmış çift boşluklar birleşiyor.
hyphens: Heceleme Dile Bağlı
hyphens: auto, tarayıcının dil sözlüğünü kullanarak kelimeyi hece sınırından bölmesine ve araya tire koymasına izin verir. Dil, element'in lang attribute'undan okunur; lang yoksa heceleme hiç yapılmaz. 9em genişliğinde dar kolonlarda İngilizce ve Türkçe bir cümleyi denedim; sayılar her satırın piksel genişliği:
.hyphen { inline-size: 9em; hyphens: auto; }
.hyphen.manual { hyphens: manual; }
--- chromium
hyphen lang=en 5 lines: 128 141 84 81 101
hyphen manual lang=en 5 lines: 128 141 58 97 101
hyphen lang=tr 5 lines: 158 103 60 114 137
hyphen manual lang=tr 5 lines: 158 103 60 114 137
hyphen manual shy lang=tr 5 lines: 134 137 60 114 137
--- webkit
hyphen lang=en 5 lines: 128 141 84 139 52
hyphen manual lang=en 5 lines: 128 141 58 97 101
hyphen lang=tr 5 lines: 158 103 60 114 137
hyphen manual lang=tr 5 lines: 158 103 60 114 137
hyphen manual shy lang=tr 5 lines: 134 137 60 114 137
İngilizce'de iki motor da heceliyor: manual ile üçüncü satır 58px'te kalırken auto ile "ex-" eklenip 84px'e çıkıyor. Hangi kelimenin nereden bölüneceği motora göre değişiyor; WebKit son iki satırı farklı dağıtmış. Türkçe'de ise iki motorda da auto ile manual arasında hiçbir fark yok ve ilk satır 158px ile 144px'lik kolondan taşıyor: "Kütüphanelerimizdeki" bölünmedi. browser-compat-data'ya göre Chrome Türkçe heceleme sözlüğüne sahip değil; Firefox ve Safari'de Türkçe destekli görünüyor. Playwright'ın WebKit build'i ise bu makinede Türkçe'yi hecelemedi; aynı build Almanca bir kelimeyi heceledi. Gerçek Safari'de ve Firefox'ta Türkçe'yi ayrıca deneyemedim.
hyphens property'si Mart 2026'dan beri widely available, ama bu yalnız property'nin kendisi için geçerli. Dil desteği tarayıcıdan tarayıcıya değişir ve Türkçe kullanıcıların büyük kısmının kullandığı Chrome'da yok. Türkçe metinde hyphens: auto'ya güvenme. Uzun kelimenin nereden bölüneceğini biliyorsan ­ (soft hyphen; yalnız satır o noktada kırılırsa görünen tire) ile işaretleyebilirsin; hyphens: manual zaten varsayılan değer:
<p class="hyphen manual shy" lang="tr">Kütüp­hane­lerimiz­deki karşılaştırmalı edebiyat koleksiyonlarını güncelleyemiyoruz.</p>
Son satırda ilk satır 134px'e indi ve kolona sığdı: "Kütüphanelerimiz-" yazılıp kalan kısım alta geçti. Kullanıcı içeriği gibi önceden bilinmeyen metinde ise güvence overflow-wrap: anywhere.
text-wrap: balance ve pretty
text-wrap, text-wrap-mode ile text-wrap-style'ın shorthand'i. Stil tarafında iki değer günlük işte işe yarıyor. balance satırları birbirine yakın uzunlukta dağıtır, pretty ise paragrafın sonunda tek bir kelimenin kalmasını önlemeye çalışır:
.title { inline-size: 20em; font-size: 24px; margin: 0; }
.title.balance { text-wrap: balance; }
.para { inline-size: 20em; }
.para.pretty { text-wrap: pretty; }
--- chromium
title line widths: 457 404 79
title balance line widths: 327 236 377
para line widths: 307 299 311 49
para pretty line widths: 307 299 290 70
--- webkit
para line widths: 307 299 311 49
para pretty line widths: 256 233 241 235
Uzun bir kitap adında balance olmadan son satırda tek başına "World" kalıyor (79px). balance ile en kısa satır 236px'e çıkıyor ve başlık üç dengeli satır gibi okunuyor; toplam genişlik değişmiyor, yalnız kırma noktaları değişiyor. Satır uzunluklarının tam eşit olmaması normal: kırma ancak kelime aralarında yapılabiliyor. İki motor bu başlıkta aynı sonucu verdi.
pretty'de motorlar ayrışıyor. Normal paragrafta son satırda tek kelime var (49px). Chromium pretty ile yalnız son satırları düzeltiyor ve son satıra bir kelime daha alıyor (70px); WebKit ise bütün paragrafı yeniden dağıtıyor ve dört satırı birbirine yaklaştırıyor. Spesifikasyon algoritmayı tarayıcıya bırakıyor, bu yüzden iki sonuç da geçerli. balance Mayıs 2024'ten beri Baseline newly available; pretty Chrome 117 ve Safari 26'da var, Firefox'ta yok, yani Baseline değil. Desteklemeyen tarayıcı bu değeri yok saydığı için progressive enhancement (eski tarayıcıda da çalışan bir temel üzerine destekleyen tarayıcıya ek iyileştirme koymak) olarak doğrudan yazılabilir. Bookstore'un base stiline şunu ekledik:
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
Chromium balance'ı performans nedeniyle belli bir satır sayısının altındaki bloklarla sınırlıyor; Chrome'un dokümanı bu sınırı 6 satır olarak veriyor. Uzun bir paragrafa balance yazmak bu yüzden ya hiçbir şey yapmaz ya da gereksiz maliyet getirir; onu başlıklara, etiketlere ve kısa alıntılara bırak.
Çok Satırlı Kesme: -webkit-line-clamp
Tek satırlık metni text-overflow: ellipsis ile kesmek CSS3 serisinin 03. yazısında anlatıldı. Kitap açıklamasını üç satırda kesip sonuna "…" koymak için ise hâlâ eski, prefix'li bir syntax kullanıyoruz:
.summary { inline-size: 20em; }
.clamp {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.clamp-block { -webkit-line-clamp: 3; overflow: hidden; }
--- chromium
summary clamp height 72px (line-height 24px)
summary clamp-block height 192px (line-height 24px)
supports: line-clamp false, -webkit-line-clamp true, text-wrap: pretty true, text-box true
--- webkit
summary clamp height 72px (line-height 24px)
summary clamp-block height 192px (line-height 24px)
supports: line-clamp false, -webkit-line-clamp true, text-wrap: pretty true, text-box true
.clamp kutusu 72px, yani 24px'lik üç satır. .clamp-block aynı -webkit-line-clamp: 3 değerini aldığı hâlde 192px ile sekiz satırın tamamını gösteriyor: -webkit-line-clamp yalnız display: -webkit-box ve -webkit-box-orient: vertical ile birlikte çalışıyor. Bu dörtlü kalıp, 2009'dan kalma eski flexbox syntax'ının bir parçası ama bugün üç motorda da tutarlı çalışan tek yol. Prefix'siz line-clamp hiçbir tarayıcıda yayında değil: iki motor da CSS.supports('line-clamp: 3') için false döndü, browser-compat-data'ya göre yalnız Chrome ve Firefox'un geliştirme sürümlerinde deneysel bir flag ile, Safari'de de yalnız Technology Preview'da var.
Bookstore'da özet paragrafı varsayılan olarak üç satırda kesiliyor ve altında bir "Read more" butonu var:
/* "Read more"a basılana kadar üç satır */
.book-summary.is-clamped {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
.review-text {
/* müşterinin satır sonlarını koru, fazla boşlukları birleştir */
white-space: pre-line;
/* uzun URL ve ISBN'ler herhangi bir yerden kırılabilir */
overflow-wrap: anywhere;
}
<p class="book-summary is-clamped" id="summary">Middlemarch follows the lives of the people of a small
English town in the years before the 1832 Reform Act: Dorothea Brooke, who marries an elderly
scholar, the young doctor Tertius Lydgate, who wants to reform medicine, and the families around
them, whose ambitions and mistakes slowly change the town. George Eliot published the novel in
eight parts in 1871 and 1872.</p>
<button type="button" class="read-more" aria-controls="summary" aria-expanded="false" hidden>Read more</button>
// "Read more" butonunu yalnız kesilen özet gerçekten metin gizliyorsa gösterir.
for (const button of document.querySelectorAll('.read-more')) {
const summary = document.getElementById(button.getAttribute('aria-controls'));
const update = () => {
if (summary.classList.contains('is-clamped')) {
button.hidden = summary.scrollHeight <= summary.clientHeight;
}
};
// stiller, font'lar ya da viewport genişliği layout'u değiştirince yeniden çalışır
new ResizeObserver(update).observe(summary);
button.addEventListener('click', () => {
const expanded = summary.classList.toggle('is-clamped') === false;
button.setAttribute('aria-expanded', String(expanded));
button.textContent = expanded ? 'Show less' : 'Read more';
});
}
Buton yalnız özet gerçekten kesildiğinde görünüyor: kesilen bir element'te scrollHeight gizlenen satırları da içerir ve clientHeight'tan büyük olur. Bu karşılaştırmayı ResizeObserver içinde yapmak önemli. İlk denememde kontrolü script çalıştığı anda yapmıştım ve WebKit'te buton gizli kalıyordu; o anda stil dosyaları ve font henüz yerine oturmamıştı. ResizeObserver, font değiştiğinde ya da ekran döndürüldüğünde de kontrolü tekrarlıyor.
node scripts/03-pages.mjs
--- chromium
clamped: height 76.78125px, button "Read more" aria-expanded=false
red ::first-letter pixels while clamped: 899
text in the DOM: 62 words
expanded: height 204.75px, button "Show less" aria-expanded=true
red ::first-letter pixels when expanded: 899
review: 4 lines, scrollWidth 372 = clientWidth 372
--- webkit
clamped: height 76.78125px, button "Read more" aria-expanded=false
red ::first-letter pixels while clamped: 0
text in the DOM: 62 words
expanded: height 205px, button "Show less" aria-expanded=true
red ::first-letter pixels when expanded: 70
review: 4 lines, scrollWidth 372 = clientWidth 372
Ölçüm birkaç şeyi gösteriyor. Kesilmiş özet 76.8px, açıldığında 205px; buton aria-expanded değerini doğru güncelliyor. Kesilmiş hâlde de metnin 62 kelimesinin tamamı DOM'da (Document Object Model; tarayıcının sayfayı temsil eden element ağacı) duruyor: ekran okuyucular ve arama motorları tam metni okur, yalnız görsel olarak kesilir. Yorum metni 4 satır ve scrollWidth clientWidth'e eşit, yani uzun URL taşmıyor.
Beklemediğim bir motor farkı da çıktı. Önceki yazıda özet paragrafının ilk harfini ::first-letter ile kırmızı yapmıştık. Kesilmiş hâlde Chromium bu harfi çiziyor (899 kırmızı piksel), WebKit ise hiç çizmiyor (0); özet açılıp display: -webkit-box kalkınca WebKit'te de harf kırmızıya dönüyor. WebKit, -webkit-box kutusunda ::first-letter uygulamıyor. Clamp'li bir element'e başka pseudo-element dekorasyonu koyacaksan iki motorda da kontrol et.
text-box: Butonda Dikey Hizalamaya Kısa Bir Bakış
Bir butonun içindeki metin, font'un ascent ve descent alanı ile line-height'ın eklediği boşluk yüzünden çoğu zaman tam ortada durmuyor: büyük harflerin üstünde altından daha fazla boşluk kalır. text-box, satır kutusunun üst ve altındaki bu fazlalığı font'un metriklerine göre kırpar:
.btn { font: 600 16px/1.5 system-ui, sans-serif; padding: 0.5em 1em; border: 0; }
.btn.trim { text-box: trim-both cap alphabetic; }
btn height 40.0px
btn trim height 27.3px
trim-both cap alphabetic üstü büyük harf yüksekliğine, altı baseline'a kırpıyor; buton 40px'ten 27.3px'e indi ve padding artık görünen harflere göre ölçülüyor. Tasarım araçlarındaki "metin kutusunu harfe göre kırp" ayarının CSS karşılığı bu. Destek durumu karışık: web-features 3.46'da text-box Baseline değil. browser-compat-data property'leri Firefox 154'te (Ağustos 2026) gösteriyor, ama cap ve alphabetic gibi değer kayıtlarında Firefox desteği işaretli olmadığı için özellik bütün olarak Baseline sayılmıyor. Firefox'u bu makinede çalıştıramadığım için kendim doğrulayamadım; desteklemeyen tarayıcıda buton yalnız biraz daha yüksek görünür, bu yüzden progressive enhancement olarak güvenle yazılabilir.
Üretimde Dikkat
Satır kırma kurallarının sonuçları içeriğe bağlı ve test verisi çoğu zaman kısa ve düzgündür. Gerçek veriyi temsil eden uç örnekleri (çok uzun kitap adı, boşluksuz URL, uzun Türkçe kelime, satır sonu içeren yorum) bir test sayfasına koyup her değişiklikte bu yazıdaki gibi satır sayısını ve scrollWidth'i ölç. Bir kartın taşması çoğu zaman production'da, bir müşterinin yazdığı yorumla fark edilir.
text-wrap: balance ve pretty layout hesabına ek iş katar. Başlıklarda bu ihmal edilebilir; uzun listelerde ve sık güncellenen içerikte pretty'yi bütün paragraflara uygulamadan önce DevTools'un Performance panelinde layout süresine bak.
Kesilen metnin erişilebilir olması için tam metin DOM'da kalmalı (bizde kalıyor) ve "Read more" gerçek bir button olmalı. Kesme işini metni sunucuda karakter sayısıyla kısaltarak yaparsan tam metne ulaşmanın tek yolu başka bir istek olur.
Sık Düşülen Tuzaklar
Birincisi, text-wrap: balance'ı uzun paragraflara uygulamak. Chromium belli bir satır sayısından sonra dengelemeyi bırakır, yani beklenen sonuç gelmez ve maliyet boşa gider.
İkincisi, line clamp ile kesilen metnin tam hâline ulaşmanın bir yolunu bırakmamak. Görsel olarak kesilen metin klavye ve fare kullanıcısı için kaybolmuş olur; açma butonu ve aria-expanded ekle.
Üçüncüsü, flex item içinde taşan metni satır kırma sorunu sanmak. Flex item'ın varsayılan min-width: auto değeri item'ın içeriğinden daha dar olmasına izin vermez; overflow-wrap: break-word bu durumda yardımcı olmaz, item'a min-width: 0 vermek ya da anywhere kullanmak gerekir. Mekanizması CSS3 serisinin 05. yazısında.
Dördüncüsü, pre-wrap verdiğin bir element'in metnini template'te etiketten sonra yeni bir satıra ve girintiyle yazmak. Template'teki satır sonu ve girinti de korunur ve yorumun başında boş bir satır olarak görünür; metni açılış etiketinin hemen arkasına yaz.
Baseline Durumu
node scripts/baseline.mjs white-space white-space-collapse text-wrap overflow-wrap word-break hyphens text-wrap-balance text-wrap-pretty line-clamp text-box text-overflow
white-space widely 2018-01-29 Chrome 1, Firefox 1, Safari 1, iOS 1
white-space-collapse widely 2026-09-19 Chrome 114, Firefox 124, Safari 17.4, iOS 17.4
text-wrap newly 2024-10-17 Chrome 130, Firefox 124, Safari 17.5, iOS 17.5
overflow-wrap widely 2021-04-02 Chrome 23, Firefox 49, Safari 7, iOS 7
word-break widely 2018-03-30 Chrome 44, Firefox 15, Safari 9, iOS 9
hyphens widely 2026-03-18 Chrome 88, Firefox 43, Safari 17, iOS 17
text-wrap-balance newly 2024-05-13 Chrome 114, Firefox 121, Safari 17.5, iOS 17.5
text-wrap-pretty not Baseline Chrome 117, Firefox -, Safari 26, iOS 26
line-clamp not Baseline Chrome -, Firefox -, Safari -, iOS -
text-box not Baseline Chrome 133, Firefox -, Safari 18.2, iOS 18.2
text-overflow widely 2018-01-29 Chrome 1, Firefox 7, Safari 1.3, iOS 1
| Özellik | Durum | Not |
|---|---|---|
white-space, overflow-wrap, word-break, text-overflow | Widely available | anywhere değeri de üç motorda var |
white-space-collapse | Widely available | 2026-09'dan beri |
hyphens | Widely available | Türkçe sözlük Chrome'da yok |
text-wrap, text-wrap: balance | Newly available | Başlıklar için |
text-wrap: pretty | Baseline değil | Firefox yok; progressive enhancement |
Prefix'siz line-clamp | Baseline değil | Yayında değil; -webkit-line-clamp kalıbını kullan |
text-box | Baseline değil | Chrome ve Safari; Firefox verisi eksik |
Küçük Bir Deneme
★☆☆ Lab'deki .fit kutularını bir flex container içinde 200px genişliğe zorla (.row'a inline-size: 200px ver) ve iki kutunun genişliğinin nasıl değiştiğini 03-wrap.mjs ile ölç.
★★☆ Kitap detayında yorum metnini pre-wrap yap ve metni template'te bir alt satırdan başlat. Tuzaklar bölümündeki boş satırı ölçümle göster, sonra pre-line ile farkı karşılaştır.
★★★ "Read more" butonunu details/summary ile, hiç JavaScript yazmadan yapmayı dene. Kesilmiş ve açık hâlde metnin DOM'da ve accessibility tree'de görünür olup olmadığını Playwright'ın ariaSnapshot() method'u ile karşılaştır.
Bir sonraki yazıda metinden kutunun görünümüne geçiyoruz: background longhand'leri ve shorthand'in sıfırladıkları, background-size ve position, çoklu katman, linear, radial ve conic gradient'ler, image-set() ve kampanya banner'ı.