Membuat Status Progres Bergaya

Elemen <progress> adalah cara semantik untuk merepresentasikan progres penyelesaian tugas, seperti mendownload atau mengupload informasi atau menyelesaikan bagian dari proses multi-langkah. Penggunaan elemen native membantu aksesibilitas, karena pembaca layar memiliki role="progressbar" implisit dan dapat mengumumkan nilai.

Panduan ini menerapkan status progres horizontal yang sepenuhnya bergaya dengan penyesuaian gaya.

Status progres atau diagram progres?

  • Jika ruang terbatas atau rasio aspek persegi lebih disukai, pertimbangkan untuk menggunakan status progres melingkar (diagram progres): Diagram progres
  • Pilih indikator lingkaran berputar untuk sebagian besar skenario progres yang tidak tentu (Indikator lingkaran berputar). Gunakan status progres tidak tentu jika status tidak tentu bersifat sementara dan akan segera digantikan oleh status progres tentu (misalnya, menunggu metadata terkait upload sebelum % progres dapat ditampilkan)

Markup

  1. Gunakan elemen <progress> native: Elemen ini memberikan aksesibilitas bawaan dan perilaku yang konsisten dengan platform.
  2. Gunakan max jika diperlukan skala yang berbeda dari skala default 0-1.
  3. Untuk nilai progres tertentu, tetapkan atribut value. Hilangkan value untuk status yang tidak pasti (misalnya, sebelum masukan yang lebih konkret dimulai).
  4. Gunakan <label for="...">, aria-labelledby, atau aria-label untuk mengaitkan elemen progres dengan label.

Apa jenis penyesuaian yang diperlukan?

Untuk gaya visual terbatas, properti berikut sudah cukup:

  • accent-color untuk mengubah warna status progres
  • width / inline-size untuk mengubah lebar status progres
  • height/block-size berlaku secara tidak konsisten — uji terlebih dahulu
  • writing-mode: vertical-lr untuk batang vertikal.
  • Efek yang diterapkan setelah penggambaran: opacity, filter, clip-path, mask, transformasi, box-shadow, dll.

Jika gaya visual terbatas sudah cukup, lewati bagian berikutnya.

PERINGATAN: Properti tertentu (misalnya border-radius, background, border) akan menonaktifkan gaya visual status progres default secara otomatis. Jika memerlukan gaya selain properti di atas, Anda harus menerapkan gaya kustom sepenuhnya seperti yang dijelaskan di bagian berikutnya. JANGAN menerapkan gaya yang lebih ekstensif "untuk berjaga-jaga jika berhasil".

Gaya kustom sepenuhnya

1. Penyiapan dasar

  • WAJIB: Gunakan appearance: none untuk mengaktifkan gaya kustom.
  • Reset batas UA dengan border: 0 solid
  • Untuk memotong panel aktif ke radius batas elemen, gunakan overflow: clip ATAU terapkan border-radius yang sama ke panel. JANGAN gunakan border-radius: inherit pada panel, yang bergantung pada struktur elemen tertentu di shadow DOM.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. Gaya visual

  • Atur gaya trek pada elemen progress itu sendiri.
  • Gunakan elemen semu yang ditargetkan browser yang sesuai untuk menargetkan status progres itu sendiri: ::-webkit-progress-value untuk WebKit/Blink dan ::-moz-progress-bar untuk Firefox.
  • WAJIB: JANGAN mengelompokkan elemen semu -webkit- dan -moz- dalam aturan yang sama. Aturan tersebut akan dianggap tidak valid dan akan diabaikan. :is() dan :where() tidak membantu di sini, karena tidak mendukung elemen semu.
  • Setiap elemen semu yang ditargetkan browser harus muncul tepat satu kali. Penyesuaian khusus status (seperti ukuran dan animasi segmen yang tidak tentu) harus dirutekan melalui properti kustom yang ditetapkan pada elemen progress itu sendiri.
  • Opsional: Transisikan inline-size pada pengisian sehingga perubahan nilai determinat dianimasikan, bukan melompat. Firefox mengukur pengisian dalam tata letak dan mengabaikannya, yang tidak berbahaya.
progress {
  --progress-bar-radius: 1e5px;
  border-radius: var(--progress-bar-radius);
  /* Apply color for custom progress track (background) */
  background: var(--progress-bar-track, var(--color-neutral-90));
  /* Subtle depth on the empty part of the track */
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
}

/* Let <progress> styling show through */
progress::-webkit-progress-bar {
  background: transparent;
}

