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

<progress> öğesiyle renge uyarlanabilir ve erişilebilir bir yükleme çubuğu oluşturma hakkında temel bilgiler.

Bu yayında, <progress> öğesiyle renge uyarlanabilir ve erişilebilir bir yükleme çubuğu oluşturma konusundaki düşüncelerimi paylaşmak istiyorum. Demoyu deneyin ve kaynağı görüntüleyin.

Açık ve koyu, belirsiz, artan ve tamamlanma stilleri Chrome'da gösterilir.

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

<progress> öğesi, kullanıcılara tamamlanma durumuyla ilgili görsel ve sesli geri bildirim sağlar. Bu görsel geri bildirim; formda ilerleme, indirme veya yükleme bilgilerini gösterme, hatta ilerleme miktarının bilinmediği ancak çalışmanın devam ettiği durumlar için değerlidir.

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

Her tarayıcıda, uyarlanabilir simgeye yukarıdan aşağıya genel bir bakış sunan açık ve koyu sekmeler: Safari, Firefox, Chrome.
Açık ve koyu temalarda Firefox, Safari, iOS Safari, Chrome ve Android Chrome'da gösterilen demo.

Brüt kar

<progress> öğesini <label> içine sarmayı tercih ettim. Böylece açık ilişki özelliklerini atlayıp örtülü bir ilişki kullanabildim. 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 belirsizdir. max özelliği varsayılan olarak 1'dir. Bu nedenle ilerleme durumu 0 ile 1 arasındadır. Örneğin, max ayarını 100 olarak belirlemek aralığı 0-100 olarak ayarlar. 0 ile 1 arasındaki sınırları aşmamayı tercih ettim ve ilerleme değerlerini 0, 5 veya %50 olarak çevirdim.

Etiket sarmalanmış ilerleme durumu

Örtülü ilişkide, ilerleme öğesi şu şekilde 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 <span> içine alarak ve ekranda görünmemesi için bazı stiller uygulayarak yapılır:

<label>
  <span class="sr-only">Loading progress</span>
  <progress></progress>
</label>

WebAIM'den aşağıdaki CSS ile birlikte:

.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 öğeyi gösteren geliştirici araçlarının ekran görüntüsü.

Yükleme işleminden etkilenen alan

Görüşünüz sağlıklıysa ilerleme göstergesini ilgili öğeler 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 sorunu düzeltebilirsiniz. Ayrıca, ilerleme durumu ile yükleme alanı arasında aria-describedby ile bir ilişki olduğunu belirtin.

<main id="loading-zone" aria-busy="true">
  …
  <progress aria-describedby="loading-zone"></progress>
</main>

JavaScript'te, görevin başında aria-busy simgesini true, tamamlandığında ise false olarak değiştirin.

Aria özelliği eklemeleri

<progress> öğesinin örtülü rolü progressbar olsa da bu örtülü role sahip olmayan tarayıcılar için rolü açık hale getirdim. Ayrıca, öğeyi açıkça bilinmeyen bir duruma getirmek için indeterminate özelliğini ekledim. Bu, öğede value ayarlanmadığını gözlemlemekten daha net bir yöntemdir.

<label>
  Loading 
  <progress 
    indeterminate 
    role="progressbar" 
    aria-describedby="loading-zone"
    tabindex="-1"
  >unknown</progress>
</label>

İlerleme öğesinin JavaScript'ten odaklanılabilir hale getirmek için tabindex="-1" kullanın. İlerleme durumu değiştikçe ilerleme durumuna odaklanmak, güncellenen ilerleme durumunun ne kadar ilerlediğini kullanıcıya bildireceğinden ekran okuyucu teknolojisi için önemlidir.

Stiller

İlerleme öğesinin stilini belirlemek biraz zordur. Yerleşik HTML öğelerinin, seçilmesi zor olabilen ve genellikle yalnızca sınırlı sayıda özelliğin ayarlanmasına olanak tanıyan özel gizli bölümleri vardır.

Düzen

Düzen stilleri, ilerleme öğesinin boyutu ve etiket konumunda bir miktar esneklik sağlamayı amaçlar. Faydalı olabilecek ancak zorunlu olmayan ek bir görsel ipucu olarak özel bir tamamlama durumu eklenir.

<progress> Düzen

