Membuat komponen panel pemuatan

Ringkasan dasar tentang cara membangun status pemuatan yang adaptif warna dan dapat diakses dengan elemen <progress>.

Dalam postingan ini saya ingin berbagi pemikiran tentang cara membangun warna yang adaptif dan panel pemuatan yang dapat diakses dengan elemen <progress>. Coba demo dan lihat sumber Google Cloud Anda.

Terang dan gelap, tidak tentu, meningkat, dan selesai didemonstrasikan di Chrome.

Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:

Ringkasan

Tujuan <progress> elemen menyediakan umpan balik visual dan terdengar kepada pengguna tentang penyelesaian proyek. Ini masukan visual penting untuk skenario seperti: kemajuan dalam melalui formulir, menampilkan informasi download atau upload, atau bahkan menunjukkan bahwa jumlah progres tidak diketahui tetapi pekerjaan masih aktif.

Tantangan GUI ini bekerja dengan elemen <progress> HTML yang ada untuk menghemat upaya aksesibilitas. Tujuan warna dan tata letak mendorong batas-batas penyesuaian untuk elemen bawaan, untuk memodernisasi komponen dan membuatnya lebih cocok dalam sistem desain.

Tab terang dan gelap di setiap browser menyediakan 
    ringkasan ikon adaptif dari atas ke bawah: 
    Safari, Firefox, Chrome.
Demo yang ditampilkan di Firefox, Safari, iOS Safari, Chrome, dan Android Chrome dalam skema terang dan gelap.

Markup

Saya memilih untuk menggabungkan elemen <progress> dalam <label> jadi Saya dapat melewati atribut hubungan eksplisit dan menggantinya dengan hubungan akun. Saya juga telah memberi label elemen induk yang terpengaruh oleh status pemuatan, jadi teknologi pembaca dapat menyampaikan informasi itu kembali kepada pengguna.

<progress></progress>

Jika tidak ada value, progres elemen akan tidak tentu. Atribut max ditetapkan secara default ke 1, sehingga progresnya antara 0 dan 1. Menyetel max ke 100, misalnya, akan menetapkan rentang ke 0-100. Saya memilih untuk tetap berada di angka 0, dan 1 batas, menerjemahkan nilai kemajuan menjadi 0,5 atau 50%.

Progres yang digabungkan dengan label

Dalam hubungan implisit, elemen progres digabungkan dengan label seperti ini:

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

Dalam demo saya, saya memilih untuk menyertakan label untuk pembaca layar saja. Hal ini dilakukan dengan menggabungkan teks label dalam <span> dan menerapkan beberapa gaya agar alat ini bisa benar-benar keluar dari layar:

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

Dengan CSS yang disertakan dari WebAIM berikut:

.sr-only {
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

Screenshot devtools yang menampilkan elemen khusus layar hanya tersedia.

Area yang terpengaruh oleh progres pemuatan

Jika Anda memiliki penglihatan yang sehat, mudah untuk mengaitkan indikator kemajuan dengan elemen dan area halaman terkait, namun untuk pengguna penyandang gangguan penglihatan, begitu jelas. Tingkatkan hal ini dengan menetapkan aria-busy ke elemen paling atas yang akan berubah saat pemuatan selesai. Selain itu, tunjukkan hubungan antara progres dan zona pemuatan dengan aria-describedby

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

Dari JavaScript, alihkan aria-busy ke true di awal tugas, dan false setelah selesai.

Penambahan atribut Aria

Meskipun peran implisit elemen <progress> adalah progressbar, saya telah membuatnya vulgar bagi browser yang tidak memiliki peran implisit tersebut. Saya juga telah menambahkan atribut indeterminate untuk secara eksplisit menempatkan elemen ke dalam status tidak diketahui, yaitu lebih jelas daripada mengamati elemen yang belum ditetapkan value.

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

Gunakan tabindex="-1" untuk membuat elemen progres dapat difokuskan dari JavaScript. Hal ini penting bagi teknologi {i>screen reader<i}, karena memberikan fokus pada kemajuan ketika kemajuan berubah, akan memberi tahu pengguna seberapa jauh progres yang telah diperbarui.

Gaya

Elemen progres agak rumit dalam hal penataan gaya. HTML Bawaan elemen memiliki bagian tersembunyi khusus yang mungkin sulit untuk dipilih dan sering hanya menawarkan serangkaian properti terbatas untuk ditetapkan.

Tata Letak

Gaya tata letak dimaksudkan untuk memberikan fleksibilitas dalam prosesnya ukuran dan posisi label elemen. Menambahkan status penyelesaian khusus yang dapat menjadi petunjuk visual tambahan yang berguna, tetapi tidak diperlukan.

Tata Letak <progress>

Lebar elemen progres tidak disentuh sehingga dapat menciut dan membesar dengan ruang yang dibutuhkan dalam desain. Gaya {i>built-in<i} dihilangkan oleh menyetel appearance dan border ke none. Hal ini dilakukan agar elemen dapat dinormalkan di seluruh browser, karena setiap browser memiliki gaya sendiri untuk .

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

Nilai 1e3px untuk _radius menggunakan bilangan ilmiah notasi untuk mengekspresikan angka besar sehingga border-radius selalu dibulatkan. Ini setara dengan 1000px. Saya suka menggunakan ini karena tujuan saya adalah menggunakan nilai yang cukup besar sehingga Saya dapat menyetelnya dan melupakannya (dan penulisannya lebih pendek daripada 1000px). Ini juga mudah untuk membuatnya lebih besar lagi jika diperlukan: cukup ubah 3 menjadi 4, maka 1e4px adalah setara dengan 10000px.

overflow: hidden digunakan dan telah menjadi gaya yang bertentangan. Hal itu menghasilkan beberapa hal-hal mudah, seperti tidak perlu meneruskan nilai border-radius ke melacak, dan melacak elemen isian; tapi itu juga berarti tidak ada anak-anak dari kemajuan bisa hidup di luar elemen. Iterasi lain pada progres kustom ini elemen dapat dilakukan tanpa overflow: hidden dan mungkin membuka beberapa peluang untuk menghasilkan animasi atau status penyelesaian yang lebih baik.

Proses selesai

Pemilih CSS melakukan pekerjaan sulit di sini dengan membandingkan nilai maksimum dengan nilai, dan jika cocok, progres sudah selesai. Setelah selesai, elemen semu akan dibuat dan ditambahkan ke bagian akhir elemen progres, sehingga memberikan isyarat visual tambahan yang bagus untuk penyelesaian.

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

Screenshot panel pemuatan 100% dan menampilkan tanda centang di bagian akhir.

Warna

Browser menghadirkan warnanya sendiri untuk elemen progres, dan adaptif untuk terang dan gelap hanya dengan satu properti CSS. Model ini dapat dikembangkan dengan beberapa {i>browser<i} khusus yang khusus digunakan.

Gaya browser terang dan gelap

Untuk mengikutsertakan situs Anda ke elemen <progress> adaptif gelap dan terang, Anda hanya perlu color-scheme.

progress {
  color-scheme: light dark;
}

Warna terisi progres properti tunggal

Untuk mewarnai elemen <progress>, gunakan accent-color.

progress {
  accent-color: rebeccapurple;
}

Perhatikan bahwa warna latar belakang trek berubah dari terang ke gelap tergantung pada accent-color. Browser memastikan kontras yang tepat: cukup rapi.

Warna terang dan gelap yang sepenuhnya kustom

Tetapkan dua properti khusus pada elemen <progress>, satu untuk warna trek dan satu lagi untuk warna kemajuan lintasan. Di dalam prefers-color-scheme kueri media, memberikan nilai warna baru untuk jalur dan melacak kemajuan.

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

Memfokuskan gaya

Sebelumnya, kita memberi elemen indeks tab negatif sehingga dapat digunakan secara terprogram tetap fokus. Gunakan :focus-visible ke sesuaikan fokus untuk memilih ikut serta dalam gaya lingkaran fokus yang lebih cerdas. Dengannya, mouse klik dan fokus tidak akan menampilkan cincin fokus, tetapi klik keyboard akan melakukannya. Tujuan Video YouTube membahas hal ini secara lebih mendalam dan layak untuk ditinjau.

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

Screenshot panel pemuatan dengan cincin fokus di sekelilingnya. Semua warna cocok.

Gaya kustom di seluruh browser

Sesuaikan gaya dengan memilih bagian dari elemen <progress> yang masing-masing ekspos browser. Menggunakan elemen progres adalah satu tag, tetapi terdiri dari beberapa elemen turunan yang diekspos melalui pemilih pseudo CSS. DevTools Chrome akan menampilkan elemen ini jika Anda mengaktifkan setelan:

  1. Klik kanan di halaman Anda dan pilih Periksa Elemen untuk membuka DevTools.
  2. Klik roda gigi Settings di sudut kanan atas jendela DevTools.
  3. Di bawah judul Elements, cari dan aktifkan Tampilkan bayangan agen pengguna DOM.

Screenshot tempat di DevTools untuk mengaktifkan eksposur shadow DOM agen pengguna.

Gaya Safari dan Chromium

Browser berbasis WebKit seperti Safari dan Chromium mengekspos ::-webkit-progress-bar dan ::-webkit-progress-value, yang memungkinkan subset CSS yang akan digunakan. Untuk saat ini, tetapkan background-color menggunakan properti kustom yang dibuat sebelumnya, yang beradaptasi dengan terang dan gelap.

/*  Safari/Chromium  */
progress[value]::-webkit-progress-bar {
  background-color: var(--_track);
}

progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
}

