<progress>
öğesi ile renk uyarlamalı ve erişilebilir bir yükleme çubuğu oluşturma hakkında temel bilgiler.
Bu yayında, <progress>
öğesini kullanarak renk uyumlu ve erişilebilir bir yükleme çubuğu oluşturma konusundaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin ve kaynağı görüntüleyin.
Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:
Genel Bakış
<progress>
öğesi, kullanıcılara tamamlama hakkında görsel ve işitsel geri bildirim sağlar. Bu görsel geri bildirim, formdaki ilerleme durumu, indirme veya yükleme bilgilerinin gösterilmesi ya da ilerleme miktarının bilinmediği ancak çalışmanın devam ettiği durumların gösterilmesi gibi senaryolar için değerlidir.
Bu GUI Challenge, erişilebilirlik konusunda biraz çabadan tasarruf etmek için mevcut HTML <progress>
öğesiyle çalıştı. Renkler ve düzenler, bileşeni modernize etmek ve tasarım sistemlerine daha iyi sığdırmak için yerleşik öğenin özelleştirme sınırlarını zorlar.
Brüt kar
Açık ilişki özelliklerini atlayıp örtülü ilişki kullanabilmek için <progress>
öğesini <label>
içine sarmaladım.
Ayrıca, yükleme durumundan etkilenen bir üst öğeyi etiketledim. Böylece ekran okuyucu teknolojileri bu bilgileri kullanıcıya iletebilir.
<progress></progress>
value
yoksa öğenin ilerleme durumu belirsiz olur.
max
özelliği varsayılan olarak 1 değerine ayarlanır. Bu nedenle ilerleme 0 ile 1 arasındadır. Örneğin, max
değerini 100 olarak ayarlamak aralığı 0-100 olarak ayarlar. 0 ile 1 sınırları arasında kalmayı tercih ederek ilerleme değerlerini 0, 5 veya %50 olarak çevirdim.
Etiket sarmalanmış ilerleme durumu
Örtük ilişkide ilerleme öğesi şuna benzer bir etiketle sarmalanır:
<label>Loading progress<progress></progress></label>
Demomda etiketi yalnızca ekran okuyucular için eklemeyi seçtim.
Bu işlem, etiket metnini bir <span>
içine sarmalayarak ve ekranda görünmeyecek şekilde bazı stiller uygulayarak yapılır:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
WebAIM'den aşağıdaki CSS ile:
.sr-only {
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
Yükleme ilerleme durumundan etkilenen alan
İyi bir görüşünüz varsa ilerleme göstergelerini ilgili öğelerle ve sayfa alanlarıyla ilişkilendirmek kolay olabilir ancak görme engelli kullanıcılar için bu durum o kadar net değildir. Yükleme tamamlandığında değişecek en üstteki öğeye aria-busy
özelliğini atayarak bu durumu iyileştirin.
Ayrıca, aria-describedby
ile ilerleme durumu ve yükleme bölgesi arasındaki ilişkiyi belirtin.
<main id="loading-zone" aria-busy="true">
…
<progress aria-describedby="loading-zone"></progress>
</main>
JavaScript'de, görevin başında aria-busy
'ü true
'e, bittikten sonra false
'e getirin.
Aria özelliği eklemeleri
<progress>
öğesinin örtük rolü progressbar
olsa da bu örtük role sahip olmayan tarayıcılar için açık rol olarak ayarladım. Ayrıca, öğeyi bilinmeyen bir duruma açıkça yerleştirmek için indeterminate
özelliğini de ekledim. Bu, öğenin value
değerinin ayarlanmadığını gözlemlemekten daha net bir durumdur.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
İlerleme öğesini JavaScript'den odaklanılabilir hale getirmek için tabindex="-1"
değerini kullanın. İlerleme değiştikçe ilerleme durumuna odaklanmak, kullanıcıya güncellenmiş ilerlemenin ne kadara ulaştığını bildireceğinden ekran okuyucu teknolojisi için önemlidir.
Stiller
İlerleme öğesi, stillendirme açısından biraz karmaşıktır. Yerleşik HTML öğelerinin, seçimi zor olabilecek özel gizli bölümleri vardır ve genellikle ayarlanabilecek sınırlı sayıda özellik sunar.
Düzen
Düzen stilleri, ilerleme öğesinin boyutu ve etiket konumu konusunda biraz esneklik sağlamak için tasarlanmıştır. Yararlı ancak gerekli olmayan ek bir görsel ipucu olabilecek özel bir tamamlama durumu eklenir.
<progress>
Düzen
İlerleme öğesinin genişliği, tasarımda gereken alanla birlikte küçülebileceği ve büyüyebileceği için değiştirilmez. Yerleşik stiller, appearance
ve border
değerleri none
olarak ayarlanarak kaldırılır. Bu, her tarayıcıda öğe için kendi stilleri olduğundan öğenin tarayıcılar arasında normalleştirilebilmesi için yapılır.
progress {
--_track-size: min(10px, 1ex);
--_radius: 1e3px;
/* reset */
appearance: none;
border: none;
position: relative;
height: var(--_track-size);
border-radius: var(--_radius);
overflow: hidden;
}
_radius
için 1e3px
değeri, büyük bir sayıyı ifade etmek üzere bilimsel sayı gösterimini kullanır. Bu nedenle border-radius
her zaman yuvarlanır. 1000px
değerine eş değer. Ayarlayıp unutabileceğim kadar büyük bir değer kullanmak istediğim için bunu kullanmayı tercih ediyorum (ayrıca 1000px
'ten daha kısa yazılıyor). Gerekirse daha da büyütmek de kolaydır: 3'ü 4'e değiştirdiğinizde 1e4px
, 10000px
ile eşdeğer olur.
overflow: hidden
kullanılıyor ve tartışmalı bir stil. Bu, border-radius
değerlerini parçaya ve parça doldurma öğelerine aktarmak gerekmemesi gibi bazı şeyleri kolaylaştırdı ancak ilerlemenin hiçbir alt öğesinin öğenin dışında bulunamayacağı anlamına da geliyordu. Bu özel ilerleme öğesi için overflow: hidden
olmadan başka bir iterasyon yapılabilir. Bu, animasyonlar veya daha iyi tamamlama durumları için bazı fırsatlar sunabilir.
İşlem tamamlandı
CSS seçicileri, maksimum değeri değerle karşılaştırarak zor işi yapar. Eşleşme varsa ilerleme tamamlanır. İşlem tamamlandığında, bir sözde öğe oluşturulur ve ilerleme öğesinin sonuna eklenir. Bu öğe, işlemin tamamlandığına dair güzel bir ek görsel ipucu sağlar.
progress:not([max])[value="1"]::before,
progress[max="100"][value="100"]::before {
content: "✓";
position: absolute;
inset-block: 0;
inset-inline: auto 0;
display: flex;
align-items: center;
padding-inline-end: max(calc(var(--_track-size) / 4), 3px);
color: white;
font-size: calc(var(--_track-size) / 1.25);
}
Renk
Tarayıcı, ilerleme öğesi için kendi renklerini getirir ve yalnızca bir CSS özelliğiyle açık ve koyu modlara uyum sağlar. Bu, tarayıcıya özel bazı seçicilerle geliştirilebilir.
Açık ve koyu tarayıcı stilleri
Sitenizi koyu ve açık temaya uyumlu bir <progress>
öğesine dönüştürmek için tek yapmanız gereken color-scheme
seçeneğini etkinleştirmektir.
progress {
color-scheme: light dark;
}
Tek tesis ilerleme dolgu rengi
Bir <progress>
öğesini renklendirmek için accent-color
simgesini kullanın.
progress {
accent-color: rebeccapurple;
}
Parçanın arka plan renginin, accent-color
değerine bağlı olarak açıktan koyuya değiştiğini fark edin. Tarayıcı, doğru kontrastı sağlıyor: Oldukça şık.
Tamamen özel açık ve koyu renkler
<progress>
öğesinde biri parça rengi, diğeri parça ilerleme rengi olmak üzere iki özel özellik ayarlayın. prefers-color-scheme
medya sorgusunda parça ve parça ilerleme durumu için yeni renk değerleri sağlayın.
progress {
--_track: hsl(228 100% 90%);
--_progress: hsl(228 100% 50%);
}
@media (prefers-color-scheme: dark) {
progress {
--_track: hsl(228 20% 30%);
--_progress: hsl(228 100% 75%);
}
}
Odaklanma stilleri
Daha önce, programatik olarak odaklanabilmesi için öğeye negatif bir sekme dizini vermiştik. Daha akıllı odak halkası stilini etkinleştirmek için odağı özelleştirmek üzere :focus-visible
simgesini kullanın. Bu durumda, fare tıklaması ve odak, odak halkasını göstermez ancak klavye tıklamaları gösterir. Bu konuyu daha ayrıntılı olarak ele alan YouTube videosunu incelemenizi öneririz.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Tarayıcılar genelinde özel stiller
<progress>
öğesinin her tarayıcının gösterdiği bölümlerini seçerek stilleri özelleştirin. İlerleme öğesi tek bir etikettir ancak CSS sözde seçicileri aracılığıyla gösterilen birkaç alt öğeden oluşur. Chrome Geliştirici Araçları, ayarı etkinleştirirseniz aşağıdaki öğeleri gösterir:
- Sayfanızı sağ tıklayıp Öğeyi İncele'yi seçerek Geliştirici Araçları'nı açın.
- DevTools penceresinin sağ üst köşesindeki Ayarlar dişli simgesini tıklayın.
- Öğeler başlığı altında Kullanıcı aracısı gölge DOM'unu göster onay kutusunu bulup etkinleştirin.
Safari ve Chromium stilleri
Safari ve Chromium gibi WebKit tabanlı tarayıcılar, CSS'nin bir alt kümesinin kullanılmasına olanak tanıyan ::-webkit-progress-bar
ve ::-webkit-progress-value
öğelerini gösterir. Şimdilik, daha önce oluşturulan ve açık ve koyuya uyum sağlayan özel özellikleri kullanarak background-color
'ü ayarlayın.
/* Safari/Chromium */
progress[value]::-webkit-progress-bar {
background-color: var(--_track);
}
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
}
Firefox stilleri
Firefox, yalnızca <progress>
öğesinde ::-moz-progress-bar
sözde seçicisini gösterir. Bu, parçayı doğrudan renklendiremeyeceğimiz anlamına da gelir.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Firefox'un accent-color
olarak ayarlanmış bir parça rengine sahipken iOS Safari'nin açık mavi bir parça rengine sahip olduğunu unutmayın. Koyu modda da aynıdır: Firefox'ta koyu bir parça vardır ancak belirlediğimiz özel renk değil ve Webkit tabanlı tarayıcılarda çalışır.
Animasyon
Tarayıcı yerleşik sözde seçicileriyle çalışırken genellikle izin verilen CSS özelliklerinin sınırlı bir kümesiyle çalışırsınız.
Parçanın dolmasını animasyonlu olarak gösterme
İlerleme öğesinin inline-size
bölümüne geçiş eklemek Chromium'da işe yarar ancak Safari'de işe yaramaz. Firefox, ::-moz-progress-bar
özelliğinde geçiş özelliği de kullanmaz.
/* Chromium Only 😢 */
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
transition: inline-size .25s ease-out;
}
:indeterminate
durumunu animasyonlu hale getirme
Burada, animasyon oluşturmak için biraz daha yaratıcı oluyorum. Chromium için bir sözde öğe oluşturulur ve üç tarayıcıda da ileri geri animasyonlu bir degrade uygulanır.
Özel özellikler
Özel özellikler birçok şey için mükemmeldir ancak en sevdiğim özelliklerden biri, büyülü görünen bir CSS değerine isim vermektir. Aşağıda, oldukça karmaşık ancak güzel bir ada sahip bir linear-gradient
gösterilmektedir. Amacını ve kullanım alanlarını net bir şekilde anlayabilmelisiniz.
progress {
--_indeterminate-track: linear-gradient(to right,
var(--_track) 45%,
var(--_progress) 0%,
var(--_progress) 55%,
var(--_track) 0%
);
--_indeterminate-track-size: 225% 100%;
--_indeterminate-track-animation: progress-loading 2s infinite ease;
}
Özel özellikler, bu tarayıcıya özgü seçicileri bir araya getiremeyeceğimiz için kodun DRY kalmasına da yardımcı olur.
Animasyon kareleri
Amaç, ileri geri giden sonsuz bir animasyon oluşturmaktır. Başlangıç ve bitiş ana kareleri CSS'de ayarlanır. Başlangıç noktasına tekrar tekrar dönen bir animasyon oluşturmak için yalnızca bir animasyon karesi (50%
'teki orta animasyon karesi) gerekir.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Her tarayıcıyı hedefleme
Bazı tarayıcılar, <progress>
öğesinin kendisinde sözde öğe oluşturmaya veya ilerleme çubuğunun animasyonlu olmasına izin vermez. Parçanın animasyonu, sözde öğelerden daha fazla tarayıcı tarafından desteklenir. Bu nedenle, temel olarak sözde öğelerden animasyonlu çubuklara geçiş yapıyorum.
Chromium sözde öğesi
Chromium, öğeyi kapsayacak şekilde konumla birlikte kullanılan ::after
sözde öğesine izin verir. Belirsiz özel özellikler kullanılır ve ileri geri animasyon çok iyi çalışır.
progress:indeterminate::after {
content: "";
inset: 0;
position: absolute;
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
Safari ilerleme çubuğu
Safari'de, özel özellikler ve bir animasyon sözde öğe ilerleme çubuğuna uygulanır:
progress:indeterminate::-webkit-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
Firefox ilerleme çubuğu
Firefox için özel özellikler ve animasyon, sözde öğe ilerleme çubuğuna da uygulanır:
progress:indeterminate::-moz-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
JavaScript
JavaScript, <progress>
öğesinde önemli bir rol oynar. Öğeye gönderilen değeri kontrol eder ve belgede ekran okuyucular için yeterli bilginin bulunduğundan emin olur.
const state = {
val: null
}
Demoda, ilerlemeyi kontrol etmek için düğmeler sunulur. Bu düğmeler state.val
öğesini günceller ve ardından DOM'u güncellemek için bir işlev çağırır.
document.querySelector('#complete').addEventListener('click', e => {
state.val = 1
setProgress()
})
setProgress()
Kullanıcı arayüzü/kullanıcı deneyimi koordinasyonunun gerçekleştiği yer bu işlevdir. setProgress()
işlevi oluşturarak başlayın. state
nesnesine, ilerleme öğesine ve <main>
bölgesine erişimi olduğundan parametreye gerek yoktur.
const setProgress = () => {
}
<main>
bölgesindeki yükleme durumunu ayarlama
İlerleme durumunun tamamlanıp tamamlanmadığına bağlı olarak, ilgili <main>
öğesinin aria-busy
özelliğinde güncelleme yapılması gerekir:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Yükleme miktarı bilinmiyorsa özellikleri temizleme
Değer bilinmiyorsa veya ayarlanmamışsa bu kullanımda null
, value
ve aria-valuenow
özelliklerini kaldırın. Bu işlem, <progress>
değerini belirsiz olarak değiştirir.
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
}
JavaScript ondalık matematik sorunlarını düzeltme
İlerleme durumunun varsayılan maksimum değerini 1 olarak kullanmayı tercih ettiğim için demo artma ve eksiltme işlevleri ondalık matematik kullanır. JavaScript ve diğer diller bu konuda her zaman iyi değildir.
Aşağıda, matematik sonucunun fazlalıklarını kırpacak bir roundDecimals()
işlevi verilmiştir:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Değeri, sunulabilecek ve okunaklı olacak şekilde yuvarlayın:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
}
Ekran okuyucular ve tarayıcı durumu için değer ayarlama
Değer, DOM'da üç yerde kullanılır:
<progress>
öğesininvalue
özelliği.aria-valuenow
özelliği.<progress>
iç metin içeriği.
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
progress.value = val
progress.setAttribute('aria-valuenow', valPercent)
progress.innerText = valPercent
}
İlerleme durumuna odaklanma
Değerler güncellendiğinde, görme engeli olmayan kullanıcılar ilerleme durumunun değiştiğini görür ancak ekran okuyucu kullanıcılarına henüz değişiklik duyurusu yapılmamıştır. <progress>
öğesine odaklanın. Tarayıcı güncellemeyi duyurur.
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
const val = roundDecimals(state.val, 2)
const valPercent = val * 100 + "%"
progress.value = val
progress.setAttribute('aria-valuenow', valPercent)
progress.innerText = valPercent
progress.focus()
}
Sonuç
Bunu nasıl yaptığımı öğrendiğinize göre, siz ne yapardınız? 🙂
Tekrar şansım olsaydı kesinlikle birkaç değişiklik yapmak isterdim. Mevcut bileşeni temizlemek ve <progress>
öğesinin sözde sınıf stili sınırlamaları olmadan bir bileşen oluşturmaya çalışmak için yer olduğunu düşünüyorum. Bu konuyu incelemeye değer.
Yaklaşımlarımızı çeşitlendirelim ve web'de uygulama geliştirmenin tüm yollarını öğrenelim.
Bir demo oluşturun, bağlantılarını bana tweetleyin. Ardından, aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.