Form Kontrolleri ve Etkileşim Property'leri: accent-color, appearance, caret-color, field-sizing, cursor, pointer-events ve user-select
Form kontrollerini önce font: inherit ve color-scheme ile sayfaya bağla, rengi accent-color ile ver; appearance: none ile yapılan özel kontrolde durumu renkle değil şekille göster. Yazı Bookstore'un checkout form'unu (stepper, Gift wrap checkbox'ı, field-sizing ile büyüyen not) ve pointer-events, user-select, cursor ile interactivity'yi Chromium ve WebKit ölçümleriyle anlatır.
Sürüm notu: Bu yazı CSS Basic User Interface Level 4, CSS Color Adjustment Level 1, CSS Box Sizing Level 4 (field-sizing) ve CSS Form Control Styling Level 1 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 checkout form'unu bitiriyoruz: adet butonları, hediye paketi checkbox'ı, büyüyen bir sipariş notu ve kitap detayında kapağa tıklamayı engellemeyen bir indirim rozeti. Önce bir buton:
/* 6) pointer-events: none fareyi durdurur, klavyeyi değil */
#ghost { pointer-events: none; }
<button type="button" id="ghost">Pay now</button>
Fareyle tıklamak bir şey yapmıyor; buton gri gösterilse kullanıcı onu devre dışı sanar. Peki buton gerçekten devre dışı mı? Klavyeyle ona gelip Enter'a basan biri ödeme yapabilir mi?
Form Reset'i: font: inherit ve color-scheme
Form kontrolleri sayfanın font'unu varsayılan olarak almaz; tarayıcının kendi kuralı onlara ayrı bir font verir. Lab'de iki input var, ikincisinde font: inherit:
/* 1) form kontrollerinin, sıfırlayana kadar kendi font'u vardır */
#reset { font: inherit; }
/* 2) native kontroller color-scheme'e uyar */
.dark { color-scheme: dark; }
node scripts/12-forms.mjs
--- chromium
font-size: default input 13.3333px, font: inherit 16px
color-scheme: dark input rgb(59, 59, 59) + rgb(133, 133, 133)
--- webkit
font-size: default input 11px, font: inherit 16px
color-scheme: dark input rgb(30, 30, 30) + rgb(255, 255, 255)
Hiç stil verilmemiş input Chromium'da 13.33, WebKit'te 11 piksel; font: inherit ile ikisi de sayfanın 16 pikseline çıkıyor. Bookstore'da bunu tek tek component'lerde yazıyorduk; artık base.css'te bütün kontroller için bir kere yazıyoruz:
/* form kontrolleri varsayılan olarak sayfanın font'unu almaz (Chromium'da 13.33px, WebKit'te 11px) */
input, button, select, textarea { font: inherit; }
16 pikselin bir yan faydası daha var: iOS Safari, font boyutu 16 pikselin altında olan bir input'a odaklanınca sayfayı yakınlaştırır. Kullanıcı form'u doldururken sayfa büyür ve yana kayar. Bunu gerçek bir iPhone'da denemedim; ama 16 piksel kuralı bu davranışın bilinen çözümü ve font: inherit ile kendiliğinden sağlanıyor. maximum-scale=1 ile yakınlaştırmayı kapatmak ise erişilebilirliği bozar.
İkinci reset konusu dark mode. color-scheme: dark, tarayıcıya o bölgedeki native kontrolleri (input, checkbox, scrollbar) koyu çizmesini söyler. Ölçümde koyu input'un arka planı Chromium'da rgb(59, 59, 59), WebKit'te rgb(30, 30, 30). Bunu yazmazsan koyu bir sayfada beyaz input'lar kalır. Bookstore'un :root'unda şimdilik color-scheme: light var; tema ve light-dark() sitedeki CSS serisinin (CSS3: Flexbox, Grid ve Modern CSS) 10. yazısında.
accent-color
Checkbox, radio, range ve progress'in rengini tek bir property ile değiştirmenin yolu accent-color:
/* 3) accent-color: tarayıcı onay işaretini okunur tutar */
.accent input { inline-size: 40px; block-size: 40px; margin: 4px; accent-color: #b91c1c; }
.accent #light-check { accent-color: #fde68a; }
--- chromium
accent-check rgb(185, 28, 28) + rgb(255, 255, 255)
accent-radio rgb(255, 255, 255) + rgb(185, 28, 28)
light-check rgb(253, 230, 138) + rgb(59, 59, 59)
--- webkit
accent-check rgb(185, 28, 28) + rgb(255, 255, 255)
accent-radio rgb(185, 28, 28) + rgb(255, 255, 255)
light-check rgb(206, 187, 111) + rgb(255, 255, 255)
Kırmızı vurgu rengi iki motorda da olduğu gibi kullanılıyor: kutunun dolgusu rgb(185, 28, 28), onay işareti beyaz. İlginç olan açık sarı (#fde68a). Beyaz bir işaret açık sarının üstünde okunmaz; Chromium dolguyu sarı bırakıp işareti koyu griye (rgb(59, 59, 59)) çeviriyor, WebKit ise işareti beyaz bırakıp dolguyu koyulaştırıyor (rgb(206, 187, 111)). Yol farklı, amaç aynı: işaretle dolgu arasında yeterli contrast. Yani verdiğin renk her zaman birebir çizilmeyebilir.
web-features accent-color'ı "Baseline değil" sayıyor ve plandaki şüphe doğru çıktı: sebep Chrome for Android. browser-compat-data'nın notuna göre Android'deki Chrome, Safari 26.2'den önceki sürümler gibi, kontrolün okunabilirliği için bu minimum contrast'ı korumuyor. Rengin kendisi her yerde uygulanıyor; yalnız açık bir vurgu rengi seçtiğinde Android'de beyaz işaret okunmaz hâle gelebilir. Koyu ve doygun bir renk seçersen fark kalmaz. Bookstore'da checkout form'una link rengini verdik:
/* native radio ve checkbox'lar link renginde; tarayıcı işareti okunur tutar */
.checkout-form { accent-color: var(--color-link); }
.shipping-option { cursor: pointer; }
/* özel checkbox: appearance: none native görünümü kaldırır, davranışı değil */
.check {
appearance: none;
display: inline-grid;
place-content: center;
flex: none;
inline-size: 1.5rem;
block-size: 1.5rem;
margin: 0;
border: 2px solid var(--color-muted);
border-radius: 0.25rem;
background: var(--color-surface);
}
.check:checked { border-color: var(--color-link); background: var(--color-link); }
/* işaret bir border, bu yüzden forced colors onu korur; yalnız işaretliyken var */
.check::before {
content: "";
display: none;
inline-size: 0.75rem;
block-size: 0.375rem;
border: solid #ffffff;
border-width: 0 0 3px 3px;
rotate: -45deg;
translate: 0 -2px;
}
.check:checked::before { display: block; }
.gift { display: flex; gap: 0.75rem; align-items: center; }
/* adet stepper'ı */
.order-items { padding: 0; border: 0; }
.order-item { display: flex; gap: 1rem; align-items: center; }
.order-item .cost { margin-inline-start: auto; font-weight: 700; }
.stepper {
display: inline-flex;
align-items: center;
border: 1px solid var(--color-border);
border-radius: 999px;
}
.stepper button {
inline-size: 2.5rem;
block-size: 2.5rem;
border: 0;
background: none;
cursor: pointer;
/* çift dokunuşla yakınlaştırma yok, dolayısıyla dokunma gecikmesi de yok */
touch-action: manipulation;
/* hızlı dokunuşlar çevredeki metni seçmez; 27.3 öncesi Safari prefix ister */
-webkit-user-select: none;
user-select: none;
}
.stepper input {
inline-size: 3rem;
padding: 0;
border: 0;
text-align: center;
}
/* sipariş notu metinle büyür; field-sizing'i olmayan tarayıcılarda rows ve resize kalır */
.field textarea {
inline-size: 100%;
min-block-size: 5rem;
max-block-size: 15rem;
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-border);
border-radius: 0.375rem;
field-sizing: content;
resize: vertical;
}
.field input, .field textarea { caret-color: var(--color-accent); }
appearance: none ile Özel Checkbox
accent-color'ın yetmediği yerde (boyut, köşe, işaretin biçimi) appearance: none kontrolün native görünümünü kaldırır ve onu sıradan bir kutu gibi stillemene izin verir. Kaldırdığı şey yalnız görünüm: input yine input, klavyeyle odaklanır, Space ile değişir, form'la gönderilir. Lab'de iki yol var:
/* 4) appearance: none — border ile çizilen bir onay işareti */
.check {
appearance: none;
display: inline-grid;
place-content: center;
inline-size: 24px;
block-size: 24px;
margin: 4px;
border: 2px solid #4b5563;
border-radius: 4px;
}
.check:checked { border-color: #1d4ed8; background: #1d4ed8; }
.check::before {
content: "";
inline-size: 12px;
block-size: 6px;
border: solid transparent;
border-width: 0 0 3px 3px;
rotate: -45deg;
translate: 0 -2px;
}
/* saf yol: şeffaf bir işaret, işaretlenince beyaz yapılır */
.naive:checked::before { border-color: #ffffff; }
/* daha iyisi: kutu işaretlenene kadar hiç işaret yok */
.better::before { display: none; }
.better:checked::before { display: block; border-color: #ffffff; }
--- chromium
check naive-on rgb(29, 78, 216) + rgb(255, 255, 255)
check naive-off rgb(255, 255, 255) + rgb(75, 85, 99)
check better-on rgb(29, 78, 216) + rgb(255, 255, 255)
check better-off rgb(255, 255, 255) + rgb(75, 85, 99)
Space on #better-off: checked true
--- webkit
check naive-on rgb(29, 78, 216) + rgb(255, 255, 255)
check naive-off rgb(255, 255, 255) + rgb(75, 85, 99)
check better-on rgb(29, 78, 216) + rgb(255, 255, 255)
check better-off rgb(255, 255, 255) + rgb(75, 85, 99)
Space on #better-off: checked true
--- chromium, forced colors
check naive-on dark pixels 214, background rgb(255, 255, 255)
check naive-off dark pixels 214, background rgba(255, 255, 255, 0)
check better-on dark pixels 214, background rgb(255, 255, 255)
check better-off dark pixels 156, background rgba(255, 255, 255, 0)
Normal modda iki yolun piksel sonucu aynı: işaretli kutu mavi, işaretsiz kutu beyaz ve gri kenarlıklı. Space iki motorda da kutuyu işaretliyor. Fark forced colors modunda (Windows'un yüksek contrast teması) çıkıyor; bu modu yalnız Chromium emüle edebiliyor. Forced colors, renkleri sistemin birkaç rengine indirir: arka planlar Canvas (burada beyaz) olur, kenarlıklar CanvasText (siyah). Saf yolda işaretsiz kutunun şeffaf onay işareti de siyaha döndü ve işaretli kutuyla aynı sayıda koyu piksel çiziyor (214): kullanıcı iki kutuyu ayırt edemez. İşaretli kutunun mavi dolgusu da gitti, yani durumu gösteren tek şey onay işareti. Doğru yolda işaret yalnız :checked iken oluşuyor; işaretsiz kutuda yalnız kenarlık kalıyor (156 piksel).
Ders şu: appearance: none ile bir durum gösteriyorsan durumu renkle değil, var olan ya da olmayan bir şekille göster. Bookstore'un "Gift wrap" kutusu doğru yolu kullanıyor. Checkout sayfasında ölçüm:
<fieldset class="order-items">
<legend>Your order</legend>
<div class="order-item">
<span>Middlemarch</span>
<div class="stepper">
<button type="button" data-step="-1" aria-label="One less">−</button>
<input id="quantity" name="quantity" value="1" inputmode="numeric" data-min="1" data-max="10" aria-label="Quantity">
<button type="button" data-step="1" aria-label="One more">+</button>
</div>
<span class="cost">₺385.50</span>
</div>
</fieldset>
<label class="gift"><input type="checkbox" class="check" name="gift" value="yes"> Gift wrap (₺25.00)</label>
<div class="field">
<label for="note">Order note</label>
<textarea id="note" name="note" rows="3" placeholder="Anything we should know?"></textarea>
</div>
node scripts/12-pages.mjs
--- chromium
Tab from the shipping radios → .check; on .check: outline solid 3px
Space → checked true, ::before display block
--- webkit
Tab from the shipping radios → #note; on .check: outline solid 3px
Space → checked true, ::before display block
Chromium'da radio grubundan Tab ile doğrudan checkbox'a geçildi. WebKit'te focus sipariş notuna atladı: macOS'ta Tab varsayılan olarak yalnız metin alanlarında durur (Safari'de bu davranış bir ayarla değişir); ölçüm orada checkbox'a doğrudan focus veriyor. İki motorda da kutu :focus-visible ile 3 piksellik halkayı alıyor: appearance: none focus halkasını kaldırmaz, ama outline: none yazarsan kaldırırsın (bu serinin border ve outline yazısı). Forced colors için piksel ölçümünü lab'de yaptım: tam checkout sayfasında Chromium'un emülasyonu, serinin clip-path ve mask yazısında anlattığım hatayı burada da gösterdi (computed kenarlık siyah, çizilen gri).
Native kontrolün birkaç parçası daha var. caret-color, metin imlecinin rengini değiştirir; Bookstore'da input ve textarea'da vurgu rengi (ölçümde iki motorda da rgb(185, 28, 28)). ::placeholder ve ::file-selector-button bu serinin pseudo-element yazısında. input[type=range]'in thumb'ı (sürüklenen yuvarlak) için standart bir pseudo-element yok; her motorun kendi prefix'li adı var:
/* 5) range: thumb için her motorun prefix'li pseudo-element'i gerekir */
#range { appearance: none; inline-size: 200px; block-size: 6px; background: #d1d5db; border-radius: 3px; }
#range::-webkit-slider-thumb { appearance: none; inline-size: 20px; block-size: 20px; border-radius: 50%; background: #b91c1c; }
#range::-moz-range-thumb { inline-size: 20px; block-size: 20px; border: 0; border-radius: 50%; background: #b91c1c; }
--- chromium
range thumb: rgb(209, 213, 219) + rgb(185, 28, 28)
--- webkit
range thumb: rgb(209, 213, 219) + rgb(185, 28, 28)
Ray gri, thumb kırmızı; ::-webkit-slider-thumb Chromium ve WebKit'te çalışıyor. Firefox'ta karşılığı ::-moz-range-thumb; Firefox'u bu makinede çalıştıramadığım için o kuralı denemedim. İki kuralı aynı selector listesinde birleştirme: tarayıcı tanımadığı bir pseudo-element görünce bütün kuralı atar, bu yüzden her motor için ayrı kural yazılır.
field-sizing ile Büyüyen Textarea
Sipariş notu için sabit yükseklikli bir textarea ya kısa notta boş yer bırakır ya uzun notta scrollbar çıkarır. field-sizing: content, kontrolün boyutunu içeriğine göre hesaplatır; min-block-size ve max-block-size sınırları belirler:
--- chromium
order note: 0 lines 80px, 6 lines 172px, 20 lines 240px (scrolls)
--- webkit
order note: 0 lines 80px, 6 lines 172px, 20 lines 240px (scrolls)
Boş not 80 piksel (5rem minimum), altı satırla 172 piksele büyüyor, yirmi satırda 240 pikselde (15rem maksimum) duruyor ve içerik kaymaya başlıyor. İki motorda sonuç aynı. field-sizing Haziran 2026'dan beri newly available (Firefox 152 ile); desteklemeyen tarayıcı rows="3"'ün verdiği yüksekliği ve resize: vertical ile kullanıcının tutamacını kullanır. Eskiden bunun için yazılan JavaScript'e (her input'ta scrollHeight okuyup yükseklik atamak) gerek kalmıyor.
Select'in kendisini stillemek için appearance: base-select (customizable select) var; ölçümde iki motor da bu değeri tanıyor. web-features'a göre Chrome 135 ve Safari 27'de, Firefox'ta yok, yani Baseline değil. Element tarafı ve progressive enhancement yolu sitedeki HTML serisinin (HTML5 ve Semantic HTML) 07. yazısında; burada yalnız şunu not edelim: base-select'i tanımayan tarayıcı native select'i gösterir, bu yüzden güvenle eklenebilir.
pointer-events: Rozetin Arkasındaki Link
Giriş sorusunun cevabı:
--- chromium
pointer-events: none button: clicks by mouse 0, after focus + Enter 1
--- webkit
pointer-events: none button: clicks by mouse 0, after focus + Enter 1
Fare tıklaması 0, focus ve Enter 1 tıklama üretti: pointer-events: none yalnız farenin ve dokunmanın hedef bulmasını engeller. Buton Tab sırasında durur, Enter ve Space ile çalışır, screen reader'lar onu normal bir buton olarak okur. Bir kontrolü devre dışı bırakmak için disabled attribute'u, bir bölgeyi tamamen etkisiz yapmak için HTML'in inert attribute'u kullanılır.
pointer-events: none'ın doğru kullanımı tam tersi: üstte duran ama tıklanmaması gereken bir süsün tıklamayı alttakine geçirmesi. Kitap detayındaki "Recommended" şeridinde Persuasion kapağının üstüne bir indirim rozeti koyduk:
<li><a href="book.html"><img src="img/cover-persuasion.webp" alt="Persuasion" width="240" height="360" loading="lazy"></a><span class="sale-badge">15% off</span></li>
/* rozet kapağın üstünde durur, ama tıklamalar ondan geçip linke gider */
.sale-badge {
position: absolute;
inset-block-start: 0.5rem;
inset-inline-start: 0.5rem;
padding: 0.125rem 0.5rem;
border-radius: 999px;
background: var(--color-accent);
color: #ffffff;
font-size: 0.75rem;
font-weight: 700;
pointer-events: none;
}
--- chromium
sale badge center hits: pointer-events none → IMG inside A, auto → SPAN
--- webkit
sale badge center hits: pointer-events none → IMG inside A, auto → SPAN
Rozetin ortasına denk gelen nokta pointer-events: none ile kapak görseline, yani linkin içine düşüyor; değer auto olunca rozetin span'ine. Rozet olmasa tıklamaların bir kısmı boşa giderdi. Rozet metni linkin dışında olduğu için screen reader'da "Persuasion" linkinden sonra "15% off" olarak okunur.
user-select, cursor ve interactivity
Adet butonları için iki property daha kullanıyoruz. Biri bir önceki yazıdaki touch-action: manipulation (çift dokunuşla yakınlaştırmayı kapatıp dokunma gecikmesini kaldırır), öbürü user-select: none. Lab'de aynı satır üç kez var:
/* 7) user-select */
#unprefixed button { user-select: none; }
#both button { -webkit-user-select: none; user-select: none; }
#code { -webkit-user-select: all; user-select: all; }
<p id="plain">Quantity <button type="button">−</button> 2 <button type="button">+</button></p>
<p id="unprefixed">Quantity <button type="button">−</button> 2 <button type="button">+</button></p>
<p id="both">Quantity <button type="button">−</button> 2 <button type="button">+</button></p>
--- chromium
select #plain "Quantity − 2 +"
select #unprefixed "Quantity 2 "
select #both "Quantity 2 "
one click on #code selects "BK-1042"
#unprefixed button: user-select none, -webkit-user-select none
--- webkit
select #plain "Quantity − 2 +"
select #unprefixed "Quantity − 2 +"
select #both "Quantity 2 "
one click on #code selects "BK-1042"
#unprefixed button: user-select none, -webkit-user-select text
Satırın tamamı seçilince sade satır "Quantity − 2 +" kopyalıyor. Yalnız user-select: none yazılan satır Chromium'da butonları dışarıda bırakıyor ama WebKit'te bırakmıyor. Son satır sebebi gösteriyor: WebKit 27.2 prefix'siz user-select'i parse ediyor (computed değeri none, CSS.supports() da true dönüyor) ama uygulamıyor; davranışı belirleyen -webkit-user-select hâlâ text. browser-compat-data'ya göre prefix'siz user-select Safari 27.3 ile geliyor; Playwright'ın WebKit build'i yayındaki Safari değil, ama bu sonuç onunla tutarlı. user-select'in "Baseline değil" olmasının sebebi bu: Safari için iki satırı birlikte yaz. user-select: all ise bir kod ya da sipariş numarasını tek tıklamayla seçtiriyor: iki motorda da BK-1042. Bookstore'un stepper'ı:
// Adet butonları: alandaki sayıyı data-min ile data-max arasında değiştirir.
for (const stepper of document.querySelectorAll('.stepper')) {
const input = stepper.querySelector('input');
for (const button of stepper.querySelectorAll('button')) {
button.addEventListener('click', () => {
const value = Number(input.value) + Number(button.dataset.step);
input.value = String(Math.min(Number(input.dataset.max), Math.max(Number(input.dataset.min), value)));
});
}
}
--- chromium
stepper: 3 × + → 4, then 5 × − → 1; touch-action manipulation
selecting the order row copies "Middlemarch ₺385.50"
--- webkit
stepper: 3 × + → 4, then 5 × − → 1; touch-action manipulation
selecting the order row copies "Middlemarch ₺385.50"
Üç kez + ile 4, beş kez − ile alt sınır 1. Satırı seçip kopyalayan kullanıcı "Middlemarch ₺385.50" alıyor, araya butonların işaretleri karışmıyor. Adet alanı type="number" değil, inputmode="numeric"'li bir metin alanı: number input'un kendi küçük artırma butonları stepper'la çakışırdı ve onları gizlemek için yine prefix'li pseudo-element'ler gerekirdi.
Lab'deki son iki satır cursor ve interactivity için:
/* 8) özel cursor her zaman bir anahtar kelime fallback'iyle biter */
#zoom { cursor: url("../img/heart.svg") 8 8, zoom-in; }
/* 9) interactivity: inert (yalnız Chromium) */
#inert-box { interactivity: inert; }
--- chromium
interactivity: inert supported true, focus() on the button inside → active element BODY
cursor: url("http://bookstore.test/img/heart.svg") 8 8, zoom-in
appearance: base-select supported true
--- webkit
interactivity: inert supported false, focus() on the button inside → active element inert-button
cursor: url("http://bookstore.test/img/heart.svg") 8 8, zoom-in
appearance: base-select supported true
Özel bir cursor görselinden sonra her zaman bir anahtar kelime yaz; görsel yüklenmezse ya da tarayıcı onu kullanamazsa o kullanılır (Firefox 67'den beri 32×32 pikselden büyük özel cursor'ları reddediyor). 8 8, tıklama noktasının görseldeki konumu. Buton, link ve label gibi tıklanabilen element'lerde cursor: pointer yaygın bir tercih; Bookstore'da kargo seçeneği kartlarına verdik, çünkü bütün kart bir label. web-features cursor'ı iPad'deki sınırlı destek yüzünden "Baseline değil" sayıyor; masaüstünde her değer çalışır.
interactivity: inert, HTML'in inert attribute'unun CSS karşılığı: Chromium'da butona focus() verilemedi (aktif element BODY kaldı), WebKit property'yi tanımıyor ve buton focus aldı. Yalnız Chromium'da olduğu için bugün bir modal açıkken arka planı kapatmak gibi işlerde inert attribute'u (Ekim 2025'ten beri widely available) kullanılır. Mobilde dokunulan link ve butonların üstünde beliren gri kutuyu kaldıran -webkit-tap-highlight-color ise standart değil; kaldırırsan kendi :active geri bildirimini ver.
Üretimde Dikkat
Form kontrollerini dört durumda kontrol et: normal, dark mode (color-scheme), forced colors ve %200 yakınlaştırma. Özel kontroller çoğu zaman ilk ikisinde düzgün görünür, son ikisinde bozulur. Forced colors Windows'ta Ayarlar'daki contrast temalarıyla açılır; Chromium'da Playwright ile emüle edebilirsin, ama piksel kontrolünü küçük sayfalarda yap.
accent-color ve appearance: none arasında seçim yaparken önce accent-color'ı dene: native kontrol klavye, screen reader, forced colors ve dokunmatik hedef boyutu konusunda zaten doğru davranır. Özel kontrolün her bir eksiği (focus halkası, forced colors, :disabled görünümü, hata durumu) senin sorumluluğun. Doğrulama durumlarının selector'leri (:user-invalid gibi) HTML5 serisinin 05. yazısında, contrast kuralları Accessibility (a11y) serisinde.
Checkout'ta kullanıcının yanlışlıkla iki kez ödeme yapmasını önlemek için butona pointer-events: none vermek yaygın bir hata. Gönderim sürerken butonu disabled yap ya da form'un submit handler'ında ikinci gönderimi engelle; sunucu tarafında da aynı siparişin iki kez işlenmemesi gerekir.
Sık Düşülen Tuzaklar
Birincisi, pointer-events: none ile bir kontrolü devre dışı saymak. Klavye ve yardımcı teknolojiler onu kullanmaya devam eder; disabled ya da inert kullan.
İkincisi, appearance: none ile yapılan kontrolde durumu yalnız renkle göstermek. Forced colors renkleri sildiğinde işaretli ve işaretsiz aynı görünür; işareti yalnız :checked iken oluştur.
Üçüncüsü, appearance: none sonrası outline: none yazıp focus halkasını kaybetmek. Global :focus-visible kuralını ezme.
Dördüncüsü, input'lara 16 pikselden küçük font vermek. iOS Safari odaklanınca sayfayı yakınlaştırır; font: inherit ve 16 piksellik gövde metni bunu önler.
Beşincisi, Safari için yalnız prefix'siz user-select yazmak. Safari 27.3'ten önce etkisiz; -webkit-user-select'i de yaz.
Baseline Durumu
node scripts/baseline.mjs color-scheme accent-color appearance caret-color input-range field-sizing customizable-select cursor pointer-events user-select resize interactivity
color-scheme widely 2024-08-03 Chrome 98, Firefox 96, Safari 13, iOS 13
accent-color not Baseline Chrome 93, Firefox 92, Safari 26.2, iOS 26.2
appearance widely 2024-09-14 Chrome 84, Firefox 80, Safari 15.4, iOS 15.4
caret-color widely 2022-07-15 Chrome 57, Firefox 53, Safari 11.1, iOS 11.3
input-range widely 2019-09-16 Chrome 4, Firefox 23, Safari 3.1, iOS 5
field-sizing newly 2026-06-16 Chrome 123, Firefox 152, Safari 26.2, iOS 26.2
customizable-select not Baseline Chrome 135, Firefox -, Safari 27, iOS 27
cursor not Baseline Chrome 68, Firefox 27, Safari 11, iOS -
pointer-events widely 2018-01-29 Chrome 2, Firefox 3.6, Safari 4, iOS 3.2
user-select not Baseline Chrome 54, Firefox 69, Safari -, iOS -
resize not Baseline Chrome 4, Firefox 5, Safari 4, iOS -
interactivity not Baseline Chrome 135, Firefox -, Safari -, iOS -
| Özellik | Durum | Not |
|---|---|---|
color-scheme, appearance, caret-color | Widely available | appearance 2024-09'dan beri widely |
accent-color | Baseline değil | Chrome Android minimum contrast'ı korumuyor |
field-sizing | Newly available (2026-06) | Desteklemeyen tarayıcıda rows ve resize |
appearance: base-select | Baseline değil | Chrome 135, Safari 27; Firefox yok |
pointer-events | Widely available | Devre dışı bırakmaz |
user-select | Baseline değil | Safari 27.3'e kadar -webkit- prefix'i |
cursor, resize | Baseline değil | iOS yüzünden; masaüstünde her yerde |
interactivity: inert | Baseline değil | Yalnız Chromium; HTML inert widely |
Küçük Bir Deneme
★☆☆ Lab'de #light-check'in vurgu rengini #1d4ed8 yap ve 12-forms.mjs'te iki motorun da rengi değiştirmeden kullandığını doğrula.
★★☆ Bookstore'un kargo seçeneklerindeki radio'ları appearance: none ile daireye çevir; seçili durumu bir iç daireyle (::before, yalnız :checked iken) göster ve lab'deki forced colors ölçümünü bu radio'lara uygula.
★★★ Stepper'a min ve max'a gelindiğinde ilgili butonu disabled yapan kodu ekle; disabled butonun hem fareyle hem Enter ile tıklanamadığını 12-pages.mjs'e bir satır ekleyerek ölç.
Bir sonraki ve son yazıda tablolara ve yazdırmaya geçiyoruz: border-collapse, table-layout: fixed, para kolonları için tabular-nums, sticky tablo başlığı, @media print, @page ve print-color-adjust; Bookstore'un "My orders" sayfası ve yazdırılabilir faturası.