Chrome Geliştirici Araçları'nın Stiller sekmesindeki CSS özelliklerinin son zamanlarda biraz daha şık göründüğünü fark ettiniz mi? Chrome 121 ile 128 arasında kullanıma sunulan bu güncellemeler, CSS değerlerini ayrıştırma ve sunma şeklimizdeki önemli bir iyileştirmenin sonucudur. Bu makalede, normal ifade eşleştirme sisteminden daha sağlam bir ayrıştırıcıya geçişle ilgili teknik ayrıntıları ele alacağız.
Mevcut Geliştirici Araçları'nı önceki sürümle karşılaştıralım:

Arada epey fark var, değil mi? Önemli geliştirmelerin dökümünü aşağıda bulabilirsiniz:
color-mix.color-mixişlevindeki iki renk bağımsız değişkenini görsel olarak temsil eden kullanışlı bir önizleme.pink.pinkadlı renk için tıklanabilir bir renk önizlemesi. Kolayca ayarlama yapmak için tıklayarak renk seçiciyi açabilirsiniz.var(--undefined, [fallback value]). Tanımlanmamış değişkenlerin işlenmesi iyileştirildi. Tanımlanmamış değişken gri renkte gösterilir ve etkin yedek değer (bu örnekte bir HSL rengi), tıklanabilir bir renk önizlemesiyle gösterilir.hsl(…):hslrenk işlevi için tıklanabilir başka bir renk önizlemesi. Renk seçiciye hızlı erişim sağlar.177deg: Açı değerini etkileşimli olarak sürükleyip değiştirmenize olanak tanıyan tıklanabilir bir açı saati.var(--saturation, …): Özel nitelik tanımına yönlendiren tıklanabilir bir bağlantı. Bu bağlantı, ilgili beyana kolayca gitmenizi sağlar.
Aradaki fark çarpıcı. Bunu başarmak için Geliştirici Araçları'na CSS özellik değerlerini eskisinden çok daha iyi anlamayı öğretmemiz gerekiyordu.
Bu önizlemeler zaten mevcut değil miydi?
Bu önizleme simgeleri tanıdık gelse de her zaman tutarlı bir şekilde gösterilmiyordu. Özellikle yukarıdaki örnekte olduğu gibi karmaşık CSS söz dizimlerinde bu durum daha belirgindi. Çalıştıkları durumlarda bile, doğru şekilde çalışabilmeleri için genellikle önemli ölçüde çaba sarf edilmesi gerekiyordu.
Bunun nedeni, değerleri analiz eden sistemin Geliştirici Araçları'nın ilk günlerinden beri organik olarak büyümesidir. Ancak CSS'den aldığımız son muhteşem yeni özelliklere ve buna bağlı olarak dil karmaşıklığındaki artışa ayak uyduramadı. Sistemin gelişmelere ayak uydurabilmesi için tamamen yeniden tasarlanması gerekiyordu ve biz de tam olarak bunu yaptık.
CSS mülk değerlerinin işlenme şekli
Geliştirici Araçları'nda, Stiller sekmesinde özellik bildirimlerini oluşturma ve süsleme süreci iki farklı aşamaya ayrılır:
- Yapısal analiz. Bu ilk aşamada, özellik beyanı incelenerek temel bileşenleri ve bunların ilişkileri belirlenir. Örneğin,
border: 1px solid redbildiriminde1pxdeğerini uzunluk,soliddeğerini dize vereddeğerini renk olarak tanır. - Oluşturuluyor. Yapısal analize dayalı olarak oluşturma aşaması, bu bileşenleri HTML gösterimine dönüştürür. Bu sayede, gösterilen tesis metni etkileşimli öğeler ve görsel ipuçlarıyla zenginleştirilir. Örneğin,
redrenk değeri, tıklandığında kolayca değiştirilebilmesi için bir renk seçici gösteren tıklanabilir bir renk simgesiyle oluşturulur.
Normal ifadeler
Daha önce, yapısal analiz için özellik değerlerini incelemek amacıyla normal ifadelerden (regex) yararlanıyorduk. Süslemeyi düşündüğümüz özellik değerlerinin bitleriyle eşleşen normal ifadelerin bir listesini tuttuk. Örneğin, CSS renkleri, uzunlukları, açıları, var işlev çağrıları gibi daha karmaşık alt ifadelerle eşleşen ifadeler vardı. Değer analizi yapmak için metni soldan sağa taradık ve sürekli olarak listede metnin bir sonraki parçasıyla eşleşen ilk ifadeyi aradık.
Bu yöntem çoğu zaman işe yarasa da işe yaramadığı durumların sayısı giderek artıyordu. Yıllar içinde, eşleştirmenin tam olarak doğru yapılmadığına dair çok sayıda hata raporu aldık. Bu sorunları düzeltirken (bazı düzeltmeler basit, bazıları ise oldukça ayrıntılıydı) teknik eksikliklerimizi kontrol altında tutmak için yaklaşımımızı yeniden düşünmemiz gerekti. Şimdi bu sorunlardan bazılarına göz atalım.
Eşleşen color-mix()
color-mix() işlevi için kullandığımız normal ifade şuydu:
/color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g
Bu, söz dizimiyle eşleşir:
color-mix(<color-interpolation-method>, [<color> && <percentage [0,100]>?]#{2})
Eşleşmeleri görselleştirmek için aşağıdaki örneği çalıştırmayı deneyin.
const re = /color-mix\(.*,\s*(?<firstColor>.+)\s*,\s*(?<secondColor>.+)\s*\)/g;
// it works - simpler example
const simpler = re.exec('color-mix(in srgb, pink, hsl(127deg 100% 50%))');
console.table(simpler.groups);
re.exec('');
// it doesn't work - complex example
const complex = re.exec('color-mix(in srgb, pink, var(--undefined, hsl(127deg var(--saturation, 100%) 50%)))');
console.table(complex.groups);

Daha basit olan örnek sorunsuz çalışıyor. Ancak daha karmaşık olan örnekte <firstColor> eşleşmesi hsl(177deg var(--saturation, <secondColor> eşleşmesi ise 100%) 50%)) olur. Bu da tamamen anlamsızdır.
Bunun bir sorun olduğunu biliyorduk. CSS, resmi bir dil olarak düzenli değildir. Bu nedenle, var işlevleri gibi daha karmaşık işlev bağımsız değişkenlerini işlemek için özel işleme özelliğini zaten eklemiştik. Ancak ilk ekran görüntüsünde görebileceğiniz gibi, bu yöntem her zaman işe yaramıyordu.
Eşleşen tan()
Bildirilen en komik hatalardan biri, trigonometrik tan() işleviyle ilgiliydi . Renkleri eşleştirmek için kullandığımız normal ifade, red anahtar kelimesi gibi adlandırılmış renkleri eşleştirmek için bir alt ifade \b[a-zA-Z]+\b(?!-) içeriyordu. Ardından, eşleşen parçanın gerçekten adlandırılmış bir renk olup olmadığını kontrol ettik ve tan da adlandırılmış bir renkmiş. Bu nedenle, tan() ifadeyi yanlışlıkla renk olarak yorumladık.
Eşleşen var()
Başka bir örneğe bakalım: Diğer var() referanslarını içeren bir yedekle birlikte var() işlevleri: var(--non-existent, var(--margin-vertical)).
var() için normal ifademiz bu değerle eşleşir. Ancak ilk kapanış parantezinde eşleşmeyi durdurur. Bu nedenle, yukarıdaki metin var(--non-existent, var(--margin-vertical) olarak eşleştirilir. Bu, normal ifade eşleştirmenin tipik bir sınırlamasıdır. Eşleşen parantez gerektiren diller temelde düzenli değildir.
CSS ayrıştırıcısına geçiş
Normal ifadeler kullanılarak yapılan metin analizi çalışmayı durdurduğunda (analiz edilen dil normal olmadığından) standart bir sonraki adım vardır: daha yüksek türde bir dil bilgisi için ayrıştırıcı kullanmak. CSS için bu, bağlamdan bağımsız diller için bir ayrıştırıcı anlamına gelir. Aslında DevTools kod tabanında böyle bir ayrıştırıcı sistemi zaten vardı: CodeMirror'ın Lezer'ı. Bu, örneğin Kaynaklar panelinde bulabileceğiniz düzenleyici olan CodeMirror'da söz dizimi vurgulamanın temelidir. Lezer'ın CSS ayrıştırıcısı, CSS kuralları için (soyut olmayan) söz dizimi ağaçları oluşturmamıza olanak tanıdı ve kullanıma hazırdı. Zafer.

Ancak normal ifadeye dayalı eşleştirmeden doğrudan ayrıştırıcıya dayalı eşleştirmeye geçmenin mümkün olmadığını gördük. Bu iki yaklaşım birbirine zıt yönlerde çalışır. Geliştirici Araçları, değer parçalarını normal ifadelerle eşleştirirken girişi soldan sağa doğru tarar ve sıralı bir kalıp listesinden en erken eşleşmeyi bulmaya çalışır. Söz dizimi ağacında eşleştirme, aşağıdan yukarıya doğru başlar. Örneğin, işlev çağrısını eşleştirmeye çalışmadan önce bir çağrının bağımsız değişkenleri analiz edilir. Bunu, önce parantez içindeki ifadeleri, ardından çarpma ve toplama işleçlerini dikkate aldığınız bir aritmetik ifadeyi değerlendirmek gibi düşünebilirsiniz. Bu çerçevede, normal ifadeye dayalı eşleştirme, aritmetik ifadenin soldan sağa değerlendirilmesine karşılık gelir. Eşleştirme sisteminin tamamını sıfırdan yeniden yazmak istemiyorduk: 15 farklı eşleştirici ve oluşturucu çifti vardı ve bunlara ait binlerce satır kod bulunuyordu. Bu nedenle, sistemi tek bir aşamada yayınlamamız pek mümkün değildi.
Bu nedenle, aşağıda daha ayrıntılı olarak açıklayacağımız, aşamalı değişiklikler yapmamıza olanak tanıyan bir çözüm bulduk. Kısacası, iki aşamalı yaklaşımı koruduk ancak ilk aşamada alt ifadeleri aşağıdan yukarıya doğru eşleştirmeye çalışıyoruz (böylece normal ifade akışını bozuyoruz) ve ikinci aşamada yukarıdan aşağıya doğru oluşturuyoruz. Her iki aşamada da mevcut normal ifade tabanlı eşleştiricileri ve oluşturucuları neredeyse hiç değiştirmeden kullanabildik ve bu sayede bunları tek tek taşıyabildik.
1. aşama: Aşağıdan yukarıya eşleştirme
İlk aşama, adından da anlaşılacağı üzere, yalnızca ve tam olarak keşif yapmayı kapsar. Ağacı aşağıdan yukarıya doğru tarar ve ziyaret ettiğimiz her söz dizimi ağacı düğümündeki alt ifadeleri eşleştirmeye çalışırız. Belirli bir alt ifadeyi eşleştirmek için eşleştirici, mevcut sistemde olduğu gibi normal ifade kullanabilir. 128 sürümünden itibaren, bazı durumlarda (ör. eşleşen uzunluklar için) bu işlemi yapmaya devam ediyoruz. Alternatif olarak, eşleştirici, mevcut düğümde köklenen alt ağacın yapısını analiz edebilir. Bu sayede, söz dizimi hatalarını yakalayabilir ve aynı anda yapısal bilgileri kaydedebilir.
Yukarıdaki söz dizimi ağacı örneğini inceleyelim:

Bu ağaç için eşleştiricilerimiz aşağıdaki sırayla uygulanır:
hsl(177degvar(--saturation, 100%) 50%): İlk olarak,hslişlev çağrısının ilk bağımsız değişkeni olan ton açısını buluruz. Açı değerini açı simgesiyle süsleyebilmek için bunu bir açı eşleştiriciyle eşleştiririz.hsl(177degvar(--saturation, 100%)50%): İkinci olarak, bir değişken eşleştirici ilevarişlev çağrısını buluruz. Bu tür görüşmelerde temel olarak iki şey yapmak istiyoruz:- Değişkenin bildirimini arayıp değerini hesaplayın ve bunlara bağlanmak için değişken adına sırasıyla bir bağlantı ve popover ekleyin.
- Hesaplanan değer bir renkse çağrıyı renk simgesiyle süsleyin. Aslında üçüncü bir şey daha var ama ondan daha sonra bahsedeceğiz.
hsl(177deg var(--saturation, 100%) 50%): Son olarak,hslişlevinin çağrı ifadesini eşleştirerek renk simgesiyle süslüyoruz.
Süslemek istediğimiz alt ifadeleri aramanın yanı sıra, eşleştirme sürecinin bir parçası olarak çalıştırdığımız ikinci bir özellik daha var. 2. adımda, bir değişken adı için hesaplanan değeri aradığımızı söylemiştik. Hatta bunu bir adım ileriye taşıyıp sonuçları ağaçta yukarı doğru yayarız. Bu, yalnızca değişken için değil, yedek değer için de geçerlidir. Bir var işlev düğümü ziyaret edildiğinde, alt öğelerinin önceden ziyaret edilmiş olduğu garanti edilir. Bu nedenle, yedek değerde görünebilecek tüm var işlevlerinin sonuçlarını zaten biliyoruz. Bu nedenle, var işlevlerini sonuçlarıyla anında kolayca ve ucuza değiştirebiliriz. Bu sayede, 2. adımda yaptığımız gibi "Bu var çağrısının sonucu bir renk mi?" gibi soruları kolayca yanıtlayabiliriz.
2. aşama: Yukarıdan aşağıya oluşturma
İkinci aşamada yönü tersine çeviririz. 1. aşamadaki eşleşme sonuçlarını alarak ağacı yukarıdan aşağıya doğru sırayla gezip HTML'ye dönüştürürüz. Ziyaret edilen her düğümün eşleşip eşleşmediğini kontrol ederiz ve eşleşiyorsa eşleştiricinin ilgili oluşturucusunu çağırırız. Yalnızca metin içeren düğümler (ör. NumberLiteral "%50") için özel işlem yapma ihtiyacını, metin düğümleri için varsayılan bir eşleştirici ve oluşturucu ekleyerek ortadan kaldırıyoruz. Oluşturucular, bir araya getirildiğinde süslemeleri de dahil olmak üzere özellik değerinin temsilini üreten HTML düğümleri oluşturur.

Örnek ağaç için özellik değerinin oluşturulma sırası şu şekildedir:
hslişlev çağrısını ziyaret edin. Eşleştiği için renk işlevi oluşturucuyu çağırın. İki işlevi vardır:- Herhangi bir
varbağımsız değişkeni için anında değiştirme mekanizmasını kullanarak gerçek renk değerini hesaplar, ardından bir renk simgesi çizer. CallExpressionöğesinin alt öğelerini yinelemeli olarak oluşturur. Bu işlem, yalnızca metin olan işlev adını, parantezleri ve virgülleri otomatik olarak oluşturur.
- Herhangi bir
hslçağrısının ilk bağımsız değişkenini ziyaret edin. Eşleştiği için açı simgesini ve açının metnini çizen açı oluşturucuyu çağırın.varçağrısı olan ikinci bağımsız değişkene gidin. Eşleşme olduğu için renderer değişkenini çağırın. Bu değişken aşağıdaki çıkışı verir:- Başlangıçta
var(metni. - Değişken adı, değişkenin tanımına yönlendiren bir bağlantıyla veya tanımlanmadığını belirtmek için gri metin rengiyle süslenir. Ayrıca, değişkenin değeriyle ilgili bilgileri göstermek için değişkene bir popover eklenir.
- Virgül ve ardından yedek değeri yinelemeli olarak oluşturur.
- Kapanış parantezi.
- Başlangıçta
hslçağrısının son bağımsız değişkenini ziyaret edin. Eşleşmediği için yalnızca metin içeriğini döndürün.
Bu algoritmada, eşleşen bir düğümün alt öğelerinin nasıl oluşturulacağını tamamen bir oluşturucunun kontrol ettiğini fark ettiniz mi? Alt öğeleri yinelemeli olarak oluşturmak proaktif bir yaklaşımdır. Bu yöntem, normal ifade tabanlı oluşturmadan söz dizimi ağacı tabanlı oluşturmaya adım adım geçişi mümkün kıldı. Eski bir normal ifade eşleştiriciyle eşleşen düğümler için ilgili oluşturucu orijinal biçiminde kullanılabilir. Söz dizimi ağacı açısından, tüm alt ağacı oluşturma sorumluluğunu üstlenir ve sonucu (bir HTML düğümü), çevreleyen oluşturma sürecine sorunsuz bir şekilde entegre edilebilir. Bu sayede, eşleştiricileri ve oluşturucuları çiftler halinde taşıyıp tek tek değiştirebildik.
Eşleşen düğümün alt öğelerinin oluşturulmasını kontrol eden oluşturucuların bir diğer harika özelliği de, eklediğimiz simgeler arasındaki bağımlılıklar hakkında fikir yürütmemize olanak tanımasıdır. Yukarıdaki örnekte, hsl işlevi tarafından üretilen renk, ton değerine bağlıdır. Yani renk simgesinin gösterdiği renk, açı simgesinin gösterdiği açıya bağlıdır. Kullanıcı bu simge aracılığıyla açı düzenleyiciyi açıp açıyı değiştirirse artık renk simgesinin rengini anlık olarak güncelleyebiliriz:
Yukarıdaki örnekte görebileceğiniz gibi, bu mekanizmayı color-mix() ve iki renk kanalı gibi diğer simge eşlemeleri veya yedek renginden bir renk döndüren var işlevleri için de kullanıyoruz.
Performansa olan etkisi
Güvenilirliği artırmak ve uzun süredir devam eden sorunları düzeltmek için bu sorunu ayrıntılı olarak incelerken, tam teşekküllü bir ayrıştırıcı çalıştırmaya başladığımızı göz önünde bulundurarak performansın bir miktar düşmesini bekliyorduk. Bunu test etmek için yaklaşık 3.500 özellik beyanını oluşturan bir karşılaştırma testi oluşturduk ve M1 makinesinde 6 kat kısıtlama ile hem normal ifade tabanlı hem de ayrıştırıcı tabanlı sürümlerin profilini çıkardık.
Beklediğimiz gibi, ayrıştırmaya dayalı yaklaşımın bu durumda normal ifadeye dayalı yaklaşımdan %27 daha yavaş olduğu ortaya çıktı. Normal ifade tabanlı yaklaşımın oluşturulması 11 saniye, ayrıştırıcı tabanlı yaklaşımın oluşturulması ise 15 saniye sürdü.
Yeni yaklaşımın sağladığı avantajları göz önünde bulundurarak bu yaklaşımla devam etmeye karar verdik.
Onaylar
Bu yayının düzenlenmesindeki değerli yardımları için Sofia Emelianova ve Jecelyn Yeen'e çok teşekkür ederiz.
Önizleme kanallarını indirme
Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Dev veya Beta'yı kullanabilirsiniz. Bu önizleme kanalları, en yeni Geliştirici Araçları özelliklerine erişmenizi sağlar, en yeni web platformu API'lerini test etmenize olanak tanır ve sitenizdeki sorunları kullanıcılarınızdan önce bulmanıza yardımcı olur.
Chrome Geliştirici Araçları ekibiyle iletişime geçme
Yeni özellikler, güncellemeler veya Geliştirici Araçları ile ilgili diğer konuları tartışmak için aşağıdaki seçenekleri kullanın.
- Geri bildirimlerinizi ve özellik isteklerinizi crbug.com adresinden bize gönderebilirsiniz.
- Geliştirici Araçları'nda Diğer seçenekler > Yardım > Geliştirici Araçları ile ilgili sorun bildir'i kullanarak Geliştirici Araçları ile ilgili sorun bildirin.
- @ChromeDevTools hesabına tweet atın.
- Geliştirici Araçları'ndaki yenilikler YouTube videolarına veya Geliştirici Araçları İpuçları YouTube videolarına yorum bırakın.