L'élément <progress> est le moyen sémantique de représenter la progression d'une tâche, comme le téléchargement ou l'importation d'informations, ou l'achèvement d'une partie d'un processus en plusieurs étapes. L'utilisation de l'élément natif facilite l'accessibilité, car les lecteurs d'écran ont un role="progressbar" implicite et peuvent annoncer des valeurs.
Ce guide implémente une barre de progression horizontale entièrement stylisée avec des personnalisations de style.
Barre ou cercle de progression ?
- Si l'espace est limité ou si un format carré est préférable, envisagez d'utiliser une barre de progression circulaire (cercle de progression) : Cercle de progression
- Privilégiez les icônes de chargement pour la plupart des scénarios de progression indéterminée (Icône de chargement). Utilisez des barres de progression indéterminées lorsque l'état indéterminé est temporaire et sera bientôt remplacé par une barre de progression déterminée (par exemple, en attendant les métadonnées d'un import avant de pouvoir afficher un pourcentage de progression).
Annoter
- Utilisez un élément
<progress>natif : il offre une accessibilité intégrée et un comportement cohérent avec la plate-forme. - Utilisez
maxsi vous avez besoin d'une échelle différente de celle par défaut (0-1). - Pour une valeur de progression spécifique, définissez un attribut
value. Omettezvaluepour un état indéterminé (par exemple, avant que des commentaires plus concrets ne commencent). - Utilisez
<label for="...">,aria-labelledbyouaria-labelpour associer l'élément de progression à un libellé.
De quel type de personnalisation avez-vous besoin ?
Pour un style limité, ces propriétés sont suffisantes :
accent-colorpour modifier la couleur de la barre de progressionwidth/inline-sizepour modifier la largeur de la barre de progressionheight/block-sizes'applique de manière incohérente : testez d'abordwriting-mode: vertical-lrpour les barres verticales.- Effets qui s'appliquent après l'affichage :
opacity,filter,clip-path, masques, transformations,box-shadow, etc.
Si un style limité suffit, ignorez la section suivante.
AVERTISSEMENT : Certaines propriétés (par exemple, border-radius, background, border) désactiveront silencieusement le style de la barre de progression par défaut. Si vous avez besoin d'un style plus complexe que celui proposé par les propriétés ci-dessus, vous devez implémenter un style entièrement personnalisé, comme décrit dans la section suivante. N'appliquez PAS de style plus étendu "au cas où cela fonctionnerait".
Style entièrement personnalisé
1. Configuration de base
- OBLIGATOIRE : Utilisez
appearance: nonepour activer le style personnalisé. - Réinitialiser les bordures UA avec
border: 0 solid - Pour limiter la barre active au rayon de bordure de l'élément, utilisez
overflow: clipOU appliquez le mêmeborder-radiusà la barre. N'utilisez PASborder-radius: inheritsur la barre, car cela dépend de la structure spécifique des éléments du Shadow DOM.
progress {
/* Reset default browser styles */
appearance: none;
border: 0 solid;
/* Clip bar to element’s border-radius */
overflow: clip;
}
2. Attribution d'un style
- Appliquez un style à la piste sur l'élément
progresslui-même. - Utilisez le pseudo-élément approprié ciblant le navigateur pour cibler la barre de progression elle-même :
::-webkit-progress-valuepour WebKit/Blink et::-moz-progress-barpour Firefox. - OBLIGATOIRE : Ne regroupez PAS les pseudo-éléments
-webkit-et-moz-dans la même règle. La règle sera considérée comme non valide et sera ignorée.:is()et:where()ne sont pas utiles ici, car ils ne sont pas compatibles avec les pseudo-éléments. - Chaque pseudo-élément ciblé par le navigateur doit apparaître exactement une fois. Les personnalisations spécifiques à l'état (telles que la taille et l'animation du segment indéterminé) doivent être acheminées via des propriétés personnalisées définies sur l'élément
progresslui-même. - Facultatif : ajoutez une transition
inline-sizesur le remplissage pour que les changements de valeur déterminés soient animés au lieu de sauter. Firefox dimensionne le remplissage dans la mise en page et l'ignore, ce qui est sans danger.
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. Style de l'état indéterminé
Ajoutez une animation personnalisée pour l'état indéterminé : transformez le pseudo-élément de remplissage en un segment court et faites-le glisser le long de la piste.
- Appliquez un style au remplissage lui-même. Ne le recréez PAS en tant qu'arrière-plan sur
progress. - Le remplissage indéterminé n'existe que par son animation : les images clés définissent sa longueur avec un
inline-sizeréel (qui remplace leinline-sizeintégré de l'UA de Chromium) et le déplacent avectranslate. N'utilisez PASscalepour la longueur, car cela déforme tout motif peint sur le remplissage. - Les pourcentages
translatesont résolus par rapport à la largeur du remplissage. Exprimez donc les positions en unités de piste : faites deprogressun conteneur de taille intégrée et utilisezcqidans les images clés. Incluezmargin-right: autodansfrom, topour que Chromium place le remplissage à gauche physiquement, même en mode de lecture de droite à gauche. - Utilisez des transformations, et non des marges, pour déplacer le remplissage.
- Les transformations conservent l'animation dans le compositeur (sauf si
inline-sizeest également animé). - Les marges sont rompues en RTL pour deux raisons distinctes :
- Firefox positionne le remplissage indéterminé en utilisant uniquement sa marge physique gauche. Par conséquent,
margin-inline-startn'a aucun effet en mode RTL et le remplissage ne se déplace jamais. - Dans Chromium et WebKit, qui disposent le remplissage comme un bloc normal, une fois qu'une marge pousse le remplissage au-delà de la fin de la piste, les marges s'additionnent pour dépasser la piste. Le CSS standard résout ce problème en ignorant
margin-leftdans les langues se lisant de droite à gauche. Le segment passe donc au bord au lieu de glisser. translateest appliqué après la mise en page, donc aucun de ces problèmes ne s'applique.
- Les transformations conservent l'animation dans le compositeur (sauf si
- Pour mettre en miroir le mouvement de droite à gauche (comme le fait la barre native de Chrome), inversez l'animation sous
:dir(rtl):
progress:dir(rtl) {
--progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
- Définissez les propriétés
--progress-bar-indeterminate-*publiques surprogress(leur nom les limite déjà à l'état) afin que les consommateurs puissent les remplacer sans connaître:indeterminate. - Pour faire varier la longueur du segment (par exemple, le bord d'attaque avance et le bord de fuite rattrape son retard), définissez
--progress-bar-indeterminate-animationsur deux animations simultanées : une dont les images clés animenttranslatedanscqi(position) et une dont les images clés animentinline-size(longueur), de sorte que chacune ait ses propres images clés et son propre lissage de vitesse. N'essayez PAS de mettre en forme les deux bords dans une seule liste d'images clés. La propriétéanimation-timing-functionpar image clé s'applique à toutes les propriétés de cette image clé. --progress-bar-animations'applique dans les deux états et peut être utilisé pour des effets tels qu'un scintillement déterminé.revert-rulen'est utilisé que pour remplacer lorsqu'il est défini, afin de maximiser la composabilité.- Ne désactivez jamais l'animation indéterminée (
animation: nonelaisse un remplissage de largeur nulle dans Chromium). Pour réduire les mouvements, ralentissez-la plutôt.
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 */
}
Autres considérations sur l'accessibilité
En plus des consignes mentionnées ci-dessus :
- Annoncez la fin de l'opération sans voler le curseur : ne définissez pas
tabindexet n'appelez pas.focus()sur<progress>, car il n'est pas interactif et le déplacement du curseur vers celui-ci perturbe les utilisateurs du clavier. Pour annoncer la fin d'une tâche en arrière-plan, mettez à jour un élément d'état poli (role="status"ouaria-live="polite"). - N'ajoutez pas de texte de remplacement dans
<progress>: le texte dans<progress>est ignoré par les navigateurs récents et n'est pas utilisé par les technologies d'assistance. - Évitez d'utiliser
aria-busy+aria-describedbysur un<div>générique sans libellé. La compatibilité dearia-busyavec les lecteurs d'écran est irrégulière, etaria-describedbysur un élément<div>sans nom et sans rôle est ignoré par les technologies d'assistance. Si le contenu d'une région n'est pas prêt lors du chargement, masquez-le avechiddenouaria-hidden="true". - À FAIRE : Assurez-vous que le contraste entre la barre de progression et la piste est suffisant lorsque vous ajoutez des styles personnalisés.
- Envisagez d'autres solutions pour représenter un état indéterminé : les animations de barre de progression indéterminée affichées en parallèle d'autres contenus peuvent être considérées comme une distraction par certains utilisateurs.
Stratégies de remplacement
Solutions de remplacement et compatibilité des navigateurs pour accent-color
La propriété accent-color est une amélioration progressive.
Les navigateurs qui ne sont pas compatibles avec cette propriété l'ignoreront et utiliseront les couleurs par défaut de leur UI.
Si accent-color n'est pas compatible et que vous devez appliquer un style à la barre de progression, utilisez la méthode de style personnalisé pour appliquer le style de votre choix à la barre de progression.
revert-rule : solutions de remplacement et compatibilité avec les navigateurs
Si revert-rule n'est pas compatible, animation: var(--progress-bar-animation, revert-rule) utilise animation-name: revert-rule avec une durée de 0s lorsque --progress-bar-animation n'est pas défini, ce qui est une opération sans effet et sans danger lorsqu'aucun @keyframes revert-rule n'existe.