Ringkasan dasar tentang cara membuat status pemuatan yang adaptif dan mudah diakses dengan elemen <progress>
.
Dalam postingan ini, saya ingin membagikan pemikiran tentang cara membuat status pemuatan yang adaptif terhadap warna dan dapat diakses dengan elemen <progress>
. Coba
demo dan lihat
sumbernya.
Jika Anda lebih suka menonton video, berikut versi YouTube dari postingan ini:
Ringkasan
Elemen
<progress>
memberikan masukan visual dan audio kepada pengguna tentang penyelesaian. Masukan visual ini sangat berharga untuk skenario seperti: progres pengisian formulir, menampilkan informasi download atau upload, atau bahkan menunjukkan bahwa jumlah progres tidak diketahui, tetapi pekerjaan masih aktif.
GUI Challenge ini bekerja dengan elemen <progress>
HTML yang ada untuk menghemat upaya dalam aksesibilitas. Warna dan tata letak mendorong batas penyesuaian untuk elemen bawaan, untuk memodernisasi komponen dan membuatnya lebih sesuai dalam sistem desain.

Markup
Saya memilih untuk membungkus elemen <progress>
dalam
<label>
sehingga
saya dapat melewati atribut hubungan eksplisit dan memilih hubungan implisit.
Saya juga telah memberi label pada elemen induk yang terpengaruh oleh status pemuatan, sehingga teknologi pembaca layar dapat menyampaikan informasi tersebut kembali kepada pengguna.
<progress></progress>
Jika tidak ada value
, progres elemen adalah
indeterminate.
Atribut max
secara default adalah 1, sehingga progresnya antara 0 dan 1. Menetapkan max
ke 100, misalnya, akan menetapkan rentang ke 0-100. Saya memilih untuk tetap berada dalam batas 0 dan 1, menerjemahkan nilai progres menjadi 0,5 atau 50%.
Progres yang dibungkus label
Dalam hubungan implisit, elemen progres diapit oleh label seperti ini:
<label>Loading progress<progress></progress></label>
Dalam demo saya, saya memilih untuk menyertakan label hanya untuk pembaca layar.
Hal ini dilakukan dengan membungkus teks label dalam <span>
dan menerapkan beberapa gaya
agar teks tersebut tidak terlihat di layar:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
Dengan CSS pendamping berikut dari WebAIM:
.sr-only {
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
width: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
}
Area yang terpengaruh oleh progres pemuatan
Jika Anda memiliki penglihatan yang sehat, Anda dapat dengan mudah mengaitkan indikator progres dengan elemen dan area halaman terkait, tetapi bagi pengguna tunanetra, hal ini tidak begitu jelas. Tingkatkan kualitasnya dengan menetapkan atribut
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 ke
false
setelah selesai.
Penambahan atribut Aria
Meskipun peran implisit elemen <progress>
adalah
progressbar
, saya telah membuatnya eksplisit
untuk browser yang tidak memiliki peran implisit tersebut. Saya juga telah menambahkan atribut
indeterminate
untuk secara eksplisit menempatkan elemen ke dalam status tidak diketahui, yang
lebih jelas daripada mengamati bahwa elemen tidak memiliki value
yang ditetapkan.
<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 untuk teknologi pembaca layar, karena memberikan fokus progres saat progres berubah, akan mengumumkan kepada pengguna seberapa jauh progres yang diperbarui telah tercapai.
Gaya
Elemen progres agak rumit dalam hal gaya visual. Elemen HTML bawaan memiliki bagian tersembunyi khusus yang sulit dipilih dan sering kali hanya menawarkan serangkaian properti terbatas untuk ditetapkan.
Tata Letak
Gaya tata letak dimaksudkan untuk memungkinkan fleksibilitas dalam ukuran elemen progres dan posisi label. Status penyelesaian khusus ditambahkan yang dapat menjadi isyarat visual tambahan yang berguna, tetapi tidak diperlukan.
Tata Letak <progress>
Lebar elemen progres tidak diubah sehingga dapat mengecil dan membesar
sesuai dengan ruang yang diperlukan dalam desain. Gaya bawaan dihapus dengan
menetapkan appearance
dan border
ke none
. Hal ini dilakukan agar elemen dapat dinormalisasi di seluruh browser, karena setiap browser memiliki gaya sendiri untuk elemennya.
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 notasi angka ilmiah untuk menyatakan
angka besar sehingga border-radius
selalu dibulatkan. Setara dengan
1000px
. Saya suka menggunakannya karena tujuan saya adalah menggunakan nilai yang cukup besar sehingga saya dapat menetapkannya dan melupakannya (dan lebih pendek untuk ditulis daripada 1000px
). Nilai ini juga mudah dibuat lebih besar jika diperlukan: cukup ubah 3 menjadi 4, lalu 1e4px
setara dengan 10000px
.
overflow: hidden
digunakan dan telah menjadi gaya yang kontroversial. Hal ini mempermudah beberapa hal, seperti tidak perlu meneruskan nilai border-radius
ke bawah ke trek dan elemen pengisi trek; tetapi juga berarti tidak ada turunan progres yang dapat berada di luar elemen. Iterasi lain pada elemen progres kustom ini dapat dilakukan tanpa overflow: hidden
dan dapat membuka beberapa peluang untuk animasi atau status penyelesaian yang lebih baik.
Proses selesai
Pemilih CSS melakukan tugas berat di sini dengan membandingkan nilai maksimum dengan nilai, dan jika cocok, progres selesai. Setelah selesai, elemen semu akan dibuat dan ditambahkan ke 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);
}
Warna
Browser menampilkan warnanya sendiri untuk elemen progres, dan dapat beradaptasi dengan terang dan gelap hanya dengan satu properti CSS. Hal ini dapat dibangun dengan beberapa pemilih khusus browser.
Gaya browser terang dan gelap
Untuk mengikutsertakan situs Anda dalam elemen adaptif gelap dan terang <progress>
,
color-scheme
adalah semua yang diperlukan.
progress {
color-scheme: light dark;
}
Warna pengisian progres properti tunggal
Untuk mewarnai elemen <progress>
, gunakan accent-color
.
progress {
accent-color: rebeccapurple;
}
Perhatikan warna latar belakang jalur berubah dari terang ke gelap, bergantung pada
accent-color
. Browser memastikan kontras yang tepat: cukup rapi.
Warna terang dan gelap yang sepenuhnya kustom
Tetapkan dua properti kustom pada elemen <progress>
, satu untuk warna jalur
dan yang lainnya untuk warna progres jalur. Di dalam kueri media
prefers-color-scheme
, berikan nilai warna baru untuk jalur dan progres jalur.
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%);
}
}
Gaya fokus
Sebelumnya, kita memberikan indeks tab negatif pada elemen sehingga elemen tersebut dapat difokuskan secara terprogram. Gunakan
:focus-visible
untuk
menyesuaikan fokus agar memilih gaya ring fokus yang lebih cerdas. Dengan demikian, klik
mouse dan fokus tidak akan menampilkan lingkaran fokus, tetapi klik keyboard akan menampilkannya. Video YouTube membahas hal ini secara lebih mendalam dan layak ditinjau.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Gaya kustom di seluruh browser
Sesuaikan gaya dengan memilih bagian elemen <progress>
yang diekspos oleh setiap browser. Menggunakan elemen progres adalah satu tag, tetapi terdiri dari beberapa elemen turunan yang diekspos melalui pemilih pseudo CSS. Chrome DevTools
akan menampilkan elemen ini kepada Anda jika Anda mengaktifkan setelan:
- Klik kanan halaman Anda, lalu pilih Periksa Elemen untuk menampilkan DevTools.
- Klik roda gigi Setelan di pojok kanan atas jendela DevTools.
- Di bagian heading Elemen, temukan dan aktifkan kotak centang Tampilkan bayangan agen pengguna DOM.
Gaya Safari dan Chromium
Browser berbasis WebKit seperti Safari dan Chromium mengekspos
::-webkit-progress-bar
dan ::-webkit-progress-value
, yang memungkinkan penggunaan subset
CSS. Untuk saat ini, tetapkan background-color
menggunakan properti kustom
yang dibuat sebelumnya, yang beradaptasi dengan tema terang dan gelap.
/* Safari/Chromium */
progress[value]::-webkit-progress-bar {
background-color: var(--_track);
}
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
}
Gaya Firefox
Firefox hanya mengekspos pseudo-selektor ::-moz-progress-bar
pada
elemen <progress>
. Artinya, kita juga tidak dapat mewarnai jalur secara langsung.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Perhatikan bahwa Firefox memiliki warna jalur yang ditetapkan dari accent-color
, sedangkan iOS Safari memiliki jalur biru muda. Hal yang sama juga terjadi dalam mode gelap: Firefox memiliki jalur gelap, tetapi bukan warna kustom yang telah kita tetapkan, dan berfungsi di browser berbasis Webkit.
Animasi
Saat bekerja dengan pseudo-selector bawaan browser, sering kali hanya dengan sekumpulan kecil properti CSS yang diizinkan.
Menganimasikan pengisian jalur
Menambahkan transisi ke
inline-size
elemen
progres berfungsi untuk Chromium, tetapi tidak untuk Safari. Firefox juga 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 dapat memberikan animasi. Elemen semu untuk Chromium dibuat dan gradien diterapkan yang dianimasikan bolak-balik untuk ketiga browser.
Properti kustom
Properti kustom sangat berguna untuk banyak hal, tetapi salah satu favorit saya adalah
memberi nama pada nilai CSS yang terlihat ajaib. Berikut adalah linear-gradient
yang cukup
kompleks,
tetapi 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 kustom juga akan membantu kode tetap DRY karena sekali lagi, kita tidak dapat mengelompokkan pemilih khusus browser ini.
Frame utama
Tujuannya adalah animasi tanpa batas yang bergerak maju mundur. Keyframe awal dan akhir akan ditetapkan di CSS. Hanya satu keyframe yang diperlukan, yaitu keyframe tengah
di 50%
, untuk membuat animasi yang kembali ke tempat awalnya, berulang-ulang.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Menargetkan setiap browser
Tidak semua browser mengizinkan pembuatan elemen semu pada elemen <progress>
itu sendiri atau mengizinkan animasi status progres. Lebih banyak browser yang mendukung
animasi jalur daripada elemen semu, jadi saya mengupgrade dari elemen semu sebagai
dasar dan ke dalam animasi batang.
Elemen semu Chromium
Chromium mengizinkan penggunaan pseudo-elemen: ::after
dengan posisi untuk menutupi elemen. Properti kustom yang tidak ditentukan digunakan, dan animasi bolak-balik berfungsi 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 kustom dan animasi diterapkan ke progress bar 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 kustom dan animasi juga diterapkan ke progress bar 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>
. Atribut ini mengontrol
nilai yang dikirim ke elemen dan memastikan informasi yang cukup ada dalam
dokumen untuk pembaca layar.
const state = {
val: null
}
Demo ini menawarkan tombol untuk mengontrol progres; tombol tersebut memperbarui state.val
lalu 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 = () => {
}
Menyetel status pemuatan di zona <main>
Bergantung pada apakah progres selesai atau tidak, elemen <main>
terkait memerlukan pembaruan pada
atribut aria-busy
:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Hapus atribut jika jumlah pengisian daya tidak diketahui
Jika nilai tidak diketahui atau tidak disetel, null
dalam penggunaan ini, hapus atribut value
dan
aria-valuenow
. Tindakan ini akan mengubah <progress>
menjadi tidak ditentukan.
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 nilai maksimum default progres 1, fungsi penambahan dan pengurangan demo menggunakan matematika desimal. JavaScript, dan bahasa lainnya, tidak selalu bagus dalam hal itu.
Berikut fungsi roundDecimals()
yang akan memangkas kelebihan hasil
matematika:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Bulatkan nilai agar dapat ditampilkan dan mudah 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 digunakan di tiga lokasi di DOM:
- Atribut
value
elemen<progress>
. - Atribut
aria-valuenow
. - 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 progres
Dengan nilai yang diperbarui, pengguna yang dapat melihat akan melihat perubahan progres, tetapi pengguna pembaca layar belum diberi pengumuman perubahan. Fokuskan elemen
<progress>
dan browser akan mengumumkan pembaruan.
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()
}
Kesimpulan
Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda‽ 🙂
Tentu ada beberapa perubahan yang ingin saya lakukan jika diberi kesempatan lain. Saya rasa ada ruang untuk membersihkan komponen saat ini, dan ruang untuk mencoba membangun komponen tanpa batasan gaya pseudo-class elemen <progress>
. Anda bisa mencobanya.
Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web.
Buat demo, tweet linknya kepada saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.