Ringkasan dasar tentang cara membuat panel pemuatan yang mudah diakses dan adaptif warna dengan elemen <progress>.
Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat panel pemuatan yang adaptif warna dan
mudah diakses dengan elemen <progress>. Coba
demo dan lihat
sumbernya.
Jika Anda lebih suka video, berikut versi YouTube dari postingan ini:
Ringkasan
Elemen
<progress>
memberikan masukan visual dan audio kepada pengguna tentang penyelesaian. Masukan
visual ini berguna untuk skenario seperti: progres melalui formulir,
menampilkan informasi download atau upload, atau bahkan menunjukkan bahwa
jumlah progres tidak diketahui, tetapi pekerjaan masih aktif.
Tantangan GUI ini menggunakan
elemen <progress> HTML yang ada untuk menghemat upaya dalam aksesibilitas. Warna dan tata letak mendorong batas penyesuaian untuk elemen bawaan, guna memodernisasi komponen dan membuatnya lebih sesuai dalam sistem desain.
Markup
Saya memilih untuk menggabungkan 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
tidak dapat ditentukan.
Atribut max ditetapkan secara default ke 1, sehingga progresnya antara 0 dan 1. Misalnya, menetapkan max
ke 100 akan menetapkan rentang ke 0-100. Saya memilih untuk tetap berada dalam batas 0 dan 1, dengan menerjemahkan nilai progres 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 memilih untuk menyertakan label untuk pembaca layar
saja.
Hal ini dilakukan dengan menggabungkan teks label dalam <span> dan menerapkan beberapa gaya
ke dalamnya sehingga secara efektif tidak terlihat di layar:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
Dengan CSS pendukung 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, akan mudah untuk mengaitkan indikator progres
dengan elemen dan area halaman terkait, tetapi bagi pengguna penyandang disabilitas netra, hal ini tidak
begitu jelas. Tingkatkan hal ini 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 elemen yang tidak memiliki setelan 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 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 mungkin 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 petunjuk visual tambahan yang berguna, tetapi tidak wajib.
Tata letak <progress>
Lebar elemen progres tidak diubah sehingga dapat menyusut dan bertambah
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 gayanya 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 yang besar sehingga border-radius selalu dibulatkan. Setara dengan
1000px. Saya suka menggunakan ini karena tujuan saya adalah menggunakan nilai yang cukup besar sehingga
saya dapat menetapkannya dan melupakannya (dan lebih singkat untuk ditulis daripada 1000px). Juga
mudah untuk membuatnya lebih besar jika diperlukan: cukup ubah 3 menjadi 4, maka 1e4px
setara dengan 10000px.
overflow: hidden digunakan dan telah menjadi gaya yang kontroversial. Hal ini memudahkan beberapa hal, seperti tidak perlu meneruskan nilai border-radius ke trek, dan melacak elemen pengisian; 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 pekerjaan berat di sini dengan membandingkan nilai maksimum dengan nilai, dan jika cocok, progresnya selesai. Setelah selesai, elemen pseudo 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 menghadirkan warnanya sendiri untuk elemen progres, dan adaptif terhadap terang dan gelap hanya dengan satu properti CSS. Hal ini dapat dikembangkan dengan beberapa pemilih khusus browser.
Gaya browser terang dan gelap
Untuk mengaktifkan elemen <progress> adaptif gelap dan terang di situs Anda,
color-scheme adalah satu-satunya yang diperlukan.
progress {
color-scheme: light dark;
}
Warna terisi progres properti tunggal
Untuk memberi tint pada elemen <progress>, gunakan accent-color.
progress {
accent-color: rebeccapurple;
}
Perhatikan bahwa warna latar belakang trek berubah dari terang menjadi 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 trek
dan satu lagi untuk warna progres trek. Di dalam
kueri media
prefers-color-scheme, berikan nilai warna baru untuk trek dan progres trek.
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 memberi elemen indeks tab negatif sehingga dapat difokuskan
secara terprogram. Gunakan
:focus-visible untuk
menyesuaikan fokus agar menggunakan gaya cincin fokus yang lebih cerdas. Dengan ini, klik mouse dan fokus tidak akan menampilkan cincin fokus, tetapi klik keyboard akan menampilkannya. Video YouTube membahas hal ini secara lebih mendalam dan layak untuk ditinjau.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}

Gaya kustom di berbagai browser
Sesuaikan gaya dengan memilih bagian elemen <progress> yang ditampilkan oleh setiap browser. Penggunaan elemen progres adalah tag tunggal, 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 judul Elemen, temukan dan aktifkan kotak centang Tampilkan DOM bayangan 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 digunakan. Untuk saat ini, tetapkan background-color menggunakan properti kustom
yang dibuat sebelumnya, yang beradaptasi dengan mode 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 memberi tint pada trek 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 berlaku dalam mode gelap: Firefox memiliki trek gelap, tetapi
bukan warna kustom yang telah kita tetapkan, dan berfungsi di browser berbasis Webkit.
Animasi
Saat menggunakan pemilih pseudo bawaan browser, sering kali ada kumpulan properti CSS yang diizinkan dalam jumlah terbatas.
Menganimasikan pengisian trek
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 sedikit lebih kreatif sehingga saya dapat memberikan animasi. Elemen semu untuk Chromium dibuat dan gradien diterapkan yang dianimasikan bolak-balik untuk ketiga browser.
Properti kustom
Properti kustom sangat bagus 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.
Keyframe
Tujuannya adalah animasi tanpa batas yang bergerak maju mundur. Keyframe awal dan akhir
akan ditetapkan di CSS. Hanya diperlukan satu keyframe, yaitu keyframe tengah
di 50%, untuk membuat animasi yang kembali ke tempat awalnya, berulang
kali.
@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 trek daripada elemen semu, jadi saya mengupgrade dari elemen semu sebagai
dasar dan menjadi batang animasi.
Elemen semu Chromium
Chromium mengizinkan elemen semu: ::after digunakan dengan posisi untuk menutupi
elemen. Properti kustom yang tidak tentu 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 status progres pseudo-elemen:
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 status progres pseudo-elemen:
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 terjadinya orkestrasi UI/UX. Mulai dengan membuat fungsi setProgress(). Tidak diperlukan parameter karena memiliki akses ke objek state, elemen progres, dan zona <main>.
const setProgress = () => {
}
Menetapkan status pemuatan di zona <main>
Bergantung pada apakah progresnya selesai atau tidak, elemen <main>
terkait memerlukan update pada
atribut
aria-busy:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Hapus atribut jika jumlah pemuatan tidak diketahui
Jika nilainya tidak diketahui atau tidak ditetapkan, null dalam penggunaan ini, hapus atribut value dan
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 nilai maksimum default progres, yaitu 1, fungsi penambahan dan pengurangan demo menggunakan matematika desimal. JavaScript, dan bahasa lainnya, tidak selalu bagus dalam hal itu.
Berikut adalah fungsi roundDecimals() yang akan memangkas kelebihan dari hasil matematika:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Bulatkan nilai agar dapat ditampilkan dan terbaca:
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 dalam DOM:
- Atribut
valuemilik 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 pada 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 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()
}

Kesimpulan
Setelah tahu cara saya melakukannya, bagaimana cara Anda‽ 🙂
Tentu saja ada beberapa perubahan yang ingin saya lakukan jika diberi kesempatan lagi. Menurut saya, ada ruang untuk membersihkan komponen saat ini, dan ruang untuk mencoba dan membangun komponen tanpa batasan gaya pseudo-class elemen <progress>. Hal ini layak untuk dipelajari.
Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web.
Buat demo, tweet link-nya kepada saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.