Crea una barra di avanzamento con stile

L'elemento <progress> è il modo semantico per rappresentare l'avanzamento del completamento di un'attività, ad esempio il download o il caricamento di informazioni o il completamento di una parte di un processo in più passaggi. L'utilizzo dell'elemento nativo contribuisce all'accessibilità, poiché gli screen reader hanno un role="progressbar" implicito e possono annunciare i valori.

Questa guida implementa una barra di avanzamento orizzontale completamente personalizzata con personalizzazioni dello stile.

Barra di avanzamento o anello di progresso?

  • Se lo spazio è limitato o se preferisci un formato quadrato, valuta la possibilità di utilizzare una barra di avanzamento circolare (anello di progresso): Anello di progresso
  • Preferisci gli indicatori di caricamento per la maggior parte degli scenari di avanzamento indeterminato (Indicatore di caricamento). Utilizza le barre di avanzamento indeterminate quando lo stato indeterminato è temporaneo e verrà presto sostituito da una barra di avanzamento determinata (ad es.in attesa dei metadati relativi a un caricamento prima che sia possibile mostrare una percentuale di avanzamento).

Segni e linee

  1. Utilizza un elemento <progress> nativo: offre accessibilità integrata e un comportamento coerente con la piattaforma.
  2. Utilizza max se è necessaria una scala diversa da quella predefinita 0-1.
  3. Per un valore di avanzamento specifico, imposta un attributo value. Ometti value per uno stato indeterminato (ad es. prima che inizi un feedback più concreto).
  4. Utilizza <label for="...">, aria-labelledby o aria-label per associare l'elemento di avanzamento a un'etichetta.

Che tipo di personalizzazione è necessaria?

Per uno stile limitato, sono sufficienti queste proprietà:

  • accent-color per cambiare il colore della barra di avanzamento
  • width/inline-size per modificare la larghezza della barra di avanzamento
  • height/block-size si applica in modo incoerente: esegui prima un test
  • writing-mode: vertical-lr per le barre verticali.
  • Effetti che si applicano dopo la pittura: opacity, filter, clip-path, maschere, trasformazioni, box-shadow e così via.

Se lo stile limitato è sufficiente, salta la sezione successiva.

ATTENZIONE: alcune proprietà (ad es. border-radius, background, border) disattiveranno silenziosamente lo stile predefinito della barra di avanzamento. Se hai bisogno di uno stile che vada oltre le proprietà sopra indicate, devi implementare uno stile completamente personalizzato, come descritto nella sezione successiva. NON applicare uno stile più esteso "nel caso in cui funzioni".

Stile completamente personalizzato

