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

<progress> öğesiyle uyarlanabilir ve erişilebilir bir yükleme çubuğunun nasıl oluşturulacağına dair temel bilgiler.

Bu yayında, <progress> öğesiyle 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.

Chrome'da açık ve koyu, belirsiz, artış ve tamamlamanın demosu yapıldı.

Videoyu tercih ediyorsanız bu yayının YouTube sürümünü burada bulabilirsiniz:

Genel bakış

<progress> öğesi, kullanıcılara tamamlanma hakkında görsel ve sesli geri bildirim sağlar. Bu görsel geri bildirim; formda ilerleme, bilgi indirme veya yükleme, hatta ilerleme miktarının bilinmediği halde işin hâlâ devam ettiğini gösterme gibi senaryolar için değerlidir.

Bu GUI Yarışması, erişilebilirlik konusunda biraz çaba sarf etmek için mevcut HTML <progress> öğesiyle çalıştı. Renkler ve düzenler, bileşeni modernleştirmek ve tasarım sistemlerine daha iyi sığdırmak için yerleşik öğenin özelleştirme sınırlarını zorlar.

Her bir tarayıcıda, uyarlanabilir simgeye yukarıdan aşağıya bir genel bakış sağlayan açık ve koyu renkli sekmeler: Safari, Firefox, Chrome.
Demo; Firefox, Safari, iOS Safari, Chrome ve Android Chrome'da açık ve koyu şemalarla gösterilmiştir.

Markup

<progress> öğesini bir <label> içine sarmayı tercih ettim. Böylece açık ilişki özelliklerini atlayarak örtülü ilişkiye öncelik verebilirim. Ayrıca, yükleme durumundan etkilenen bir üst öğeyi de etiketledim. Böylece ekran okuyucu teknolojileri bu bilgiyi 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. Dolayısıyla ilerleme durumu 0 ile 1 arasındadır. Örneğin, max değeri 100 olarak ayarlanırsa aralık 0-100 olarak ayarlanır. 0 ve 1 sınırları içinde kalmayı seçtim, ilerleme değerlerini %0,5 veya %50’ye çevirdim.

Etiket sarmalama ilerleme durumu

Örtülü ilişkide, ilerleme öğesi şunun gibi bir etiketle sarmalanır:

<label>Loading progress<progress></progress></label>

Demomda, yalnızca ekran okuyucular için etiket eklemeyi seçtim. Bu işlem, etiket metnini bir <span> içine sarmalayarak ve etkili şekilde ekran dışında kalması için ona bazı stiller uygulanarak 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;
}

Geliştirici Araçları&#39;nda, &quot;yalnızca ekran için hazır&quot; öğesini gösteren ekran görüntüsü.

Alan, yükleme ilerleme durumundan etkileniyor

Sağlıklı bir görüşe sahipseniz, bir ilerleme göstergesini ilgili öğeler ve sayfa alanlarıyla ilişkilendirmek kolay olabilir, ancak görme engelli kullanıcılar için bu durum pek net 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 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'ten, görevin başında aria-busy öğesini true, tamamlandığında da false konumuna getirin.

Aria özelliği eklemeleri

<progress> öğesinin örtülü rolü progressbar olsa da bunu, bu dolaylı role sahip olmayan tarayıcılar için açık hale getirdim. Öğeyi açıkça bilinmeyen bir duruma sokmak için indeterminate özelliğini de ekledim. Bu, öğenin value ayarlanmamış olduğunu gözlemlemekten daha anlaşılırdır.

<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 odaklanmak, güncellenen ilerlemenin ne kadar ilerlediğini kullanıcıya bildirecektir.

Stiller

İlerleme öğesi, stil açısından biraz karmaşıktır. Yerleşik HTML öğelerinin seçilmesi zor olabilen özel gizli bölümleri vardır ve genellikle ayarlanacak sınırlı bir özellik grubu sunulur.

Düzen

Düzen stilleri, ilerleme öğesinin boyutunda ve etiket konumunda biraz esneklik sağlamak amacıyla tasarlanmıştır. Yararlı ama zorunlu olmayan bir ek görsel işaret olabilecek özel bir tamamlanma durumu eklenir.

<progress> Düzeni

İlerleme öğesinin genişliğine dokunulmaz, böylece tasarımda gereken alanla birlikte küçülebilir ve büyüyebilir. appearance ve border, none olarak ayarlanarak yerleşik stiller sadeleştirilir. Bu, her tarayıcının kendi öğesi 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. Böylece border-radius her zaman yuvarlanır. 1000px işlevine eş değerdir. Bu yöntemi kullanmayı tercih ediyorum, çünkü bu değeri ayarlayıp unutabileceğim (ve yazması 1000px değerinden daha kısa) kadar büyük bir değer olacak. Gerekirse değeri daha da büyütmek de çok kolay: 3 değerini 4 olarak değiştirmeniz yeterlidir. Bu durumda 1e4px, 10000px ile eşdeğerdir.

