shadcn/ui’ın size bıraktığı durumlar: yükleme, başarı, hata, boş ve azaltılmış hareket
shadcn/ui size temiz bir düğme verir, yükleme düğmesi değil. Her shadcn projesinin elle yazdığı zor arayüz durumları, onları doğru hissettiren zamanlamalarla.
Preet Suthar · · 5 dk okuma
- React
- shadcn/ui
- UX
- Accessibility
"shadcn button loading state" diye aratın, aynı şeyi yapan yüz parçacık bulursunuz: bir isLoading prop’u ekle, etiketi bir spinner ile değiştir, düğmeyi devre dışı bırak. Bunu gönderin, uygulamanız eklemeden öncekinden daha kötü hissettirir.
Bu shadcn/ui’a bir eleştiri değil. Size temiz bir başlangıç noktası verir ve durumları bilerek size bırakır. Ama bir arayüzün hissinin çoğu "durumlarda" yaşar ve neredeyse hiç kimse onlara zaman ayırmaz. Bu, gönderdiğim her bileşende ele aldığım durumların listesi ve "bitmiş"i "çalışıyor"dan ayıran belirli zamanlamalar.
1. Titremeyen yükleme
Naif yükleme düğmesi, tıkladığınız anda bir spinner gösterir. Çoğu istek 200ms’nin altında biter; dolayısıyla kullanıcının gördüğü bir spinner parlaması ve ardından sonuçtur. Titreme gibi okunur.
İki kural bunu düzeltir:
- Spinner’ı göstermeden önce bekleyin. İstek yaklaşık 150ms içinde biterse hiç göstermeyin.
- Bir kez gösterildikten sonra en az bir süre açık tutun. Yaklaşık 400ms. 30ms görünen bir spinner, hiç spinner olmamasından kötüdür.
Bu bir prop değil, bir kancadır. İşte biçimi:
track promise’i alır. Kanca, isteğin spinner’ı atlayacak kadar hızlı olup olmadığına karar verir, spinner’ı titremeyecek kadar uzun tutar ve promise sonuçlandığında düğmeyi başarı ya da hata durumuna taşır. HextaUI’ın useButtonFeedback ve useDelayedLoading kancaları tam olarak bunu yapar ve tek oturuşta okunacak kadar küçüktür.
Aynı gecikme kuralı skeleton’lar için de geçerli. 50ms görünüp içerikle değiştirilen bir skeleton gri bir parlamadır. 150ms geciktirin ve içerik geldiğinde etrafındaki hiçbir şeyi oynatmadan solarak gösterin.
2. Yerinde başarı ve hata
Bir kayıt başarılı olduktan sonra düğme bunu söylemelidir. Bir onay işaretiyle "Saved", yaklaşık iki saniye, sonra boşta durumuna dönüş. Başarısız olduktan sonra da düğme bunu söylemeli ve hatayı daha uzun, yaklaşık dört saniye tutmalıdır; çünkü insanların bir hatayı okuyup ne yapacağına karar vermesi zaman alır.
Kulağa geldiğinden daha önemli iki ayrıntı:
- İşaretçi düğmenin üzerindeyken sıfırlamayın. Biri üzerine geliyorsa okuyordur. Ayrıldıktan sonra sıfırlayın.
- İstek yoldayken tıklamaları yok sayın. Düğmeyi sekme sırasından çıkaran ve ekran okuyucuların atlamasına yol açan
disabledile değil.aria-busykullanın ve tıklamayı yok sayın.
Bir Switch’in aynı sorunu daha küçük bir alanda vardır. Çevirin, isteği gönderin ve istek başarısız olursa geri çevirin. Kullanıcının geri alımı görmesi gerekir, bu yüzden animasyonla yapın ve kayıt dışarıdayken kısa bir bekleme durumu gösterin.
3. Yazmayı cezalandırmadan geçersiz durumu göstermek
Bir gönderme denemesi alanı geçersiz bulduğunda alanı sallayın. Biri hâlâ yazarken asla sallamayın. Kural tetikleyicidir: gönderimde ya da blur’da doğrulama sallanır, değişimde doğrulama sallanmaz.
Sallanmanın kendisi kısa olmalı, yaklaşık 300ms içinde üç dört salınım; prefers-reduced-motion altında ise kenarlıkta bir renk değişimine dönüşür, başka hiçbir şeye değil.
Yerel :user-invalid burada yardımcı olur. Yalnızca kullanıcı etkileşime girdikten sonra eşleşir; böylece geçersiz durumu React’te bir touched bayrağı olmadan CSS’te stillendirebilirsiniz.
4. Boş bir durumdur, yokluk değil
Boş bir liste, yeni kullanıcıların ilk indiği yerdir ve varsayılan, üstünde başlık olan boş bir alandır. Her boş durumun üç şeye ihtiyacı vardır: alanın kasıtlı görünmesi için bir simge ya da illüstrasyon, burada neyin görüneceğini söyleyen tek bir cümle ve bir şeyin görünmesini sağlayan eylem.
Bunu kurmak ucuzdur ve neredeyse her zaman atlanır, çünkü özelliği kuran kişinin test verisi vardır. Arada bir veritabanınızı temizleyin.
5. Azaltılmış hareket "hareketsizlik" değildir
prefers-reduced-motion: reduce kullanıcıların kayda değer bir kısmında açık ve tembel uygulama küresel bir * { transition: none }. Bu, hareketin taşıdığı bilgiyi siler. Solma olmadan aniden açılan bir diyalog, solarak açılandan daha zor ayrıştırılır.
Kullandığım kural: ekranda hareket eden her şey solmaya dönüşür. Solan her şey solma kalır. Döngüde olan her şey durur. Süreler kısalır, sıfırlanmaz.
Kontrolü tek bir yere koyun ki her bileşen oradan okusun:
Easing eğrilerini ve süreleri de paylaşın. Her bileşen kendi cubic-bezier değerini seçerse uygulama altı kişi tarafından yapılmış gibi hissettirir, çünkü öyledir.
6. Kesebileceğiniz hareket
Bir akordeon açın, sonra açılış animasyonunun yarısında kapatın. Pek çok uygulamada önce açılmayı bitirir sonra kapanır, ya da zıplar. Doğru davranış, bulunduğu yerden geri dönmektir.
Web Animations API bunu kolaylaştırır: çalışan animasyona bir referans tutun ve bir sonraki geçişte geçerli zamanını okuyup o anki hesaplanmış yükseklikten animasyon yapın. height: auto üzerindeki CSS geçişleri bunu yapamaz; grid-template-rows: 0fr’dan 1fr’ye geçiş yapabilir ama yarı yolda tersine çevirmesi daha zordur. Kesmenize izin veren yaklaşımı seçin.
7. Dokunma hedefleri ve iOS yakınlaştırması
Telefonlarda bozulan ve dizüstünde asla görmeyeceğiniz iki şey:
- 16px altındaki input’lar iOS Safari’de sayfayı yakınlaştırır. Temanız başka yerde 15px olsa bile kaba işaretçilerde input metnini 16px tutun. Tek bir
@media (pointer: coarse)kuralı bunu halleder. - 44px altındaki dokunma hedefleri ıskalar. Düğmeler masaüstünde 32px yüksekliğinde olabilir ve aynı medya sorgusuyla dokunmatik ekranlarda büyüyebilir.
8. Sağdan sola
Arapça ya da İbranice kullanıcılara bir gün ürün gönderirseniz, bileşenlerinizdeki her ml-, pl- ve left- bir hatadır. Mantıksal özellikler kullanın: ms-, ps-, start-. shadcn/ui, Ocak 2026’da RTL desteği ekledi; dolayısıyla yeni çektiğiniz bileşenler sorunsuz, ama 2024’te kopyaladıklarınız değil.
Bu sizi nereye bırakıyor
Bunların hiçbiri zor değil. Hepsi birden, shadcn/ui boyutunda bir bileşen kümesinde birkaç haftalık iş ve bittiğinde görünmez; bu yüzden budanır.
HextaUI’ı bu işi her projede tekrar tekrar yaptığım için kurdum. Bir shadcn/ui projesine eklediğiniz ücretsiz, MIT lisanslı bir registry’dir. Aynı CLI, aynı bileşen adları, aynı tema token’ları ve yukarıdaki durumların her biri, 150ms spinner gecikmesine kadar hazır ele alınmış. Koda kendiniz sahip olup durumları kendiniz yazmayı tercih ederseniz kancalar bağımsızdır ve yalnızca onları kurabilirsiniz.
SSS
Yüklenirken düğmeyi devre dışı bırakmalı mıyım? aria-busy="true" ayarlayın ve tıklamaları yok sayın. disabled onu sekme sırasından çıkarır ve eylemin ortasında ekran okuyuculardan gizler.
Bir başarı durumu ne kadar gösterilmeli? Yaklaşık iki saniye, işaretçi hâlâ kontrolün üzerindeyse daha uzun. Hatalar yaklaşık dört saniye.
Skeleton her zaman spinner’dan daha mı iyi? Gelecek şeyin biçimini biliyorsanız skeleton daha iyi. Eylemler için spinner daha iyi. İkisine de 150ms gecikme gerekir.
Azaltılmış hareket tüm animasyonu kaldırmam gerektiği anlamına mı gelir? Hayır. Hareketi solmalarla değiştirin, döngüleri durdurun ve süreleri kısaltın. Bilgiyi koruyun.