Een laadspinner (of activiteitsindicator) laat gebruikers weten dat er een proces wordt uitgevoerd als de exacte duur onbekend is. In tegenstelling tot een voortgangsring is een spinner 'onbepaald' en gebruikt deze meestal animatie om activiteit aan te geven.
In deze handleiding wordt een spinner geïmplementeerd door:
- Het systeemeigen
<progress>-element gebruiken als semantische basis. Als je het kenmerkvalueweglaat, behandelt de browser dit als een onbepaalde voortgangsbalk, zodat ondersteunende technologieën dit correct aankondigen. - De component stylen met
conic-gradient()om een visueel 'spoor' te maken enbackground-clip: border-areaom het midden uit te hollen tot een ring. - De spinner wordt efficiënt geanimeerd met CSS-transformaties en respecteert
prefers-reduced-motionom een prettige gebruikerservaring voor alle gebruikers te garanderen.
Ga naar Voortgangsring voor informatie over hoe je taken met een bekende duur afhandelt.
Implementatie
1. Opmaak
Gebruik het native <progress>-element als de semantische bron van waarheid en de visuele component. Zonder een value-kenmerk is het impliciet onbepaald.
<progress aria-label="Loading" class="loading-spinner"></progress>
2. Stijlen
Systeemeigen UI verbergen
Als je het element <progress> wilt opmaken als een spinner, verberg je eerst de standaardbrowserstijl voor onbepaalde voortgangsbalken.
/* 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;
}
Spinnerring en -spoor
De spinner gebruikt een conic-gradient om een visueel spoor te maken.
@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;
}
}
Bewegingsvoorkeuren respecteren
Gebruikers die gevoelig zijn voor beweging, kunnen snel draaiende elementen desoriënterend vinden. Respecteer altijd de mediaquery prefers-reduced-motion. Stel de interne property --_used-spinner-duration in om de waarde --spinner-duration van de gebruiker te overschrijven.
@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;
}
}
Reservestrategieën
Als @property wordt ondersteund, wordt de streepjesanimatie automatisch opgenomen via de initial-value van de eigenschap --progress-dash-animation. In browsers zonder ondersteuning voor @property wordt de propertyregistratie genegeerd en valt de animatie terug naar een eenvoudige rotatie. Voor deze reserveoptie is geen JavaScript vereist.
Voor browsers die background-clip: border-area nog niet ondersteunen, gebruik je een mask-image om het midden uit te hollen.
/* 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;
}