Tasarımda gereken alana göre küçülüp büyüyebilmesi için ilerleme öğesinin genişliğine dokunulmaz. Yerleşik stiller, appearance ve border değerleri none olarak ayarlanarak kaldırılır. Bunun nedeni, her tarayıcının kendi öğesi için kendi stillerine sahip olmasıdır. Bu nedenle, öğenin tarayıcılarda normalleştirilmesi gerekir.

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österimini kullandığından border-radius her zaman yuvarlanır. 1000px ile eşdeğerdir. Bunu kullanmayı tercih ediyorum çünkü amacım, ayarlayıp unutabileceğim kadar büyük bir değer kullanmak (ve 1000px değerinden daha kısa). Gerekirse değeri daha da büyütmek kolay: 3'ü 4 olarak değiştirmeniz yeterli. Bu durumda 1e4px, 10000px değerine eşit olur.

overflow: hidden kullanılıyor ve tartışmalı bir stil oldu. Bu, border-radius değerlerinin parçaya ve parça doldurma öğelerine aktarılmasına gerek olmaması gibi bazı şeyleri kolaylaştırdı ancak ilerleme öğesinin hiçbir alt öğesinin öğenin dışında bulunamayacağı anlamına da geliyordu. Bu özel ilerleme öğesinin başka bir yinelemesi overflow: hidden olmadan yapılabilir ve animasyonlar veya daha iyi tamamlama durumları için bazı fırsatlar sunabilir.

İşlem tamamlandı

CSS seçiciler, maksimum değeri değerle karşılaştırarak zorlu işi yapar ve eşleşme olursa ilerleme tamamlanır. İşlem tamamlandığında, ilerleme öğesinin sonuna bir sözde öğe eklenir. Bu öğe, tamamlanma durumuyla ilgili 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&#39;e ulaştığını ve sonunda onay işareti gösterdiğini belirten ekran görüntüsü.

Renk

Tarayıcı, ilerleme öğesi için kendi renklerini kullanır ve yalnızca bir CSS özelliğiyle açık ve koyu temaya uyum sağlar. Bu, bazı özel tarayıcıya özgü seçicilerle geliştirilebilir.

Açık ve koyu tarayıcı stilleri

Sitenizi koyu ve açık renkli uyarlanabilir bir <progress> öğesine kaydetmek için tek yapmanız gereken color-scheme eklemektir.

progress {
  color-scheme: light dark;
}

Tek mülk ilerleme durumu dolgu rengi

<progress> öğesine renk tonu vermek için accent-color kullanın.

progress {
  accent-color: rebeccapurple;
}

accent-color bağlı olarak parça arka plan renginin açıktan koyuya değiştiğini fark edin. Tarayıcı, uygun kontrastı sağlıyor.

Tamamen özel açık ve koyu renkler

<progress> öğesinde iki özel özellik ayarlayın. Bunlardan biri parça rengi, diğeri ise parça ilerleme rengi için olmalıdır. 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, öğeye programatik olarak odaklanılabilmesi için negatif bir sekme dizini vermiştik. Daha akıllı odak halkası stilini etkinleştirmek için odaklamayı özelleştirmek üzere :focus-visible simgesini kullanın. Bu ayar etkinleştirildiğinde, fare tıklaması ve odaklama işlemi odak halkasını göstermez ancak klavye tıklamaları gösterir. Bu konu, YouTube videosunda daha ayrıntılı olarak ele alınmaktadır ve incelenmesi önerilir.

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ılar genelinde özel stiller

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

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

Geliştirici Araçları&#39;nda kullanıcı aracısı gölge DOM&#39;unun gösterilmesinin etkinleştirileceği 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 olanak tanıyan ::-webkit-progress-bar ve ::-webkit-progress-value özelliklerini kullanıma sunar. Şimdilik, açık ve koyu temaya uyum sağlayan, daha önce oluşturulmuş ö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 ::-moz-progress-bar sözde seçicisini <progress> öğesinde kullanıma sunar. Bu, parçaya doğrudan renk tonu uygulayamayacağı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 nerede bulunacağı.

Safari, iOS Safari, Firefox, Chrome ve Android&#39;de Chrome&#39;un yükleme çubuğunun çalıştığı gösterilen hata ayıklama köşesinin ekran görüntüsü.

Firefox'ta accent-color renkli bir iz, iOS Safari'de ise açık mavi bir iz olduğunu fark edeceksiniz. Koyu modda da aynı durum söz konusudur: Firefox'ta koyu bir parça bulunur ancak ayarladığımız özel renk yoktur ve bu özellik Webkit tabanlı tarayıcılarda çalışır.