overflow: hidden kullanılıyor ve tartışmalı bir tarz. border-radius değerlerini kanala aktarma ve dolgu öğelerini izleme gibi birkaç işi kolaylaştırdı. Aynı zamanda, ilerlemenin hiçbir alt öğesinin öğenin dışında yaşayamayacağı anlamına da geliyordu. Bu özel ilerleme öğesinde bir başka yineleme overflow: hidden olmadan yapılabilir ve bu da, animasyonlar veya daha iyi tamamlanma durumları için bazı fırsatlar sağlayabilir.

İşlem tamamlandı

Bu noktada CSS seçiciler, maksimum değer ile değeri karşılaştırarak işin zor kısmını yapar. Bu seçiciler eşleşiyorsa ilerleme tamamlanmış olur. İşlem tamamlandığında sözde öğe oluşturulur ve ilerleme öğesinin sonuna eklenir. Böylece, sonuna güzel bir ek görsel işaret sağlanır.

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

Yükleme çubuğunun% 100 olduğunu ve sonda onay işaretinin gösterildiği ekran görüntüsü.

Renk

Tarayıcı, ilerleme öğesi için kendi renklerini getirir ve tek bir CSS özelliğiyle açık ve koyuya uyarlanabilir. Bu, tarayıcıya özgü bazı özel seçicilerle oluşturulabilir.

Açık ve koyu tarayıcı stilleri

Sitenizde koyu ve açık renkli uyarlanabilir <progress> öğelerini etkinleştirmek için color-scheme yeterlidir.

progress {
  color-scheme: light dark;
}

Tek mülk ilerleme durumu doldurulmuş rengi

Bir <progress> öğesinin renk tonunu ayarlamak için accent-color öğesini kullanın.

progress {
  accent-color: rebeccapurple;
}

accent-color özelliğine bağlı olarak parkur arka plan renginin açıktan koyuya dönüştüğüne dikkat edin. Tarayıcı, uygun kontrastı sağlıyor: Oldukça düzenli.

Tamamen özel açık ve koyu renkler

<progress> öğesinde, biri parkur rengi, diğeri de parkur ilerleme durumu rengi için olmak üzere iki özel özellik ayarlayın. prefers-color-scheme medya sorgusunda, izleme ve izleme ilerleme durumu için yeni renk değerlerini girin.

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

Öğeye programlı bir şekilde odaklanabilmesi için negatif bir sekme dizini vermiştik. :focus-visible kullanarak odağı özelleştirin ve daha akıllı odak halkası stilini etkinleştirin. Bu durumda, fare tıklaması ve odaklanması odak halkasını göstermez, ancak klavye tıklamalarını gösterir. Bu konuyu daha ayrıntılı bir şekilde ele alan YouTube videosunda incelemeye değer.

progress:focus-visible {
  outline-color: var(--_progress);
  outline-offset: 5px;
}

Yükleme çubuğunun çevresinde bir odak halkası olan ekran görüntüsü. Tüm renkler eşleşiyor.

Tarayıcılar genelinde özel stiller

Her bir tarayıcının gösterdiği <progress> öğesinin bölümlerini seçerek stilleri özelleştirin. İlerleme öğesi kullanımı tek bir etikettir ancak CSS sözde seçicileri aracılığıyla açığa çıkan birkaç alt öğeden oluşur. Ayarı etkinleştirirseniz Chrome Geliştirici Araçları size bu öğeleri gösterir:

  1. Sayfanızı sağ tıklayın ve Geliştirici Araçları'nı açmak için Öğeyi İncele'yi seçin.
  2. Geliştirici Araçları 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'sini göster onay kutusunu bulup etkinleştirin.

Geliştirici Araçları&#39;nda, kullanıcı aracısı gölge DOM&#39;un açığa çıkarılmasını etkinleştirebileceğiniz yerin 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 izin veren ::-webkit-progress-bar ve ::-webkit-progress-value öğelerini açığa çıkarır. Şimdilik, daha önce oluşturulan, açık ve koyuya uyum sağlayan özel özellikleri kullanarak background-color değerini 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, <progress> öğesinde yalnızca ::-moz-progress-bar sözde seçiciyi gösterir. Bu aynı zamanda parçanın renk tonunu doğrudan belirleyemeyeceğimiz 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 nerede bulunduğu.

Safari, iOS Safari, Firefox, Chrome ve Android&#39;de Chrome&#39;un çalışır durumda gösterildiği Hata Ayıklama Köşesi&#39;nin ekran görüntüsü.

