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 dan
dapat 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 sangat berharga 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 berfungsi dengan
elemen <progress>
HTML yang ada untuk menghemat beberapa 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 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 kembali informasi tersebut kepada pengguna.
<progress></progress>
Jika tidak ada value
, progres elemen akan
tidak ditentukan.
Atribut max
secara default ditetapkan 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, yang 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
kepadanya sehingga secara efektif tidak ditampilkan di layar:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
Dengan CSS yang menyertainya 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 baik, Anda dapat dengan mudah mengaitkan indikator progres
dengan elemen terkait dan area halaman, tetapi bagi pengguna yang mengalami gangguan penglihatan, 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 value
yang ditetapkan.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
Gunakan
tabindex="-1"
agar elemen progres dapat difokuskan dari JavaScript. Hal ini penting bagi
teknologi pembaca layar, karena memberikan fokus progres saat progres berubah,
akan mengumumkan kepada pengguna sejauh mana progres yang diperbarui telah dicapai.
Gaya
Elemen progres sedikit rumit dalam hal gaya visual. Elemen HTML bawaan memiliki bagian tersembunyi khusus yang dapat sulit dipilih dan sering kali hanya menawarkan kumpulan properti terbatas untuk ditetapkan.
Tata Letak
Gaya tata letak dimaksudkan untuk memungkinkan beberapa fleksibilitas dalam ukuran dan posisi label elemen progres. 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 menyusut dan membesar
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 besar sehingga border-radius
selalu dibulatkan. Fungsi ini setara dengan
1000px
. Saya suka menggunakannya karena tujuan saya adalah menggunakan nilai yang cukup besar sehingga
saya dapat menetapkannya dan melupakannya (dan lebih singkat untuk ditulis daripada 1000px
). Nilai ini juga
mudah untuk membuatnya lebih besar jika diperlukan: cukup ubah 3 menjadi 4, lalu 1e4px
setara dengan 10000px
.
overflow: hidden
digunakan dan telah menjadi gaya yang diperdebatkan. Hal ini memudahkan beberapa
hal, seperti tidak perlu meneruskan nilai border-radius
ke
jalur, dan melacak elemen isi; tetapi hal ini 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 maksimum dengan nilai, dan jika cocok, progres akan 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 dibuat dengan beberapa pemilih khusus browser khusus.
Gaya browser terang dan gelap
Untuk memilih situs Anda ke elemen <progress>
adaptif gelap dan terang,
color-scheme
adalah satu-satunya yang diperlukan.
progress {
color-scheme: light dark;
}
Warna progres properti tunggal yang terisi
Untuk memberi tint pada elemen <progress>
, gunakan accent-color
.
progress {
accent-color: rebeccapurple;
}
Perhatikan warna latar belakang jalur yang 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 jalur
dan satu lagi untuk warna progres jalur. Di dalam
kueri media
prefers-color-scheme
, berikan nilai warna baru untuk progres lagu dan 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 dapat memilih gaya cincin fokus yang lebih cerdas. Dengan ini, klik mouse
dan fokus tidak akan menampilkan lingkaran fokus, tetapi klik keyboard akan melakukannya. Video YouTube ini membahasnya secara lebih mendalam dan
layak untuk ditinjau.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Gaya kustom di seluruh browser
Sesuaikan gaya dengan memilih bagian elemen <progress>
yang ditampilkan setiap browser. Menggunakan elemen progres adalah satu tag, tetapi terdiri dari beberapa elemen turunan yang ditampilkan melalui pemilih pseudo CSS. Chrome DevTools
akan menampilkan elemen ini kepada Anda jika Anda mengaktifkan setelan:
- Klik kanan pada halaman, lalu pilih Periksa Elemen untuk membuka 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 sebagian
CSS 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);
}
Gaya Firefox
Firefox hanya mengekspos pemilih pseudo ::-moz-progress-bar
pada elemen <progress>
. Hal ini juga berarti kita tidak dapat langsung mewarnai jalur.
/* 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 ini sama dengan mode gelap: Firefox memiliki jalur gelap, tetapi
bukan warna kustom yang telah kita tetapkan, dan berfungsi di browser berbasis Webkit.
Animasi
Saat menggunakan pemilih pseudo bawaan browser, sering kali dengan kumpulan properti CSS yang diizinkan terbatas.
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 pseudo 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 ke 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 menggabungkan pemilih khusus browser ini.
Keyframe
Tujuannya adalah animasi tanpa batas yang bolak-balik. 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 asalnya, berulang kali.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Menargetkan setiap browser
Tidak semua browser mengizinkan pembuatan elemen pseudo pada elemen <progress>
itu sendiri atau mengizinkan animasi status progres. Lebih banyak browser yang mendukung
animasi jalur daripada pseudo-elemen, jadi saya mengupgrade dari pseudo-elemen sebagai
dasar dan ke batang animasi.
Pseudo-elemen Chromium
Chromium mengizinkan elemen pseudo: ::after
yang digunakan 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 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 elemen pseudo:
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>
. Fungsi ini mengontrol
nilai yang dikirim ke elemen dan memastikan informasi yang memadai 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 = () => {
}
Menetapkan status pemuatan di zona <main>
Bergantung pada apakah progres sudah selesai atau belum, elemen <main>
terkait memerlukan pembaruan pada
atribut
aria-busy
:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Menghapus atribut jika jumlah pemuatan tidak diketahui
Jika nilai tidak diketahui atau tidak ditetapkan, 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 progres maksimum default 1, fungsi
peningkatan dan penurunan demo menggunakan matematika desimal. JavaScript, dan bahasa
lainnya, tidak selalu melakukannya dengan baik.
Berikut adalah fungsi roundDecimals()
yang akan memangkas kelebihan dari hasil
matematika:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Membulatkan nilai agar dapat ditampilkan 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:
- 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 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 Anda tahu cara saya melakukannya, bagaimana Anda melakukannya‽ 🙂
Tentu saja ada beberapa perubahan yang ingin saya lakukan jika diberi kesempatan lagi. Saya rasa ada ruang untuk membersihkan komponen saat ini, dan ruang untuk mencoba dan mem-build komponen tanpa batasan gaya pseudo-class elemen <progress>
. Sebaiknya Anda mencobanya.
Mari kita diversifikasi pendekatan dan pelajari semua cara untuk mem-build di web.
Buat demo, tweet link-nya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.