/* Webkit, Chrome */
progress::-webkit-progress-value {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

/* Firefox */
progress::-moz-progress-bar {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

3. Gaya status tidak tentu

Menambahkan animasi kustom untuk status tidak tentu: Ubah elemen semu pengisian menjadi segmen pendek dan geser di sepanjang trek.

  • Atur gaya isian itu sendiri; JANGAN buat ulang sebagai latar belakang di progress.
  • Isian tidak tentu hanya ada melalui animasinya: keyframe menetapkan panjangnya dengan inline-size yang sebenarnya (ini mengalahkan inline-size inline UA Chromium) dan memindahkannya dengan translate. JANGAN gunakan scale untuk panjang; hal ini akan mendistorsi pola apa pun yang digambar pada isian.
  • Persentase translate dihitung berdasarkan lebar isian itu sendiri, jadi nyatakan posisi dalam unit trek: jadikan progress sebagai penampung ukuran inline dan gunakan cqi dalam keyframe. Sertakan margin-right: auto di from, to agar Chromium juga meletakkan pengisian di sisi kiri fisik dalam RTL.
  • Gunakan transformasi, bukan margin, untuk memindahkan pengisian.
    • Transformasi mempertahankan animasi di compositor (kecuali jika inline-size juga dianimasikan)
    • Margin rusak di RTL, karena dua alasan terpisah:
    • Firefox memosisikan isian tidak tentu hanya menggunakan margin kiri fisiknya, sehingga margin-inline-start tidak berpengaruh dalam RTL dan isian tidak pernah bergerak.
    • Di Chromium dan WebKit, yang meletakkan isian sebagai blok normal, setelah margin mendorong isian melewati akhir jalur, margin akan bertambah menjadi lebih besar dari jalur. CSS standar mengatasi hal tersebut dengan mengabaikan margin-left dalam RTL, sehingga segmen akan melompat ke tepi, bukan bergeser dari tepi.
    • translate diterapkan setelah tata letak, sehingga kedua masalah tersebut tidak berlaku.
  • Untuk mencerminkan gerakan dalam RTL (seperti yang dilakukan panel native Chrome), balikkan animasi di bagian :dir(rtl):
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • Tentukan properti --progress-bar-indeterminate-* publik di progress (namanya sudah mencakup status) sehingga konsumen dapat menggantinya tanpa mengetahui :indeterminate.
  • Untuk memvariasikan panjang segmen (misalnya, tepi depan bergerak lebih cepat dan tepi belakang mengejar), tetapkan --progress-bar-indeterminate-animation ke dua animasi serentak: satu yang keyframe-nya menganimasikan translate di cqi (posisi) dan satu yang keyframe-nya menganimasikan inline-size (panjang), sehingga masing-masing mendapatkan keyframe dan easing-nya sendiri. JANGAN mencoba membentuk kedua tepi dalam satu daftar keyframe; animation-timing-function per keyframe berlaku untuk setiap properti dalam keyframe tersebut.
  • --progress-bar-animation berlaku dalam kedua status dan dapat digunakan untuk efek seperti shimmer yang pasti. revert-rule digunakan untuk mengganti hanya jika ditetapkan, guna memaksimalkan composability.
  • Jangan pernah menonaktifkan animasi yang tidak tentu (animation: none menyisakan pengisian lebar nol di Chromium). Untuk mengurangi gerakan, perlambat saja.
progress {
  /* Lets the fill position itself in track units (cqi) */
  container-type: inline-size;
  /* Segment length relative to the track */
  --progress-bar-indeterminate-size: 30%;
  --progress-bar-indeterminate-animation-duration: 1.5s;

  /* Respect prefers-reduced-motion */
  @media (prefers-reduced-motion: reduce) {
    --progress-bar-indeterminate-animation-duration: 10s;
  }
}

progress:indeterminate {
  --progress-bar-animation: var(
      --progress-bar-indeterminate-animation,
      progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear
    );
}

/* Add to the existing pseudo-element rules */
progress::-webkit-progress-value {
  animation: var(--progress-bar-animation, revert-rule);
}

progress::-moz-progress-bar {
  animation: var(--progress-bar-animation, revert-rule);
}

/* Physical: left to right regardless of direction (reverse under :dir(rtl) to mirror). */
@keyframes progress-bar-indeterminate {
  from, to {
    inline-size: var(--progress-bar-indeterminate-size);
    /* Pin to the physical left; Chromium would otherwise lay the fill out at the inline start */
    margin-right: auto;
  }
  from { translate: -100%; } /* Own width: fully outside the left edge */
  to { translate: 100cqi; }  /* Track width: fully outside the right edge */
}

Pertimbangan Aksesibilitas Tambahan

Selain panduan yang disebutkan di atas:

  • Umumkan penyelesaian tanpa mengalihkan fokus: Jangan tetapkan tabindex atau panggil .focus() di <progress>, karena tidak interaktif dan memindahkan fokus ke sana akan mengganggu pengguna keyboard. Untuk mengumumkan saat tugas latar belakang selesai, perbarui elemen status sopan (role="status" atau aria-live="polite").
  • Jangan tambahkan teks pengganti di dalam <progress>: Teks di dalam <progress> diabaikan oleh browser modern dan tidak digunakan oleh teknologi pendukung.
  • Hindari aria-busy + aria-describedby pada <div> generik tanpa label. aria-busy memiliki dukungan pembaca layar yang tidak konsisten, dan aria-describedby pada <div> tanpa nama tanpa peran akan diabaikan oleh teknologi pendukung. Jika konten suatu region belum siap saat dimuat, sembunyikan dengan hidden atau aria-hidden="true".
  • LAKUKAN pastikan kontras yang tepat antara status progres dan trek saat menambahkan gaya kustom.
  • Pertimbangkan alternatif untuk merepresentasikan status tidak tentu: Animasi status progres tidak tentu yang ditampilkan secara paralel dengan konten lain dapat dianggap mengganggu bagi sebagian pengguna.

Strategi penggantian

Penggantian & dukungan browser untuk accent-color

Properti accent-color adalah progressive enhancement. Browser yang tidak mendukung properti ini akan mengabaikannya dan menggunakan warna UI default.

Jika accent-color tidak didukung dan penataan gaya status progres diperlukan, gunakan metode penataan gaya kustom untuk menerapkan gaya yang diinginkan ke status progres.

Penggantian & dukungan browser untuk revert-rule

Jika revert-rule tidak didukung, animation: var(--progress-bar-animation, revert-rule) akan kembali ke animation-name: revert-rule dengan durasi 0s saat --progress-bar-animation tidak ditetapkan, yang merupakan no-op yang tidak berbahaya jika tidak ada @keyframes revert-rule.