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 membuat status pemuatan yang adaptif warna dan
dapat diakses dengan elemen <progress>
. Coba
demo dan lihat
sumbernya.
Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:
Ringkasan
Elemen
<progress>
memberikan masukan visual dan terdengar kepada pengguna tentang penyelesaian tugas. Masukan
visual ini berguna untuk skenario seperti: progres dalam formulir,
menampilkan informasi download atau upload, atau bahkan menunjukkan bahwa
jumlah progres tidak diketahui, tetapi tugas masih aktif.
Tantangan GUI ini berfungsi dengan elemen <progress>
HTML yang ada untuk menghemat upaya aksesibilitas. Warna
dan tata letak mendorong batas penyesuaian untuk elemen bawaan, untuk
memodernisasi komponen dan membuatnya lebih cocok dalam sistem desain.
Markup
Saya memilih untuk menggabungkan elemen <progress>
dalam
<label>
sehingga
saya dapat melewati atribut hubungan eksplisit dan mengutamakan hubungan
implisit.
Saya juga telah memberi label pada elemen induk yang terpengaruh oleh status pemuatan, sehingga teknologi
pembaca layar dapat menyampaikan informasi tersebut kembali ke 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. Misalnya, menetapkan max
ke 100 akan menetapkan rentang ke 0-100. Saya memilih untuk tetap dalam batas 0 dan 1,
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, saya memilih untuk menyertakan label untuk khusus
pembaca layar.
Hal ini dilakukan dengan menggabungkan teks label dalam <span>
dan menerapkan beberapa gaya
pada teks tersebut agar efektif berada di luar 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;
}
Area yang terpengaruh oleh progres pemuatan
Jika Anda memiliki penglihatan yang sehat, mudah untuk mengaitkan indikator progres
dengan elemen dan area halaman terkait, tetapi bagi pengguna penyandang 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.
Selanjutnya, 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 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 untuk
teknologi pembaca layar, karena memberikan fokus progres saat progres berubah,
akan memberi tahu pengguna seberapa jauh progres yang diperbarui telah tercapai.
Gaya
Elemen progres agak rumit dalam hal penataan gaya. Elemen HTML bawaan memiliki bagian khusus tersembunyi yang mungkin sulit dipilih dan sering kali hanya menawarkan serangkaian properti terbatas.
Tata Letak
Gaya tata letak dimaksudkan untuk memungkinkan fleksibilitas dalam ukuran dan posisi label elemen progres. 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 menyusut dan membesar
sesuai ruang yang dibutuhkan dalam desain. Gaya bawaan dihilangkan dengan menetapkan appearance
dan border
ke none
. Hal ini dilakukan agar elemen dapat dinormalisasi di seluruh browser, karena setiap browser memiliki gaya sendiri untuk elemen mereka.
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 menggunakan ini karena tujuan saya adalah menggunakan nilai yang cukup besar sehingga
saya dapat menyetelnya dan melupakannya (dan lebih pendek untuk menulisnya daripada 1000px
). Selain itu,
mudah untuk membuatnya lebih besar lagi jika perlu: cukup ubah 3 menjadi 4, lalu 1e4px
setara dengan 10000px
.
overflow: hidden
digunakan dan telah menjadi gaya yang bertentangan. Perubahan ini mempermudah beberapa
hal, seperti tidak perlu meneruskan nilai border-radius
ke
jalur dan melacak elemen isian; 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 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);
}
Warna
Browser menghadirkan warnanya sendiri untuk elemen progres, dan adaptif terhadap terang dan gelap hanya dengan satu properti CSS. Ini dapat dibangun dengan beberapa pemilih khusus browser.
Gaya browser terang dan gelap
Untuk mengikutsertakan situs Anda ke elemen <progress>
adaptif gelap dan terang,
color-scheme
adalah satu-satunya yang diperlukan.
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 menjadi gelap, bergantung 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 progres trek. Di dalam
kueri media prefers-color-scheme
, berikan nilai warna baru untuk jalur dan melacak progres.
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 difokuskan
secara terprogram. Gunakan
:focus-visible
untuk
menyesuaikan fokus agar dapat memilih gaya cincin fokus yang lebih cerdas. Dengan cara ini, klik dan fokus
mouse tidak akan menampilkan cincin fokus, tetapi klik keyboard akan melakukannya. 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 seluruh browser
Sesuaikan gaya dengan memilih bagian elemen <progress>
yang diekspos oleh setiap browser. Penggunaan elemen progres terdiri dari 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 di halaman Anda dan pilih Periksa Elemen untuk membuka DevTools.
- Klik roda gigi Settings di sudut kanan atas jendela DevTools.
- Di bagian judul Elements, cari dan aktifkan kotak centang Show user agent shadow DOM.
Gaya Safari dan Chromium
Browser berbasis WebKit seperti Safari dan Chromium menampilkan
::-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 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 kami tidak dapat memberi warna pada trek secara langsung.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Perhatikan bahwa Firefox memiliki warna trek yang ditetapkan dari accent-color
, sedangkan iOS Safari memiliki trek biru terang. Demikian juga di mode gelap: Firefox memiliki jalur gelap tetapi
bukan warna khusus yang telah kita tetapkan, dan berfungsi di browser berbasis Webkit.
Animasi
Saat menggunakan pemilih pseudo bawaan browser, sering kali dengan sekumpulan properti CSS yang diizinkan terbatas.
Menganimasikan trek yang terisi
Menambahkan transisi ke
inline-size
elemen progres
akan 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 saya dapat memberikan animasi. Elemen semu untuk Chromium dibuat dan gradien diterapkan dengan animasi bergerak bolak-balik untuk ketiga browser.
Properti khusus
Properti kustom cocok untuk banyak hal, tetapi salah satu favorit saya adalah memberi nama untuk nilai CSS yang terlihat ajaib. Berikut adalah linear-gradient
yang cukup rumit,
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 KERING karena sekali lagi, kita tidak dapat mengelompokkan pemilih khusus browser ini bersama-sama.
Keyframe
Tujuannya adalah animasi tanpa batas yang bolak-balik. Keyframe awal dan akhir akan ditetapkan di CSS. Hanya satu keyframe yang diperlukan, keyframe tengah
di 50%
, untuk membuat animasi yang akan kembali ke tempat awal, 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 memungkinkan animasi status progres. Lebih banyak browser yang mendukung
animasi jalur dibandingkan elemen pseudo, jadi saya mengupgrade dari elemen-elemen sebagai
dasar dan menjadi batang animasi.
Elemen semu Chromium
Chromium mengizinkan elemen semu: ::after
yang 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 khusus dan animasi diterapkan ke status progres elemen pseudo:
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 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>
. Elemen 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. Demo ini akan mengupdate 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 selesai atau belum, 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 nilai tidak diketahui atau tidak disetel, 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 default progres maksimum 1, fungsi
penambahan dan pengurangan demo menggunakan matematika desimal. JavaScript, dan bahasa
lainnya, tidak selalu baik dalam hal
ini.
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 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:
- Atribut
value
milik 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 normal 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
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.