Screenshot yang menunjukkan elemen dalam dari elemen progres.

Gaya Firefox

Firefox hanya mengekspos pemilih pseudo ::-moz-progress-bar pada elemen <progress>. Hal ini juga berarti kami tidak dapat memberi warna pada trek secara langsung.

/*  Firefox  */
progress[value]::-moz-progress-bar {
  background-color: var(--_progress);
}

Screenshot Firefox dan tempat menemukan bagian elemen progres.

Screenshot Pojok Proses Debug tempat Safari, iOS Safari, 
  Firefox, Chrome, dan Chrome di Android memiliki status pemuatan yang terlihat berfungsi.

Perhatikan bahwa Firefox memiliki warna trek yang ditetapkan dari accent-color saat iOS Safari memiliki trek berwarna biru muda. Demikian juga dalam mode gelap: Firefox memiliki jalur gelap tetapi bukan warna khusus yang telah kita tetapkan, dan bekerja di {i>browser<i} berbasis Webkit.

Animasi

Saat bekerja dengan pemilih semu bawaan browser, biasanya dengan sekumpulan properti CSS yang diizinkan.

Menganimasikan trek yang terisi

Menambahkan transisi ke inline-size dari elemen progres berfungsi untuk Chromium, tetapi tidak untuk Safari. Firefox juga melakukan tidak menggunakan properti transisi pada ::-moz-progress-bar-nya.

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

Menganimasikan status :indeterminate

Di sini saya menjadi sedikit lebih kreatif sehingga saya dapat memberikan animasi. Elemen semu untuk Chromium dibuat dan gradien diterapkan dengan animasi kembali dan untuk ketiga browser.

Properti khusus

Properti khusus cocok untuk banyak hal, tetapi salah satu favorit saya adalah memberi nama pada nilai CSS yang tampak ajaib. Mengikuti adalah kompleks linear-gradient, tapi dengan nama yang bagus. Tujuan dan kasus penggunaannya dapat dipahami dengan jelas.

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

Properti khusus juga akan membantu kode tetap KERAS karena sekali lagi, kita tidak dapat mengelompokkan pemilih khusus browser tersebut.

Keyframe

