renderNG ayrıntılı incelemesi: LayoutNG blok parçalanması

Morten Stenshorne
Morten Stenshorne

Blok parçalara bölme, CSS blok düzeyindeki bir kutuyu (bölüm veya paragraf gibi) bir bütün olarak parça kapsayıcısı adı verilen bir parça kapsayıcısının içine sığmadığında birden fazla parçaya bölme işlemidir. Parçalayıcı bir öğe değildir ancak çok sütunlu düzende bir sütunu veya sayfalı medyada bir sayfayı temsil eder.

Parçalanmanın gerçekleşmesi için içeriğin bir parçalanma bağlamında olması gerekir. Parçalanma bağlamı genellikle çok sütunlu bir kapsayıcı (içerik sütunlara bölünür) veya yazdırma işlemi (içerik sayfalara bölünür) tarafından oluşturulur. Çok sayıda satır içeren uzun bir paragrafın birden fazla parçaya bölünmesi gerekebilir. Böylece ilk satırlar ilk parçaya, kalan satırlar ise sonraki parçalara yerleştirilir.

İki sütuna bölünmüş bir metin paragrafı.
Bu örnekte, çok sütunlu düzen kullanılarak bir paragraf iki sütuna bölünmüştür. Her sütun, parçalanmış akıştaki bir parçayı temsil eden bir parçalayıcıdır.

Blok parçalara ayırma, iyi bilinen başka bir parçalara ayırma türüne benzer: satır parçalara ayırma (başka bir deyişle "satır bölme"). Birden fazla kelimeden oluşan (herhangi bir metin düğümü, herhangi bir <a> öğesi vb.) ve satır aralarına izin veren tüm satır içi öğeler birden fazla parçaya bölünebilir. Her bir parça farklı bir satır kutusuna yerleştirilir. Satır kutusu, sütunlar ve sayfalar için parçalayıcı eşdeğeri olan satır içi parçalamadır.

LayoutNG blok parçalanması

LayoutNGBlockFragmentation, LayoutNG için parçalama motorunun yeniden yazılmış halidir ve ilk olarak Chrome 102'de kullanıma sunulmuştur. Veri yapıları açısından, NG öncesi birden fazla veri yapısı doğrudan parça ağacında temsil edilen NG parçalarıyla değiştirildi.

Örneğin, artık "break-before" ve "break-after" CSS özellikleri için "avoid" değerini destekliyoruz. Bu değer, yazarların bir başlıktan hemen sonra boşluk bırakmamasını sağlar. Bir sayfada son öğe bir başlık olduğunda ve bölümün içeriği bir sonraki sayfada başladığında bu genellikle garip görünür. Başlıktan önce ara vermeniz daha iyidir.

Başlık hizalaması örneği.
Şekil 1. İlk örnekte başlık sayfanın alt kısmında, ikinci örnekte ise ilişkili içeriğiyle birlikte bir sonraki sayfanın üst kısmında gösterilmektedir.

Chrome, parçalanmayı taşmayı da destekler. Böylece, tek parça (kırılmaz olması gereken) içerik birden fazla sütuna bölünmez ve gölgeler ile dönüştürme işlemleri gibi boyama efektleri doğru şekilde uygulanır.

LayoutNG'de blok parçalandırması tamamlandı

Temel parçalara ayırma (satır düzeni, yüzen öğeler ve akış dışı konumlandırma dahil olmak üzere blok kapsayıcılar) Chrome 102'de kullanıma sunulmuştur. Esnek ve ızgara parçalandırması Chrome 103'te, tablo parçalandırması ise Chrome 106'ta kullanıma sunulmuştur. Son olarak, Chrome 108'de yazdırma özelliği kullanıma sunuldu. Blok parçalama, düzen oluşturmak için eski motora bağlı olan son özellikti.

Chrome 108'den itibaren eski motor, sayfa düzenini oluşturmak için artık kullanılmamaktadır.

Ayrıca LayoutNG veri yapıları boyama ve isabet testi işlemlerini destekler ancak offsetLeft ve offsetTop gibi düzen bilgilerini okuyan JavaScript API'leri için bazı eski veri yapılarını kullanırız.