Firefox'ta accent-color olarak ayarlanmış parkur rengi, iOS Safari'de ise açık mavi renkli bir parkur rengi olduğuna dikkat edin. Koyu modda da aynısı geçerlidir: Firefox'un koyu renkli bir izi vardır ancak bizim belirlediğimiz özel renge sahip değildir ve Webkit tabanlı tarayıcılarda çalışır.

Animasyonlar

Tarayıcının yerleşik sözde seçicileriyle çalışırken, bu genellikle sınırlı bir izin verilen CSS özelliği grubuyla yapılır.

Parçanın doldurulmasını gösteren animasyon

İlerleme öğesinin inline-size öğesine geçiş eklemek Chromium'da işe yarar ancak Safari'de işe yaramaz. Firefox, ::-moz-progress-bar öğesinde geçiş özelliği de 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, bir animasyon sağlayabilmek için biraz daha yaratıcı olduğum oluyor. Chromium için sözde bir öğe oluşturulur ve üç tarayıcı için de ileri geri animasyonlu bir renk geçişi uygulanır.

Özel özellikler

Özel özellikler birçok amaçla kullanılabilir, ancak favorilerimden biri sihirli görünen bir CSS değerine ad vermek. Aşağıda, oldukça karmaşık bir linear-gradient var, ancak güzel bir ad var. Özelliğin 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 birlikte gruplandıramadığımızdan, özel özellikler kodun DÜŞÜK kalmasına da yardımcı olur.

Animasyon kareleri

Amacınız, ileri geri hareket eden sonsuz bir animasyondur. Başlangıç ve bitiş animasyon kareleri CSS'de ayarlanır. Başlangıcına tekrar tekrar geri dönen bir animasyon oluşturmak için yalnızca bir animasyon karesi (50% konumundaki orta animasyon karesi) gerekir!

@keyframes progress-loading {
  50% {
    background-position: left; 
  }
}

Her bir tarayıcıyı hedefleme

Her tarayıcı, <progress> öğesinin kendisinde sözde öğelerin oluşturulmasına veya ilerleme çubuğuna animasyon uygulanmasına izin vermez. Parça animasyonunu, sahte öğeden çok tarayıcı destekler; bu yüzden, yapay öğelerden temel olarak ve animasyon çubuklarına yükseltme yapıyorum.

Chromium sözde öğe

Chromium, sözde öğeye izin verir: ::after, öğeyi örtecek bir konumla kullanılır. 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'ta, özel özellikler ve bir animasyon da sözde öğe ilerleme çubuğuna 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 ekran okuyucular için belgede yeterli bilginin bulunmasını sağlar.

const state = {
  val: null
}

Demoda, ilerleme durumunu kontrol etmek için düğmeler bulunur. Bu düğmeler state.val öğesini günceller ve daha sonra, 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şlevde kullanıcı arayüzü/kullanıcı deneyimi düzenlenir. setProgress() işlevi oluşturarak başlayın. state nesnesine, ilerleme öğesine ve <main> alt bölgesine erişimi olduğundan parametreye gerek yoktur.

const setProgress = () => {
  
}

<main> alt bölgesinde yükleme durumunu ayarlama

İ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 tutarı 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 getirecek.

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 sayılarla ilgili matematik sorunlarını düzeltme

Varsayılan ilerleme durumu (maksimum 1) olarak kalmayı seçtiğim için demo artış ve azaltma işlevleri ondalık matematiği kullanır. JavaScript ve diğer diller bu konuda her zaman mükemmel değildir. Matematik sonucundaki fazlalığı kırpacak bir roundDecimals() fonksiyonu aşağıda verilmiştir:

const roundDecimals = (val, places) =>
  +(Math.round(val + "e+" + places)  + "e-" + places)

Değeri sunulabilmesi ve okunabilmesi için 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 ayarlayın

Değer, DOM'da üç 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üncellendiğinde, gören kullanıcılar ilerlemenin değiştiğini görür, ancak ekran okuyucu kullanıcılarına henüz değişiklik duyurusu verilmez. <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()
}

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

Sonuç

Nasıl yaptığımı artık bildiğine göre siz de nasıl yapardınız? 🙂

Bir şans daha verilirse kesinlikle yapmak istediğim birkaç değişiklik var. Bence mevcut bileşeni temizlemeniz ve <progress> öğesinin sözde sınıf stil sınırlamaları olmadan bir tane derlemeyi denemeniz gerekiyor. Keşfetmeye değer!

Yaklaşımlarımızı çeşitlendirelim ve web'de geliştirme yapmanın tüm yollarını öğrenelim.

Bir demo oluşturun, bana tweet atın bağlantıları, aşağıdaki topluluk remiksleri bölümüne ekleyeceğim.

Topluluk remiksleri