Indikator lingkaran berputar (atau indikator aktivitas) memberi tahu pengguna bahwa suatu proses sedang berlangsung saat durasi pastinya tidak diketahui. Tidak seperti diagram progres, indikator lingkaran berputar bersifat "tidak tentu" dan biasanya menggunakan animasi untuk menandakan aktivitas.
Panduan ini mengimplementasikan indikator lingkaran berputar dengan:
- Menggunakan elemen
<progress>native sebagai fondasi semantik. Dengan menghilangkan atributvalue, browser akan memperlakukannya sebagai status progres yang tidak tentu, sehingga memastikan pengumuman yang benar oleh teknologi pendukung. - Menata gaya komponen dengan
conic-gradient()untuk membuat "jejak" visual danbackground-clip: border-areauntuk melubangi bagian tengah menjadi cincin. - Menganimasikan indikator lingkaran berputar secara efisien menggunakan transformasi CSS dan mematuhi
prefers-reduced-motionuntuk memastikan pengalaman yang nyaman bagi semua pengguna.
Lihat Diagram progres untuk menangani tugas pasti dengan durasi yang diketahui.
Penerapan
1. Markup
Gunakan elemen <progress> native sebagai sumber kebenaran semantik dan komponen visual. Tanpa atribut value, atribut ini secara implisit tidak ditentukan.
<progress aria-label="Loading" class="loading-spinner"></progress>
2. Gaya
Menyembunyikan UI Native
Untuk menata gaya elemen <progress> sebagai indikator lingkaran berputar, sembunyikan terlebih dahulu gaya browser default untuk status progres yang tidak tentu.
/* Hide native bars */
progress.loading-spinner:indeterminate::-webkit-progress-bar {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::-webkit-progress-value {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::-moz-progress-bar {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::slider-fill {
display: none;
background: none;
}
Cincin dan Jejak Spinner
Spinner menggunakan conic-gradient untuk membuat jejak visual.
@property --arc-start {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@property --arc-end {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
/* Use a custom property to conditionally include the dash animation */
@property --progress-dash-animation {
syntax: "*";
inherits: false;
initial-value: , progress-dash 3s ease-in-out infinite;
}
progress.loading-spinner:indeterminate {
--_from: calc(90deg + var(--arc-start, 0deg));
--_to: calc(90deg + var(--arc-end, 158deg));
--size: 40px;
--thickness: 2px;
--spinner-color: #3b82f6;
--track-color: #e2e5e7;
--spinner-duration: 1.5s;
--_used-spinner-duration: var(--spinner-duration);
--spinner-timing: linear;
position: relative;
width: var(--size);
height: var(--size);
border-radius: 50%;
appearance: none;
/* Create the fading trail with dynamic angles */
background: conic-gradient(
from var(--_from),
var(--spinner-color) calc(var(--_to) - var(--_from)),
transparent 0
)
var(--track-color);
@supports (background-clip: border-area) {
background-clip: border-area;
border: var(--thickness) solid transparent;
background-origin: border-box;
}
/* ... fallback for background-clip: border-area ... */
/* The dash animation is only included if @property is supported */
animation:
progress-spin var(--_used-spinner-duration) linear infinite
var(--progress-dash-animation, );
}
@keyframes progress-spin {
to {
rotate: 1turn;
}
}
@keyframes progress-dash {
from {
--arc-start: 0deg;
--arc-end: 3deg;
}
50% {
--arc-start: 100deg;
--arc-end: 358deg;
}
to {
--arc-start: 360deg;
--arc-end: 363deg;
}
}
Mematuhi Preferensi Gerakan
Pengguna yang sensitif terhadap gerakan mungkin akan merasa bingung dengan elemen yang berputar cepat. Selalu hormati kueri media prefers-reduced-motion. Tetapkan properti --_used-spinner-duration internal untuk mengganti nilai --spinner-duration pengguna.
@media (prefers-reduced-motion: reduce) {
.loading-spinner {
/* Slow down the animation significantly rather than stopping it entirely,
so the user still knows that the process is active. */
--_used-spinner-duration: 6s;
}
}
Strategi penggantian
Jika @property didukung, animasi tanda hubung akan otomatis disertakan melalui initial-value properti --progress-dash-animation. Di browser tanpa dukungan @property, pendaftaran properti akan diabaikan, dan animasi akan kembali ke rotasi sederhana. JavaScript tidak diperlukan untuk penggantian ini.
Untuk browser yang belum mendukung background-clip: border-area, gunakan mask-image untuk melubangi bagian tengah.
/* Fallback: use mask-image to create the ring */
@supports not (background-clip: border-area) {
--clip-boundary: calc(100% - var(--thickness));
mask-image: radial-gradient(
farthest-side,
transparent var(--clip-boundary),
black var(--clip-boundary)
);
border: 0;
}