Her şeyi NG ile tasarlamak, yalnızca LayoutNG uygulamalarının olduğu (eski motor eşdeğeri olmayan) yeni özellikleri (ör. CSS kapsayıcı sorguları, ankraj konumlandırma, MathML ve özel düzen (Houdini)) uygulamayı ve yayınlamayı mümkün kılar. Kapsayıcı sorguları için bu özelliği biraz önce kullanıma sunduk ve geliştiricilere baskının henüz desteklenmediğine dair bir uyarı gönderdik.

LayoutNG'nin ilk bölümünü 2019'da kullanıma sunduk. Bu bölümde normal blok kapsayıcı düzeni, satır içi düzen, yüzen öğeler ve akış dışı konumlandırma yer alıyordu. Ancak esnek, ızgara veya tablolar için destek yoktu ve blok parçalama için hiç destek yoktu. Esnek, ızgara, tablolar ve blok parçalanmasını içeren her şey için eski düzen motorunu kullanmaya geri döneriz. Bu durum, parçalanmış içerikteki blok, satır içi, yüzen ve akış dışı öğeler için bile geçerliydi. Gördüğünüz gibi, bu kadar karmaşık bir düzen motorunu yerinde yükseltmek çok hassas bir işlemdir.

Ayrıca, 2019'un ortalarına kadar LayoutNG blok parçalama düzeninin temel işlevlerinin çoğu zaten uygulanmıştı (bir işaretin arkasında). Peki, gönderim neden bu kadar uzun sürdü? Kısaca yanıt vermek gerekirse: Parçalanmanın, sistemin çeşitli eski bölümleriyle doğru şekilde birlikte çalışması gerekir. Bu bölümler, tüm bağımlılıklar yükseltilene kadar kaldırılamaz veya yükseltilemez.

Eski motor etkileşimi

Eski veri yapıları, düzen bilgilerini okuyan JavaScript API'lerinden hâlâ sorumludur. Bu nedenle, verileri eski motora anlayabileceği bir şekilde geri yazmamız gerekir. Bu, LayoutMultiColumnFlowThread gibi eski çok sütunlu veri yapılarının doğru şekilde güncellenmesini de içerir.

Eski motor yedek algılama ve işleme

İçerisinde LayoutNG blok parçalandırması tarafından henüz işlenemeyen içerikler olduğunda eski düzen motoruna geri dönmek zorunda kaldık. Gönderim sırasında temel LayoutNG blok parçalanması (flex, ızgara, tablolar ve basılı olan her şey dahil) Bu, düzenleme ağacında nesneler oluşturmadan önce eski yedekleme ihtiyacını tespit etmemiz gerektiğinden özellikle zordu. Örneğin, çok sütunlu bir kapsayıcı üst öğesi olup olmadığını ve hangi DOM düğümlerinin biçimlendirme bağlamı olacağını bilmeden önce bunu tespit etmemiz gerekiyordu. Bu, mükemmel bir çözümü olmayan bir "tavuklar ve yumurtalar" sorunudur. Ancak tek yanlış davranışı yanlış pozitifler olduğu sürece (gerçekten ihtiyaç olmadığında eski sürüme geri dönme) sorun yoktur. Çünkü bu düzen davranışındaki tüm hatalar, yeni değil, Chromium'da zaten mevcut olan hatalar olduğundan.

Boya öncesi ağaç yürüyüşü

Boya öncesi işlem, düzenlemeden sonra ancak boyamadan önce yaptığımız bir işlemdir. Temel zorluk, düzen nesnesi ağacında gezinmeye devam etmemiz gerektiği halde artık NG parçalarımız olmasıdır. Peki bu sorunu nasıl çözeriz? Hem düzen nesnesini hem de NG fragment ağaçlarını aynı anda gezeriz. İki ağaç arasında eşleme yapmak kolay olmadığı için bu işlem oldukça karmaşıktır.

Düzen nesne ağacı yapısı, DOM ağacına çok benzese de parça ağacı, düzenin girişi değil çıktısıdır. Satır içi parçalama (satır parçaları) ve blok parçalama (sütun veya sayfa parçaları) dahil olmak üzere tüm parçalamaların etkisini yansıtmanın yanı sıra, parça ağacı, içeren blok ile bu parçayı içeren blok olarak kullanan DOM alt öğeleri arasında doğrudan bir üst öğe-alt öğe ilişkisine de sahiptir. Örneğin, kesin konumlandırılmış bir öğe tarafından oluşturulan bir parça, akış dışı konumlandırılmış alt öğe ile kapsayıcı bloğu arasında soy zincirinde başka düğümler olsa bile, parça ağacında kapsayıcı blok parçasının doğrudan alt öğesidir.

