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

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

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

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.
Firefox, Safari, iOS Safari, Chrome ve Android Chrome'da açık ve koyu düzende gösterilen demo.

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

Yalnızca ekran hazır öğesini gösteren geliştirici araçlarının ekran görüntüsü.

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

Yükleme çubuğunun% 100 olarak gösterildiği ve sonunda bir onay işareti gösterilen 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 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;
}

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

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:

  1. Sayfanızı sağ tıklayın ve Öğeyi İncele'yi seçerek Geliştirici Araçları'nı açın.
  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.

Kullanıcı aracısı gölge DOM&#39;sinin gösterilmesinin Geliştirici Araçları&#39;nda nerede etkinleştirileceğini 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, 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);
}

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

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&#39;un ve ilerleme öğesi bölümlerinin nerede bulunduğunun ekran görüntüsü.

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

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:

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

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

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.

Topluluk remiksleri