1. Configurazione di base

  • OBBLIGATORIO: utilizza appearance: none per attivare lo stile personalizzato.
  • Reimposta i confini UA con border: 0 solid
  • Per ritagliare la barra attiva in base al raggio del bordo dell'elemento, utilizza overflow: clip OPPURE applica lo stesso border-radius alla barra. NON utilizzare border-radius: inherit sulla barra, che dipende dalla struttura specifica degli elementi nello shadow DOM.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. Stili

  • Definisci lo stile della traccia direttamente sull'elemento progress.
  • Utilizza lo pseudo-elemento appropriato per il browser per scegliere come target la barra di avanzamento stessa: ::-webkit-progress-value per WebKit/Blink e ::-moz-progress-bar per Firefox.
  • OBBLIGATORIO: NON raggruppare gli pseudo-elementi -webkit- e -moz- nella stessa regola. La regola verrà considerata non valida e verrà ignorata. :is() e :where() non sono utili in questo caso, in quanto non supportano gli pseudo-elementi.
  • Ogni pseudo-elemento con targeting per browser deve essere visualizzato esattamente una volta. Le personalizzazioni specifiche dello stato (come le dimensioni e l'animazione del segmento indeterminato) devono essere indirizzate tramite proprietà personalizzate impostate sull'elemento progress stesso.
  • (Facoltativo) Esegui la transizione di inline-size sul riempimento in modo che le modifiche ai valori determinati vengano animate anziché saltare. Firefox dimensiona il riempimento nel layout e lo ignora, il che è innocuo.
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. Stile dello stato indeterminato

Aggiungi un'animazione personalizzata per lo stato indeterminato: trasforma lo pseudo-elemento di riempimento in un breve segmento e fallo scorrere lungo la barra.

  • Applica lo stile al riempimento stesso; NON ricrearlo come sfondo su progress.
  • Il riempimento indeterminato esiste solo tramite la sua animazione: i keyframe impostano la sua lunghezza con un inline-size reale (questo supera l'inline-size inline dell'UA di Chromium) e lo spostano con translate. NON utilizzare scale per la lunghezza, in quanto distorce qualsiasi pattern dipinto sul riempimento.
  • Le percentuali di translate vengono risolte in base alla larghezza del riempimento, quindi esprimi le posizioni in unità di traccia: imposta progress come contenitore di dimensioni in linea e utilizza cqi nei keyframe. Includi margin-right: auto in from, to in modo che Chromium disponga il riempimento a sinistra anche in RTL.
  • Utilizza le trasformazioni, non i margini, per spostare il riempimento.
    • Le trasformazioni mantengono l'animazione nel compositore (a meno che non venga animato anche inline-size)
    • I margini si interrompono in RTL per due motivi distinti:
    • Firefox posiziona il riempimento indeterminato utilizzando solo il margine sinistro fisico, quindi margin-inline-start non ha alcun effetto in RTL e il riempimento non si sposta mai.
    • In Chromium e WebKit, che dispongono il riempimento come un blocco normale, una volta che un margine spinge il riempimento oltre la fine della traccia, i margini si sommano a più della traccia. Il CSS standard risolve questo problema ignorando margin-left in RTL, in modo che il segmento passi al bordo anziché scorrere fuori.
    • translate viene applicato dopo il layout, quindi nessuno dei due problemi si applica.
  • Per rispecchiare il movimento in RTL (come fa la barra nativa di Chrome), inverti l'animazione in :dir(rtl):
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • Definisci le proprietà --progress-bar-indeterminate-* pubbliche su progress (il loro nome le limita già allo stato) in modo che i consumer possano eseguirne l'override senza conoscere :indeterminate.
  • Per variare la lunghezza del segmento (ad esempio, il bordo anteriore avanza e il bordo posteriore lo raggiunge), imposta --progress-bar-indeterminate-animation su due animazioni simultanee: una i cui fotogrammi chiave animano translate in cqi (posizione) e una i cui fotogrammi chiave animano inline-size (lunghezza), in modo che ognuna abbia i propri fotogrammi chiave e l'easing. NON provare a modellare entrambi i bordi in un unico elenco di fotogrammi chiave; l'elemento animation-timing-function per fotogramma chiave si applica a ogni proprietà di quel fotogramma chiave.
  • --progress-bar-animation si applica in entrambi gli stati e può essere utilizzato per effetti come un luccichio determinato. revert-rule viene utilizzato per eseguire l'override solo quando è impostato, per massimizzare la componibilità.
  • Non disattivare mai l'animazione indeterminata (animation: none lascia un riempimento di larghezza zero in Chromium). Per un movimento ridotto, rallenta il movimento.
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 */
}

Considerazioni aggiuntive sull'accessibilità

Oltre alle linee guida menzionate sopra:

  • Annuncia il completamento senza rubare lo stato attivo: non impostare tabindex o chiamare .focus() su <progress>, in quanto non è interattivo e lo spostamento dello stato attivo su di esso interrompe gli utenti della tastiera. Per annunciare il completamento di un'attività in background, aggiorna un elemento di stato polite (role="status" o aria-live="polite").
  • Non aggiungere testo di riserva all'interno di <progress>: il testo all'interno di <progress> viene ignorato dai browser moderni e non viene utilizzato dalle tecnologie per la disabilità.
  • Evita aria-busy + aria-describedby su un <div> generico senza etichetta. aria-busy ha un supporto per screen reader incoerente e aria-describedby su un <div> senza nome e senza ruolo viene ignorato dalle tecnologie per la disabilità. Se i contenuti di una regione non sono pronti durante il caricamento, nascondili con hidden o aria-hidden="true".
  • SÌ: assicurati che ci sia un contrasto adeguato tra la barra di avanzamento e la traccia quando aggiungi stili personalizzati.
  • Valuta alternative per rappresentare lo stato indeterminato: le animazioni della barra di avanzamento indeterminata visualizzate in parallelo ad altri contenuti possono essere considerate fonte di distrazione per alcuni utenti.

Strategie di riserva

Alternative e supporto dei browser per accent-color

La proprietà accent-color è un potenziamento progressivo. I browser che non supportano questa proprietà la ignoreranno e utilizzeranno i colori predefiniti della UI.

Se accent-color non è supportato e devi applicare uno stile alla barra di avanzamento, utilizza il metodo di stile personalizzato per applicare lo stile che preferisci alla barra di avanzamento.

Opzioni di riserva e supporto dei browser per revert-rule

Se revert-rule non è supportato, animation: var(--progress-bar-animation, revert-rule) utilizza animation-name: revert-rule con una durata 0s quando --progress-bar-animation non è impostato, il che è un'operazione innocua quando non esiste @keyframes revert-rule.