Notlar · · 3 dk
paintcheck: iki yanlış sürümün pikselden kontrast ölçmek üzerine öğrettikleri
Fikir basitti, sayfanın görüntüsünü al, renkleri oku. İlk iki uygulama iki zıt yönde kendinden emin biçimde yanlıştı. Test örneklerinin ortaya çıkardığı hatalar ve bunları nasıl düzelttiğim.
paintcheck metin kontrastını CSS'ten değil, Chrome'un boyadığı piksellerden ölçüyor. Bu not README'de olmayan kısım hakkında: yöntem doğru olmadan önce nasıl yanlış gitti. Aşağıdakiler hafızadan değil, deponun değişiklik günlüğü ve test fixture'larından derlendi.
CSS neden yetmedi
axe-core gibi araçlar kontrastı color ve background-color değerlerinden hesaplar. Kendi sitemde metin, gradyan perde altındaki bir fotoğrafın üstündeydi; okunacak arka plan rengi yoktu, denetleyiciler ögeyi incomplete raporladı. En kötü noktadaki gerçek oran 4,5:1 gereksinime karşı 3:1 civarındaydı ve bunu bana kimse söylememişti.
Birinci deneme: metnin yanından örnekle
İlk sürüm bir ekran görüntüsü alıp arka plan rengini her harfin yanındaki piksellerden okudu. Düz zeminde çalışıyor. Gradyanda harfin yanındaki piksel altındakinden farklı renk; okumalar iki yönde de yanlıştı: geçen metne yanlış alarm, kalan metne temiz sonuç. Sayılar gördüğümle uyuşmadığı için fark ettim.
İkinci deneme: kapsamayı değişimden tahmin et
İkinci sürüm iki görüntü aldı, biri normal biri tüm metin gizli, ve her pikselin ne kadar harfle kaplandığını ne kadar değiştiğinden tahmin etti. Sonra renkleri en çok kaplanan piksellerden okudu.
Gradyanda iki görüntü arasındaki değişim zeminin koyuluğuyla büyür. Tahmin bu yüzden yalnızca en yüksek kontrastlı bölgenin piksellerini seçti ve açık uca hiç bakmadı. Zeminin metnin kendi renginden geçtiği gradient.html fixture'ının en kötü noktası 1,1:1 civarında. Bu sürüm 4,47:1 raporladı ve geçirdi. Test başarısız oldu; ben olmamıştım.
Dayanan yöntem
Aynı görünümün beş ekran görüntüsü:
- A: sayfanın kendisi
- A2: 300 ms sonra; değişen pikseller elenir (yüklenen yazı tipi, animasyon)
- B: tüm metin saydam
- C: tüm metin siyah
- D: tüm metin beyaz
C ve D her pikselin kapsamasını tam olarak verir. Bir piksel bg zemini üstünde α oranında örtülüyse C = (1−α)·bg ve D = α·255 + (1−α)·bg, yani α = (D − C)/255; zeminden ve metnin gerçek renginden bağımsız. Ögenin en büyük α'sına yakın pikseller harfin çekirdeği; kenar yumuşatma pikselleri atılır.
Sonra ön plan A'dan, arka plan B'den aynı piksellerden okunur. Hiçbir şey hesaplanmaz. Opaklık zincirleri, rgba renkler ve pseudo-element perdeler Chrome boyarken zaten uygulandı.
Testlerin sonradan zorladığı iki düzeltme
Alt piksel yumuşatma. Windows'ta ClearType her renk kanalına farklı kapsama verir. Tam kapalı sanılan pikseller hâlâ karışımdı ve her okuma yaklaşık yarım puan düşük çıkıyordu: beyaz üstünde #777 için hesaplanan 4,48'e karşı ölçülen 3,92. Chrome artık gri tonlamalı yumuşatmayla açılıyor (--disable-lcd-text).
İnce harfler. Gövde bir pikselden inceyse hiçbir piksel tam kapanmaz, en yoğun olan bile karışımdır. α bilindiği için karışım geri alınabilir: E = B + (A − B)/α. Bu olmadan gerçekte 5,85:1 olan 12 px altbilgi bağlantıları 3,81:1 çıkıyordu. Bu düzeltmenin ilk sürümü rengin kendi alfasını da kapsamadan böldü, ki bu yanlış: siyah ve beyaz sondalar rengi değiştirir ama ögenin opacity değerini değil, dolayısıyla bölünmesi gereken tek şey opaklık. Geçmesi gereken rgba .68 satır ve 3,32:1'de kalması gereken rgba .5 satır artık iki yönü de sabitliyor.
Doğrulanan ve doğrulanmayan
37bbe2a revizyonunda kayıtlı 57 test yerelde Windows 11 ve Chrome 152 ile geçti. Karşılaştırma betiği axe-core ile paintcheck’i aynı test sayfalarında çalıştırıyor; düz renklerde hesaplanan referans oranları kullanılıyor. Fotoğraf ve perde örneklerinin elle hesaplanmış referans değeri yok. Gösterilen sonuçlar yerel revizyona ait; bu revizyon henüz GitHub’da yayımlanmadı.
Güncelleme, 16 Eylül 2026: 37bbe2a revizyonu GitHub’da ve Ubuntu, Node 20/22 CI çalışması geçti. macOS hâlâ denenmedi. Yazı tiplerinin ekrana çizilmesi oranları değiştirebilir; bu iki ortam dışında aynı geçti/kaldı kararını vereceği doğrulanmış değil.
Araç henüz npm'de değil. npx github:hasanyahsitr/paintcheck ile depodan çalışır ve yerel Chrome ister.
Bir şeyi yanlış ölçtüğümü düşünüyorsanız yazın; düzeltir ve değişikliği buraya not düşerim. İletişim →