Parçalandırma içinde akış dışı konumlandırılmış bir öğe olduğunda durum daha da karmaşık olabilir. Bu durumda akış dışı parçalar, parçalandırıcının doğrudan alt öğeleri olur (CSS'nin kapsayıcı blok olarak düşündüğü öğenin alt öğesi değil). Bu, eski motorla birlikte var olabilmesi için çözülmesi gereken bir sorundu. LayoutNG, tüm modern düzen modlarını esnek bir şekilde desteklemek için tasarlandığından gelecekte bu kodu basitleştirebileceğiz.

Eski parçalama motoruyla ilgili sorunlar

Web'in daha önceki bir döneminde tasarlanan eski motorda, o zamanlar teknik olarak da olsa (yazdırmayı desteklemek için) parçalanma kavramı yoktur. Parçalara ayırma desteği, yalnızca üst kısımda sabitlenmiş (baskı) veya sonradan eklenmiş (çok sütunlu) bir özellikti.

Eski motor, parçalara ayrılabilir içeriği düzenlerken her şeyi genişliği bir sütunun veya sayfanın satır içi boyutu, yüksekliği ise içeriğini barındırmak için gereken kadar olan uzun bir şerit halinde düzenler. Bu uzun şerit sayfaya oluşturulmaz. Bunun yerine, sanal bir sayfaya oluşturulur ve daha sonra nihai görüntüleme için yeniden düzenlenir. Bu işlem, bir gazete makalesinin tamamını tek bir sütunda basıp ardından makaleyi makasla birden fazla sütuna kesmeye benzer. (Eskiden bazı gazeteler buna benzer teknikler kullanıyordu.)

Eski motor, şeritteki hayali bir sayfa veya sütun sınırını izler. Bu sayede, sınırın dışına taşmayan içeriği sonraki sayfaya veya sütuna kaydırabilirsiniz. Örneğin, bir satırın yalnızca üst yarısı, motorun mevcut sayfa olduğunu düşündüğü yere sığıyorsa motor, satırın üst yarısını bir sonraki sayfanın üst kısmının olduğu varsayılan konuma itmek için bir "sayfalandırma desteği" ekler. Ardından, gerçek parçalara ayırma işleminin çoğu ("makasla kesme ve yerleştirme"), sayfa düzeninden sonra, ön boyama ve boyama sırasında gerçekleşir. Bu işlemde, uzun içerik şeridinin sayfalara veya sütunlara bölünmesi (bölümlerin kırpılması ve çevrilmesi yoluyla) yapılır. Bu durum, dönüştürme ve göreli konumlandırma işlemlerinin parçalanmanın sonrasında uygulanması (özelliğin gerektirdiği şey budur) gibi bazı işlemlerin yapılmasını imkansız hale getiriyordu. Ayrıca, eski motorda tablo parçalara ayırma için bazı destekler olsa da esnek veya ızgara parçalara ayırma desteği hiç yoktur.

Makas, yerleşim ve yapıştırıcı kullanılmadan önce üç sütunlu bir düzenin eski motorda dahili olarak nasıl temsil edildiğini gösteren bir resim aşağıda verilmiştir (Yalnızca dört satır sığması için belirli bir yüksekliğe sahibiz ancak altta biraz fazladan alan var):

İçeriğin bölündüğü sayfalandırma destekleriyle tek sütun olarak dahili temsil ve üç sütun olarak ekranda temsil

Eski düzen motoru, düzen sırasında içeriği parçalara ayırmadığı için göreceli konumlandırma ve dönüştürme işlemlerinin yanlış uygulanması ve kutu gölgelerinin sütun kenarlarında kırpılması gibi birçok garip yapı ortaya çıkar.

text-shadow içeren bir örneği aşağıda bulabilirsiniz:

belgesine bakın.

Eski motor bu durumu iyi şekilde ele almaz:

İkinci sütuna yerleştirilen kırpılmış metin gölgeleri.

İlk sütundaki satırdaki metin gölgesinin nasıl kırpıldığını ve bunun yerine ikinci sütunun üst kısmına yerleştirildiğini görüyor musunuz? Bunun nedeni, eski düzen motorunun parçalanmayı anlamamasıdır.

Aşağıdaki gibi görünmelidir:

Gölgelerin doğru şekilde gösterildiği iki sütun metin.

Ardından, dönüşümler ve box-shadow ile bunu biraz daha karmaşık hale getirelim. Eski motorda yanlış kırpma ve sütun taşması olduğunu fark edin. Bunun nedeni, dönüşümlerin spesifikasyona göre düzen sonrası, parçalanma sonrası bir efekt olarak uygulanması gerektiğidir. LayoutNG parçalandırması ile her ikisi de düzgün çalışır. Bu, Firefox ile birlikte çalışabilirliği artırır. Firefox, bir süredir bu alandaki çoğu testin de geçtiği iyi bir parçalanma desteğine sahiptir.

belgesine bakın.

Kutular iki sütuna yanlış şekilde bölünmüş.

Eski motor, yüksek monolitik içeriklerle ilgili de sorunlara sahiptir. Birden fazla parçaya bölünmeye uygun olmayan içerikler monolitik olarak kabul edilir. Taşma kaydırma özelliğine sahip öğeler tek parçadır. Bunun nedeni, kullanıcıların dikdörtgen olmayan bir alanda kaydırmasının anlamlı olmamasıdır. Satır kutuları ve resimler de tek parça içeriklere örnek gösterilebilir. Aşağıda bununla ilgili bir örnek verilmiştir:

adlı kaleme göz atın.

Monolitik içerik parçası bir sütuna sığmayacak kadar uzunsa eski motor bunu kabaca dilimlere ayırır (kaydırılabilir kapsayıcıyı kaydırmaya çalışırken çok "ilginç" davranışlara neden olur):

İlk sütunu taşmasına izin vermek yerine (LayoutNG blok parçalanmasında olduğu gibi):

ALT_TEXT_HERE

Eski motor, zorunlu araları destekler. Örneğin, <div style="break-before:page;">, DIV'den önce bir sayfa sonu ekler. Ancak en uygun zorunlu olmayan araları bulmak için yalnızca sınırlı destek sunar. break-inside:avoid ve yalnız satır sonları ve yalnız satır başlıkları desteklenir ancak örneğin break-before:avoid aracılığıyla istenirse bloklar arasında boşluk bırakılmaması desteklenmez. Aşağıdaki örneğe bakın:

kalemine bakın.

Metin iki sütuna ayrılmış.

Burada #multicol öğesi, her sütunda 5 satıra (100 piksel yüksekliğinde ve satır yüksekliği 20 piksel olduğu için) sahiptir. Bu nedenle #firstchild öğesinin tamamı ilk sütuna sığabilir. Ancak kardeşi #secondchild'te break-before:avoid var. Bu, içeriğin aralarında ara verilmemesini istediği anlamına gelir. widows değeri 2 olduğundan, tüm ara verme isteğini dikkate almak için ikinci sütuna 2 satır #firstchild göndermemiz gerekir. Chromium, bu özellik kombinasyonunu tam olarak destekleyen ilk tarayıcı motorudur.

NG parçalanmasının işleyiş şekli

NG düzen motoru, genellikle CSS kutu ağacını derinlik öncelikli olarak dolaşarak belgeyi düzenler. Bir düğümün tüm alt öğeleri düzenlendiğinde, NGPhysicalFragment oluşturarak ve üst öğe düzen algoritmasına dönerek söz konusu düğümün düzeni tamamlanabilir. Bu algoritma, parçayı alt parça listesine ekler ve tüm alt parçalar tamamlandığında, tüm alt parçalarını içeren bir parça oluşturur. Bu yöntemle, dokümanın tamamı için bir parça ağacı oluşturur. Ancak bu, çok basitleştirilmiş bir açıklamadır: Örneğin, akış dışında konumlandırılmış öğelerin düzenlenebilmesi için DOM ağacında bulundukları yerden kapsayıcı bloklarına doğru yukarı doğru hareket etmeleri gerekir. Basitlik açısından bu gelişmiş ayrıntıyı göz ardı ediyoruz.

LayoutNG, CSS kutusunun yanı sıra bir düzen algoritmasına kısıtlama alanı sağlar. Bu sayede algoritmaya, sayfa düzeni için kullanılabilecek alan, yeni bir biçimlendirme bağlamı oluşturulup oluşturulmadığı ve önceki içerikten elde edilen ara kenar boşluğu daraltma sonuçları gibi bilgiler sağlanır. Kısıtlama alanı, parçalayıcının düzenlenmiş blok boyutunu ve mevcut blok ofsetini de bilir. Bu, nerede ara vereceğinizi gösterir.

Blok parçalanması söz konusu olduğunda, alt öğelerin düzeninin bir ara verme noktasında durması gerekir. Sayfada veya sütunda yer kalmaması ya da zorunlu bir ara verme, ara verme nedenlerinden bazılarıdır. Ardından, ziyaret ettiğimiz düğümler için parçalar oluşturur ve parçalama bağlam köküne (çoklu sütun kapsayıcısı veya baskı durumunda belge kökü) kadar geri döneriz. Ardından, parçalama bağlamı kökünde yeni bir parçalayıcıya hazırlanır ve ara vermeden önce kaldığımız yerden devam etmek için ağaca tekrar ineriz.

Aradan sonra düzeni devam ettirme olanağı sağlayan önemli veri yapısına NGBlockBreakToken adı verilir. Sonraki parçalayıcıda düzeni doğru şekilde devam ettirmek için gereken tüm bilgileri içerir. NGBlockBreakToken, bir düğümle ilişkilendirilir ve devam ettirilmesi gereken her düğümün temsil edilmesi için bir NGBlockBreakToken ağacı oluşturur. İçinde kırılan düğümler için oluşturulan NGPhysicalBoxFragment'e bir NGBlockBreakToken eklenir. Ara karakter jetonları, ara karakter jetonu ağacı oluşturacak şekilde üst öğelere dağıtılır. Bir düğümün öncesinde (içinde değil) ara vermemiz gerekirse hiçbir parça oluşturulmaz ancak üst düğümün, düğüm için bir "break-before" ara verme jetonu oluşturması gerekir. Böylece, sonraki parça kapsayıcısında düğüm ağacında aynı konuma geldiğimizde ara vermeyi başlatabiliriz.

Parçalayıcı alanında yer kalmadığında (zorunlu olmayan ara) veya zorunlu ara istendiğinde aralar eklenir.

Spesifikasyonda, zorunlu olmayan en uygun aralar için kurallar vardır ve tam olarak yer kalmadığı yerde ara eklemek her zaman doğru bir işlem değildir. Örneğin, break-before gibi çeşitli CSS özellikleri, ara yeri seçimini etkiler.

Sayfa düzeni sırasında zorunlu olmayan aralar spesifikasyon bölümünü doğru şekilde uygulamak için olası iyi kesme noktalarını takip etmemiz gerekir. Bu kayıt, ara vermeden kaçınma isteklerini ihlal ettiğimiz bir noktada (örneğin, break-before:avoid veya orphans:7) yer kalmaması durumunda geri dönüp bulunan en iyi son ara verme noktasını kullanabileceğimiz anlamına gelir. Her olası ara verme noktasına, "yalnızca son çare olarak yapın" ile "ara vermek için mükemmel yer" arasında değişen bir puan verilir. Bir ara verme konumu "mükemmel" olarak puanlanırsa orada ara verdiğimizde hiçbir ara verme kuralı ihlal edilmez (ve bu puanı tam olarak yerimizin tükendiği noktada alırsak daha iyi bir yer aramaya gerek yoktur). Puan "son çare" ise kesme noktası geçerli bile değildir ancak daha iyi bir şey bulamazsak parçalayıcı taşmasını önlemek için yine de orada ara verebiliriz.

Geçerli kesme noktaları genellikle yalnızca kardeşler (satır kutuları veya bloklar) arasında bulunur, örneğin bir üst öğe ile ilk alt öğesi arasında bulunmaz (C sınıfı kesme noktaları istisnadır ancak burada bunlar hakkında konuşmamız gerekmez). Örneğin, break-before:avoid içeren bir blok kardeşinden önce geçerli bir kesme noktası var ancak bu nokta "mükemmel" ile "son çare" arasında bir yerdedir.

Sayfa düzeni sırasında, NGEarlyBreak adlı bir yapıda şimdiye kadar bulunan en iyi kesme noktasını izleriz. Erken ara verme, bir blok düğümünden önce veya içinde ya da bir satırdan (blok kapsayıcı satırı veya esnek satır) önce olası bir kesme noktasıdır. En iyi kesme noktasının, alanımız tükendiğinde daha önce gözden kaçırdığımız bir yerin derinliklerinde olması ihtimaline karşı NGEarlyBreak nesnelerinden bir zincir veya yol oluşturabiliriz. Aşağıda bununla ilgili bir örnek verilmiştir:

.

Bu durumda, #second öğesinden hemen önce yerimiz bitiyor ancak "break-before:avoid" değeri olduğu için "break avoid'i ihlal ediyor" şeklinde bir ara verme konumu puanı alıyoruz. Bu noktada, "#outer içinde > #middle içinde > #inner içinde > "3. satır"dan önce" şeklinde bir NGEarlyBreak zincirimiz var. "Mükemmel" değerini aldığı için burada ara vermeyi tercih ederiz. Bu nedenle, #inner içindeki "3. satır"dan önce ara verebilmemiz için layout'ı #outer'ın başından döndürüp yeniden çalıştırmamız (ve bu kez bulduğumuz NGEarlyBreak'i iletmemiz) gerekir. ("3. satır"dan önce ara veriyoruz. Böylece kalan 4 satır bir sonraki parçalayıcıya yerleştirilir ve widows:4 dikkate alınır.)

Algoritma, kuralları doğru sırayla bırakarak her zaman mümkün olan en iyi kesme noktasında (özellikte tanımlandığı şekilde) durma veya tüm kurallar karşılanamazsa durma şeklinde tasarlanmıştır. Parçalama akışı başına en fazla bir kez yeniden düzenlememiz gerektiğini unutmayın. İkinci düzen geçişine geldiğimizde en iyi ara verme konumu düzen algoritmalarına zaten iletilmiştir. Bu, ilk düzen geçişinde keşfedilen ve ilgili turda düzen çıkışı kapsamında sağlanan ara verme konumudur. İkinci düzen geçişinde, yerimiz dolana kadar düzen oluşturmayız. Aslında yerimizin dolması beklenmez (bu aslında bir hata olur). Çünkü gereksiz yere kuralları ihlal etmemek için erken bir ara eklemek üzere süper güzel (mevcut olan en güzel) bir yer sağlanmıştır. Bu noktaya kadar planımızı oluşturuyoruz ve ara veriyoruz.

Bu bağlamda, bazen parçalayıcı taşmasını önlemeye yardımcı oluyorsa ara verme isteklerinin bazılarını ihlal etmemiz gerekir. Örneğin:

belgesine bakın.

Burada, #second'ten hemen önce yerimiz doldu ancak "break-before:avoid" değeri var. Bu, son örnekte olduğu gibi "break avoid'i ihlal ediyor" olarak çevrilir. Ayrıca "boş satır ve tek satırdan oluşan paragraf ihlali" (#first içinde > "2. satır"dan önce) içeren bir NGEarlyBreak'imiz de var. Bu, hâlâ mükemmel olmasa da "boş satır ve tek satırdan oluşan paragraf ihlali"nden daha iyidir. Bu nedenle, "2. satır"dan önce ara vereceğiz ve tek satırda kalan satırlar / tek satırda kalan sütunlar isteğini ihlal edeceğiz. Bu konu, spesifikasyonun 4.4. Zorunlu olmayan aralar: Parçalayıcı taşmasını önlemek için yeterli kesme noktamız yoksa önce hangi ara verme kurallarının yoksayıldığını tanımlar.

Sonuç

LayoutNG blok parçalama projesinin işlevsel hedefi, eski motorun desteklediği her şeyin ve hata düzeltmelerinin dışında mümkün olduğunca az şeyin LayoutNG mimarisini destekleyen bir şekilde uygulanmasını sağlamaktı. Bunun başlıca istisnası, daha iyi ara verme desteğidir (örneğin, break-before:avoid). Bu, parçalama motorunun temel bir parçası olduğundan, daha sonra eklenmesi başka bir yeniden yazma anlamına geleceği için baştan eklenmelidir.

LayoutNG blok parçalandırması tamamlandığından, yazdırma sırasında karışık sayfa boyutlarını destekleme, @page yazdırma sırasında kenar boşluğu kutuları box-decoration-break:clone ve daha fazlası gibi yeni işlevler eklemeye başlayabiliriz. Genel olarak LayoutNG'de olduğu gibi, yeni sistemin hata oranının ve bakım yükünün zaman içinde önemli ölçüde azalmasını bekliyoruz.

Teşekkür ederiz