<progress>
öğesiyle renge uyarlanabilir ve erişilebilir bir yükleme çubuğunun nasıl oluşturulacağına dair temel bir genel bakış.
Bu gönderide, <progress>
öğesiyle renge uyarlanabilir ve erişilebilir bir yükleme çubuğunun nasıl oluşturulacağıyla ilgili düşünceleri paylaşmak istiyorum. Demoyu deneyin ve kaynağı görüntüleyin.
Video kullanmayı tercih ederseniz bu gönderinin YouTube versiyonunu kullanabilirsiniz:
Genel bakış
<progress>
öğesi, kullanıcılara etkinliğin tamamlandığına dair görsel ve sesli geri bildirim sağlar. Bu görsel geri bildirim; bir formda ilerleme, bilgi indirme veya yükleme bilgilerini görüntüleme, hatta ilerleme miktarının bilinmediğini ancak çalışmanın hâlâ devam ettiğini göstermek gibi senaryolar açısından değerlidir.
Bu GUI Yarışması, erişilebilirlik konusunda bir miktar çabadan tasarruf etmek için mevcut HTML <progress>
öğesiyle çalıştı. Renkler ve düzenler, bileşeni modernleştirmek ve tasarım sistemlerine daha iyi uymasını sağlamak için yerleşik öğe için özelleştirme sınırlarını zorlar.
![Her tarayıcıda bulunan açık ve koyu renkli sekmeler, uyarlanabilir simgeyle ilgili yukarıdan aşağıya genel bir bakış sunar: Safari, Firefox, Chrome.](https://web.dev/static/articles/building/a-loading-bar-component/image/light-dark-tabs-each-br-cd04389a437d1.png?authuser=5&hl=tr)
Markup
<progress>
öğesini bir <label>
içine sarmayı tercih ettim. Böylece dolaylı bir ilişkinin lehine olan açık ilişki özelliklerini atlayabilirim.
Ayrıca, yükleme durumundan etkilenen bir üst öğeyi de etiketledim. Böylece ekran okuyucu teknolojileri bu bilgileri kullanıcıya geri iletebilir.
<progress></progress>
value
yoksa öğenin ilerleme durumu belirsiz olur.
max
özelliği varsayılan olarak 1 değerine ayarlanır. Bu nedenle, ilerleme durumu 0 ile 1 arasındadır. Örneğin, max
değeri 100 olarak ayarlandığında aralık 0-100 olarak ayarlanır. 0 ve 1 sınırları içinde kalmayı seçtim
ve ilerleme değerlerini %0, 5 ya da %50’ye çevirdim.
Etiketle sarmalanmış ilerleme durumu
Dolaylı bir 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 metni etkin bir şekilde ekran dışında tutmak için metne 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 işleminin ilerlemesinden etkilenen alan
Sağlıklı bir vizyonunuz varsa ilerleme göstergesini ilgili öğelerle ve sayfa alanlarıyla ilişkilendirmek kolay olabilir, ancak görme engelli kullanıcılar için durum çok açık değildir. Yükleme tamamlandığında değişecek olan en üstteki öğeye aria-busy
özelliğini atayarak bunu iyileştirin.
Ayrıca aria-describedby
ile ilerleme ve yükleme alt bölgesi arasındaki ilişkiyi belirtin.
<main id="loading-zone" aria-busy="true">
…
<progress aria-describedby="loading-zone"></progress>
</main>
JavaScript'ten, görevin başında aria-busy
olarak true
, işiniz bittiğinde ise false
olarak ayarlayın.
Aria özelliği eklemeleri
Bir <progress>
öğesinin örtülü rolü progressbar
olsa da, bu örtülü role sahip olmayan tarayıcılar için bunu açıkça belirttim. Öğeyi açıkça bilinmeyen bir duruma getirmek için indeterminate
özelliğini de ekledim. Bu, öğenin value
ayarlanmamış olduğunu gözlemlemekten daha net olur.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
İlerleme öğesine JavaScript'ten odaklanılabilir hale getirmek için tabindex="-1"
kullanın. Bu, ekran okuyucu teknolojisi için önemlidir. Çünkü ilerleme durumu değiştikçe ilerlemeye odaklanılması, kullanıcıya güncellenen ilerlemenin ne kadarına ulaştığı konusunda duyurulur.
Stiller
İlerleme öğesi, stil olarak biraz karmaşıktır. Yerleşik HTML öğelerinin seçilmesi zor olabilen özel gizli bölümleri vardır ve genellikle yalnızca sınırlı sayıda özellik ayarlanır.
Düzen
Düzen stilleri, ilerleme öğesinin boyutunda ve etiket konumunda bir miktar esneklik sağlamak amacıyla tasarlanmıştır. Yararlı ancak zorunlu olmayan ek bir görsel işaret olabilecek özel tamamlama durumu eklenir.
<progress>
Düzen
İlerleme öğesinin genişliği değiştirilmeden bırakılır. Böylece, tasarım için gereken alanla küçültüp büyüyebilir. Yerleşik stiller, appearance
ve border
politikalarının none
olarak ayarlanmasıyla kaldırılır. Bu işlem, her tarayıcının öğesi için kendi stilleri olduğundan öğenin farklı tarayıcılarda normalleştirilebilmesi amacıyla 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 için bilimsel sayı gösterimi kullandığından border-radius
her zaman yuvarlanır. Eşdeğerdir: 1000px
. Amacım, belirleyip unutabileceğim kadar büyük bir değer
kullanmak (ve yazması 1000px
değerinden daha kısa) olduğu için bunu kullanmayı seviyorum. Gerekirse değeri daha da büyük hale getirmek de
çok kolay. 3'ü 4 olarak değiştirin, sonra 1e4px
, 10000px
değerine eşit olur.
overflow: hidden
kullanılıyor ve tartışmalı bir stil içeriyor. border-radius
değerlerini parçaya aktarmaya ve dolgu öğelerini izlemeye gerek kalmamak gibi bazı işlerin kolaylaşmasını sağlamanın yanı sıra, ilerlemeyi gösteren hiçbir alt öğenin öğe dışında yaşayamayacağı anlamına da geliyordu. Bu özel ilerleme öğesindeki başka bir iterasyon, overflow: hidden
olmadan da yapılabilir ve animasyonlar ya da daha iyi tamamlanma durumları için bazı fırsatlar sunabilir.
İşlem tamamlandı
CSS seçiciler, buradaki işin zor kısmını, maksimum değeri değerle karşılaştırarak yapar ve eşleşirse ilerleme tamamlanır. İşlem tamamlandığında, yapay bir öğe oluşturulur ve ilerleme öğesinin sonuna eklenir. Bu öğe, sonuna güzel bir 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 renklere uyarlanabilir. Tarayıcıya özgü bazı özel seçiciler, bunu temel alabilir.
Açık ve koyu tarayıcı stilleri
Sitenizde koyu ve açık renkli uyarlanabilir <progress>
öğesini etkinleştirmek için color-scheme
yeterlidir.
progress {
color-scheme: light dark;
}
Tek mülk ilerleme durumu dolgulu rengi
Bir <progress>
öğesinin tonunu ayarlamak için accent-color
işlevini kullanın.
progress {
accent-color: rebeccapurple;
}
Parça arka plan renginin accent-color
özelliğine bağlı olarak açıktan koyuya dönüştüğünü fark edeceksiniz. Tarayıcı, kontrastın uygun olmasını sağlıyor: Oldukça düzenli.
Tamamen özel açık ve koyu renkler
<progress>
öğesinde, biri kanal rengi, diğeri izleme ilerleme durumu rengi için olmak üzere iki özel özellik ayarlayın. prefers-color-scheme
medya sorgusunda, parça için yeni renk değerleri sağlayın ve ilerleme durumunu izleyin.
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%);
}
}
Odak stilleri
Daha önce, programatik olarak odaklanabilmesi için öğeye negatif sekme dizini vermiştik. Odağı özelleştirmek için :focus-visible
kullanarak daha akıllı odaklama halkası stilini etkinleştirin. Bu şekilde, fare tıklaması ve odak, odak halkasını göstermez ancak klavye tıklamaları gösterir. YouTube videosu bu konuyu daha ayrıntılı bir şekilde ele alıyor ve incelenmeye değer.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Tarayıcılarda özel stiller
Her tarayıcının gösterdiği bir <progress>
öğesinin bölümlerini seçerek stilleri özelleştirin. İlerleme öğesi tek bir etikettir, ancak sözde CSS seçicileri aracılığıyla gösterilen birkaç alt öğeden oluşur. Ayarı etkinleştirirseniz Chrome Geliştirici Araçları size şu öğeleri gösterir:
- Sayfanızı sağ tıklayın ve Öğeyi İncele'yi seçerek Geliştirici Araçları'nı açın.
- Geliştirici Araçları penceresinin sağ üst köşesindeki Ayarlar dişli simgesini tıklayın.
- Öğeler başlığı altında Kullanıcı aracısı gölge DOM'sini 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, açık ve koyu geçişe uyarlanan özel özellikleri kullanarak background-color
özelliğini 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çiciyi gösterir. Bu aynı zamanda parçanın tonunu doğrudan ayarlayamadığımız anlamına da gelir.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Firefox'ta accent-color
, iOS Safari'de açık mavi renkli bir parkur rengi gösterilir. Koyu modda da aynı durum geçerlidir: Firefox'ta koyu renkli bir yol vardır ancak
ayarladığımız özel renk yoktur ve Webkit tabanlı tarayıcılarda çalışır.
Animasyonlar
Tarayıcı yerleşik sözde seçicilerle çalışırken, genellikle sınırlı sayıda izin verilen CSS özelliği içerir.
Animasyonlar doldu
İlerleme öğesinin inline-size
öğesine geçiş eklemek Chromium'da işe yarar ancak Safari'de geçerli olmaz. Firefox da ::-moz-progress-bar
öğesinde bir geçiş özelliği kullanmaz.
/* Chromium Only 😢 */
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
transition: inline-size .25s ease-out;
}
:indeterminate
durumu canlandırılıyor
Burada animasyon sağlayabilmek için daha yaratıcı oluyorum. Chromium için sözde bir öğe oluşturulur ve üç tarayıcı için de ileri geri hareket eden animasyonlu bir gradyan uygulanır.
Özel özellikler
Özel özellikler birçok şey için idealdir, ancak benim favorilerimden biri, sihirli görünen CSS değerine isim vermektir. Aşağıdaki liste oldukça karmaşık bir linear-gradient
olsa da güzel bir ada sahiptir. Amacı ve kullanım alanları net bir şekilde anlaşılabilir.
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;
}
Tarayıcıya özgü bu seçicileri bir kez daha gruplandıramayacağımız için özel özellikler de kodun DURDUR durumda kalmasına yardımcı olur.
Animasyon kareleri
Hedef, ileri geri hareket eden sonsuz bir animasyon. Başlangıç ve bitiş animasyonları
CSS'de ayarlanır. Başladığı yere tekrar tekrar dönen bir animasyon oluşturmak için yalnızca bir animasyon karesi (50%
konumundaki ortadaki animasyon karesi) gerekir.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Her tarayıcıyı hedefleme
Her tarayıcı <progress>
öğesinin kendisinde sözde öğe oluşturulmasına izin vermez veya ilerleme çubuğunun canlandırmasına izin vermez. Parça animasyonunu sanal öğeden çok daha fazla tarayıcı desteklediğinden, temel olarak sözde öğeden animasyon çubuklarına geçiş yapıyorum.
Chromium sözde öğe
Chromium, öğeyi kapatmak için bir konumla kullanılan sözde öğeye izin verir: ::after
. Belirsiz özel özellikler kullanılıyor ve ileri geri animasyon çok iyi çalışıyor.
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, sözde öğe ilerleme çubuğuna özel özellikler ve bir animasyon 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'ta, sözde öğe ilerleme çubuğuna özel özellikler ve bir animasyon 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>
öğesiyle ilgili önemli bir rol oynar. Öğeye gönderilen değeri kontrol eder ve belgede ekran okuyucular için yeterli bilgi bulunduğundan emin olur.
const state = {
val: null
}
Demoda ilerlemeyi kontrol etmek için düğmeler bulunur. Bunlar, state.val
öğesini günceller ve
DOM'u güncellemek için bir işlev çağırır.
document.querySelector('#complete').addEventListener('click', e => {
state.val = 1
setProgress()
})
setProgress()
Bu işlev, kullanıcı arayüzü/kullanıcı deneyimi düzenlemesinin gerçekleştiği yerdir. Bir setProgress()
işlevi oluşturarak başlayın. state
nesnesine, ilerleme öğesine ve <main>
alt bölgesine erişimi olduğundan parametre gerekmez.
const setProgress = () => {
}
<main>
alt bölgesinde yükleme durumu ayarlanıyor
İlerlemenin tamamlanıp tamamlanmadığına bağlı olarak, ilgili <main>
öğesinin aria-busy
özelliğinde güncellenmesi gerekir:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Yükleme miktarı bilinmiyorsa özellikleri temizle
Değer bilinmiyorsa veya ayarlanmamışsa bu kullanımda null
, value
ve aria-valuenow
özelliklerini kaldırın. Bu işlem, <progress>
öğesini belirsiz hale getirir.
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 varsayılanı maksimum 1'e bağlı kalmaya karar verdiğim için demo artış ve azalma işlevlerinde ondalık sayı kullanılır. JavaScript ve diğer diller bu konuda her zaman çok iyi olmayabilir.
Matematik sonucundaki fazlalığı kaldıracak bir roundDecimals()
fonksiyonunu burada görebilirsiniz:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Değeri yuvarlayarak sunulması ve okunaklı olması için:
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'de üç konumda kullanılır:
<progress>
öğesininvalue
özelliği.aria-valuenow
özelliği.<progress>
içindeki 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
}
İlerlemeye odaklanma
Değerler güncellendikten sonra, göreceli kullanıcılar ilerlemedeki değişikliği görür, ancak ekran okuyucu kullanıcılarına henüz değişiklik duyurusu yapılmaz. <progress>
öğesine odaklandığınızda 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ç
Şimdi bunu nasıl yaptığımı öğrendiğinize göre siz nasıl ‽ 🙂
Bir şans daha verirsem kesinlikle birkaç değişiklik yapmak isterim. Mevcut bileşeni temizlemek için yer var ve <progress>
öğesinin sözde sınıf tarzı sınırlamaları olmadan bir bileşen oluşturmayı denemek için yer olduğunu düşünüyorum. Keşfetmeye değer.
Gelin, yaklaşımlarımızı çeşitlendirelim ve web'de içerik geliştirmenin tüm yollarını öğrenelim.
Demo oluşturup beni tweet'le bağlantıları oluşturduğumda bunu aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.