Animasyon

Tarayıcıda yerleşik sözde seçicilerle çalışırken genellikle izin verilen CSS özelliklerinin sınırlı bir kümesi kullanılır.

İzin dolmasını canlandırma

İlerleme öğesinin inline-size kısmına geçiş eklemek Chromium'da çalışır ancak Safari'de çalışmaz. Firefox da ::-moz-progress-bar üzerinde geçiş özelliği kullanmaz.

/*  Chromium Only 😢  */
progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
  transition: inline-size .25s ease-out;
}

:indeterminate durumuna animasyon ekleme

Burada biraz daha yaratıcı davranarak animasyon ekleyebilirim. Chromium için bir sözde öğe oluşturulur ve üç tarayıcının tamamında ileri geri hareket eden bir gradyan uygulanır.

Özel özellikler

Özel özellikler birçok şey için harikadır ancak en sevdiğim kullanım alanlarından biri, aksi takdirde sihirli görünecek bir CSS değerine ad vermektir. Aşağıda, oldukça karmaşık olan 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;
}

Özel özellikler, tarayıcıya özgü bu seçicileri tekrar birlikte gruplandıramadığımız için kodun DRY kalmasına da yardımcı olur.

Animasyon kareleri

Amaç, ileri geri hareket eden sonsuz bir animasyon oluşturmaktır. Başlangıç ve bitiş anahtar kareleri CSS'de ayarlanır. Başladığı yere tekrar tekrar dönen bir animasyon oluşturmak için yalnızca bir animasyon karesi (50% konumundaki orta animasyon karesi) yeterlidir.

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

Her tarayıcıyı hedefleme

Her tarayıcı, <progress> öğesinde sözde öğelerin oluşturulmasına veya ilerleme çubuğunun animasyonlandırılmasına izin vermez. Parçayı animasyonlu hale getirmeyi destekleyen tarayıcı sayısı, sözde öğeleri destekleyen tarayıcı sayısından daha fazla olduğundan temel olarak sözde öğelerden animasyonlu çubuklara geçiş yapıyorum.

Chromium sözde öğesi

Chromium, öğeyi kaplamak için konumla birlikte kullanılan ::after sözde öğesine izin verir. Belirsiz özel özellikler kullanılır ve ileri geri animasyonu ç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 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 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> öğesiyle önemli bir rol oynar. Öğeye gönderilen değeri kontrol eder ve ekran okuyucular için dokümanda yeterli bilgi bulunduğundan emin olur.

const state = {
  val: null
}

Demoda ilerlemeyi kontrol etmek için düğmeler bulunur. Bu düğmeler state.val değerini 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()

Bu işlev, kullanıcı arayüzü/kullanıcı deneyimi düzenlemesinin gerçekleştiği yerdir. İşe bir işlev oluşturarak başlayın.setProgress() state nesnesine, ilerleme öğesine ve <main> bölgesine erişebildiği için parametre gerekmez.

const setProgress = () => {
  
}

<main> bölgesinde yükleme durumunu ayarlama

İlerlemenin tamamlanıp tamamlanmadığına bağlı olarak, ilgili <main> öğesinin aria-busy özelliğinin güncellenmesi gerekir:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)
}

Yükleme tutarı 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> durumunu 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

Varsayılan maksimum ilerleme değeri olan 1'i kullanmayı tercih ettiğim için demo artırma ve azaltma işlevlerinde ondalık matematik kullanılır. JavaScript ve diğer diller bu konuda her zaman iyi değildir. Matematik sonucundaki fazlalıkları kırpacak bir roundDecimals() işlevi aşağıda verilmiştir:

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

Değeri, sunulabilecek ve okunabilir 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 üç 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
}

İlerleme durumuna odaklanma

Değerler güncellendiğinde görme engelli kullanıcılar ilerleme durumundaki 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()
}

Mac OS Voice Over uygulamasının, yükleme çubuğunun ilerleme durumunu kullanıcıya okurken çekilmiş ekran görüntüsü.

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? 🙂

Başka bir şans verilirse kesinlikle yapmak istediğim birkaç değişiklik var. Mevcut bileşenin temizlenmesi ve <progress> öğesinin sözde sınıf stili sınırlamaları olmadan bir bileşen oluşturulması gerektiğini düşünüyorum. Keşfetmeye değer!

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim.

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

Topluluk remiksleri