Yükleme çubuğu bileşeni oluşturma

<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.

Chrome'da açık ve koyu, belirsiz, artan ve tamamlanma durumu gösteriliyor.

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.

Her tarayıcıda, uyarlanabilir simgenin üstten alta genel görünümünü gösteren açık ve koyu sekmeler: Safari, Firefox, Chrome.
Firefox, Safari, iOS Safari, Chrome ve Android Chrome'da açık ve koyu düzende gösterilen demo.

Brüt kar

<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 belirsizdir. 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

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 metninin bir <span> içine sarmalanması ve etkili bir şekilde ekran dışında kalması için metne bazı stiller uygulanmasıyla 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;
}

Yalnızca ekrana hazır öğesini gösteren devtools ekran görüntüsü.

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-busytrue'e, bittikten sonra false'e getirin.

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. 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 kadar ilerlediğini bildireceği için ekran okuyucu teknolojisi açısından önemlidir.

Stiller

İlerleme öğesi, stillendirme açısından 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 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 için bilimsel sayı gösterimi kullandığından 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 öğ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, 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);
}

%100&#39;e ulaşmış ve sonunda onay işareti gösteren yükleme çubuğunun ekran görüntüsü.

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. 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 tesis ilerleme dolgu rengi

Bir <progress> öğesinin tonunu ayarlamak için accent-color işlevini 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 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 odaklanma 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;
}

Yükleme çubuğunun etrafında odak halkası bulunan ekran görüntüsü. Renklerin tümü eşleşiyor.

Tarayıcılarda ö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 sözde CSS 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:

  1. Sayfanızı sağ tıklayın ve Öğeyi İncele'yi seçerek Geliştirici Araçları'nı açın.
  2. DevTools penceresinin sağ üst köşesindeki Ayarlar dişli simgesini tıklayın.
  3. Öğeler başlığı altında Kullanıcı aracısı gölge DOM'unu göster onay kutusunu bulup etkinleştirin.

Kullanıcı aracısı gölge DOM&#39;unun gösterilmesini DevTools&#39;ta etkinleştirebileceğiniz yeri gösteren ekran görüntüsü.

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);
}

İlerleme öğesinin iç öğelerini gösteren ekran görüntüsü.

Firefox stilleri

Firefox, yalnızca <progress> öğesinde ::-moz-progress-bar sözde seçicisini 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&#39;un ekran görüntüsü ve ilerleme öğesi bölümlerinin bulunduğu yer.

Safari, iOS Safari, Firefox, Chrome ve Android&#39;deki Chrome&#39;un yükleme çubuğunun çalıştığı gösterilen Hata Ayıklama Köşesi ekran görüntüsü.

Firefox'un accent-color olarak ayarlanmış bir kanal rengine sahipken iOS Safari'nin açık mavi bir kanal 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 öğesine geçiş eklemek Chromium'da işe yarar ancak Safari'de geçerli olmaz. 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 durumuna animasyon ekleme

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 özelliklerinden 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ı 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

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 animasyonunu sanal öğeden çok daha fazla tarayıcı desteklediğinden, temel olarak gerçek olmayan öğelerden ve animasyon çubuklarına 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ı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, ö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'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 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 yuvarlayın, böylece 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:

  1. <progress> öğesinin value özelliği.
  2. aria-valuenow özelliği.
  3. <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
}

İ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 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()
}

Yükleme çubuğunun ilerleme durumunu kullanıcıya okuyan Mac OS Voice Over uygulamasının ekran görüntüsü.

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.

Gelin, yaklaşımlarımızı çeşitlendirelim ve web'de içerik 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.

Topluluk remiksleri