Tujuannya adalah animasi tanpa batas yang bolak-balik. Awal dan akhir keyframe akan ditetapkan dalam CSS. Hanya satu keyframe yang diperlukan, keyframe tengah di 50%, untuk membuat animasi yang kembali ke tempat awal tersebut dimulai, berulang kali, coba lagi!

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

Menargetkan setiap browser

Tidak semua browser mengizinkan pembuatan elemen pseudo di <progress> elemen itu sendiri atau memungkinkan animasi bilah kemajuan. Dukungan browser lainnya menganimasikan jalur daripada elemen pseudo, jadi saya mengupgrade dari elemen dasar dan berubah menjadi batang animasi.

Elemen semu Chromium

Chromium mengizinkan elemen pseudo: ::after digunakan dengan posisi menutupi elemen ini. Properti khusus yang tidak tentu digunakan, dan bagian belakang serta animasi maju akan bekerja dengan sangat baik.

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);
}
Status progres Safari

Untuk Safari, properti khusus dan animasi diterapkan ke status progres elemen semu:

progress:indeterminate::-webkit-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
Status progres Firefox

Untuk Firefox, properti khusus dan animasi juga diterapkan ke status progres elemen semu:

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 memainkan peran penting dengan elemen <progress>. Mengontrol nilai yang dikirim ke elemen dan memastikan informasi yang cukup ada dalam untuk pembaca layar.

const state = {
  val: null
}

Demo ini menawarkan tombol untuk mengontrol progres; mereka memperbarui state.val dan kemudian memanggil fungsi untuk memperbarui DOM.

document.querySelector('#complete').addEventListener('click', e => {
  state.val = 1
  setProgress()
})

setProgress()

Fungsi ini adalah tempat orkestrasi UI/UX terjadi. Mulai dengan membuat Fungsi setProgress(). Tidak ada parameter yang diperlukan karena memiliki akses ke Objek state, elemen progres, dan zona <main>.

const setProgress = () => {
  
}

Menetapkan status pemuatan di zona <main>

Bergantung pada apakah progres selesai atau belum, <main> yang terkait membutuhkan pembaruan ke aria-busy :

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

Hapus atribut jika jumlah pemuatan tidak diketahui

Jika nilai tidak diketahui atau tidak disetel, null dalam penggunaan ini, hapus value dan Atribut aria-valuenow. Tindakan ini akan mengubah <progress> menjadi tidak tentu.

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

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }
}

Memperbaiki masalah matematika desimal JavaScript

Karena saya memilih untuk tetap menggunakan {i> default progres<i} maksimum 1, demo fungsi pertambahan dan pengurangan menggunakan matematika desimal. JavaScript, dan lainnya bahasa, tidak selalu mahir dalam itu. Berikut adalah fungsi roundDecimals() yang akan memangkas kelebihan perhitungan dari matematika hasil:

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

Bulatkan nilai agar dapat disajikan dan dapat dibaca:

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 + "%"
}

Menetapkan nilai untuk pembaca layar dan status browser

Nilai ini digunakan di tiga lokasi di DOM:

  1. Atribut value milik elemen <progress>.
  2. Atribut aria-valuenow.
  3. Konten teks dalam <progress>.
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
}

Memberikan fokus pada progres

Dengan nilai yang diperbarui, pengguna yang normal akan melihat perubahan kemajuan, tetapi pengguna pembaca belum diberi pengumuman perubahan. Fokus pada Elemen <progress> dan browser akan mengumumkan update!

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

Screenshot aplikasi Voice Over Mac OS 
  membaca progres bilah
pemuatan kepada pengguna.

Kesimpulan

Sekarang setelah Anda tahu bagaimana saya melakukannya, bagaimana Anda akan 🙂

Tentu saja ada beberapa perubahan yang ingin saya lakukan jika diberi kesempatan lain. Menurut saya, ada ruang untuk membersihkan komponen saat ini, dan ada ruang untuk mencoba membuat komponen tanpa batasan gaya class semu elemen <progress>. Menarik untuk dijelajahi!

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web.

Buat demo, link tweet saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah ini.

Remix komunitas