Creazione di un componente della barra di caricamento

Una panoramica di base su come creare una barra di caricamento accessibile e adattiva ai colori con l'elemento <progress>.

In questo post voglio condividere i miei pensieri su come creare un colore adattivo barra di caricamento accessibile con l'elemento <progress>. Prova la demo e guarda la Fonte.

Luce e Buio, indeterminato, in aumento e completamento demo su Chrome.

Se preferisci i video, ecco una versione di questo post su YouTube:

Panoramica

La <progress> fornisce agli utenti un feedback visivo e udibile sul completamento. Questo il feedback visivo è utile in scenari come l'avanzamento in un modulo, mostrare informazioni sul download o sul caricamento o persino mostrare che l'importo dell'avanzamento è sconosciuto, ma il lavoro è ancora attivo.

Questa Sfida GUI ha collaborato con l'elemento HTML <progress> esistente per semplificare l'accessibilità. La i colori e i layout superano i limiti della personalizzazione per l'elemento integrato, modernizzare il componente e adattarlo meglio ai sistemi di progettazione.

Le schede chiare e scure di ogni browser forniscono una 
    panoramica dell&#39;icona adattiva dall&#39;alto verso il basso: 
    Safari, Firefox, Chrome.
. Demo mostrata su Firefox, Safari, iOS, Safari, Chrome e Chrome per Android in schemi chiari e bui.

Aumento

Ho scelto di aggregare l'elemento <progress> in un <label> così Potrei saltare gli attributi di relazione esplicita a favore di una relazione implicita una relazione. Ho anche etichettato un elemento principale interessato dallo stato di caricamento, quindi nella schermata le tecnologie dei lettori possono ritrasmettere le informazioni a un utente.

<progress></progress>

Se non è presente value, l'avanzamento dell'elemento è indeterminato. Per impostazione predefinita, l'attributo max è 1, quindi l'avanzamento è compreso tra 0 e 1. Impostazione di max su 100, ad esempio, l'intervallo viene impostato su 0-100. Ho scelto di rimanere entro lo 0 e 1, traducendo i valori dei progressi in 0,5 o 50%.

Avanzamento del wrapping delle etichette

In una relazione implicita, un elemento di avanzamento è aggregato da un'etichetta come la seguente:

<label>Loading progress<progress></progress></label>

Nella mia demo ho scelto di includere l'etichetta per gli screen reader . A questo scopo, occorre racchiudere il testo dell'etichetta in un <span> e applicare alcuni stili in modo che sia effettivamente fuori schermo:

<label>
  <span class="sr-only">Loading progress</span>
  <progress></progress>
</label>

Con il seguente CSS associato di WebAIM:

.sr-only {
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

Screenshot di devtools che mostra l&#39;elemento &quot;screen ready only&quot;.

Area interessata dall'avanzamento del caricamento

Se hai una vista sana, può essere facile associare un indicatore dei progressi con elementi correlati e aree di pagina, ma per gli utenti con disabilità visiva, così chiaro. Per migliorare questo risultato, assegna aria-busy all'elemento più in alto che cambierà al termine del caricamento. Inoltre, indica una relazione tra l'avanzamento e la zona di caricamento. con aria-describedby

<main id="loading-zone" aria-busy="true">
  …
  <progress aria-describedby="loading-zone"></progress>
</main>

Da JavaScript, imposta aria-busy in true all'inizio dell'attività e per false al termine dell'operazione.

Aggiunte attributi Aria

Mentre il ruolo implicito di un elemento <progress> è progressbar, ho fatto in modo che sia esplicita per i browser che non hanno questo ruolo implicito. Ho anche aggiunto l'attributo indeterminate per mettere esplicitamente l'elemento in uno stato sconosciuto, ovvero rispetto all'osservazione che non ha value impostato.

<label>
  Loading 
  <progress 
    indeterminate 
    role="progressbar" 
    aria-describedby="loading-zone"
    tabindex="-1"
  >unknown</progress>
</label>

Utilizza le funzionalità di tabindex="-1" per rendere attivabile l'elemento "Progress" da JavaScript. Questo è importante per la tecnologia screen reader, poiché mettendo lo stato attivo man mano che l'avanzamento cambia, comunicherà all'utente quanto sono stati raggiunti i progressi aggiornati.

Stili

L'elemento di avanzamento è un po' complicato per quanto riguarda lo stile. HTML integrato presentano particolari parti nascoste che possono essere difficili da selezionare e spesso offrono solo un insieme limitato di proprietà da impostare.

Layout

Gli stili di layout sono pensati per consentire una certa flessibilità di avanzamento le dimensioni dell'elemento e la posizione dell'etichetta. Viene aggiunto uno stato di completamento speciale che un segnale visivo aggiuntivo utile ma non obbligatorio.

Layout <progress>

La larghezza dell'elemento di avanzamento non viene toccata, quindi può ridursi e aumentare con lo spazio necessario nel design. Gli stili integrati sono stati rimossi da impostazione appearance e border su none. In questo modo, l'elemento può essere normalizzato in tutti i browser, poiché ogni browser ha i propri stili .

progress {
  --_track-size: min(10px, 1ex);
  --_radius: 1e3px;

  /*  reset  */
  appearance: none;
  border: none;

  position: relative;
  height: var(--_track-size);
  border-radius: var(--_radius);
  overflow: hidden;
}

Il valore di 1e3px per _radius utilizza un numero scientifico notazione per esprimere una numero elevato in modo che border-radius sia sempre arrotondato. Equivale a 1000px. Mi piace usare questo modello perché il mio obiettivo è quello di usare un valore abbastanza grande da Posso impostarlo e poi dimenticare (ed è più breve da scrivere di 1000px). È inoltre possibile facile da ingrandire ancora di più se necessario: basta cambiare il 3 in un 4, allora 1e4px è equivalente a 10000px.

overflow: hidden è utilizzato ed è stato uno stile conflittuale. Ha generato alcune le cose semplici, come non dover passare i valori border-radius monitorare e tracciare elementi di riempimento; ma significava anche che non erano figli dei progressi potrebbero trovarsi al di fuori dell'elemento. Un'altra iterazione di questo avanzamento personalizzato può essere eseguito senza overflow: hidden e potrebbe aprire opportunità di animazioni o stati di completamento migliori.

Operazione completata

I selettori CSS fanno il resto, confrontando il valore massimo con il valore. Se corrispondono, l'avanzamento è completo. Al termine, viene generato uno pseudo-elemento che viene aggiunto alla fine dell'elemento avanzamento, offrendo un ulteriore segnale visivo per il completamento.

progress:not([max])[value="1"]::before,
progress[max="100"][value="100"]::before {
  content: "";
  
  position: absolute;
  inset-block: 0;
  inset-inline: auto 0;
  display: flex;
  align-items: center;
  padding-inline-end: max(calc(var(--_track-size) / 4), 3px);

  color: white;
  font-size: calc(var(--_track-size) / 1.25);
}

Screenshot della barra di caricamento al 100% e che mostra un segno di spunta alla fine.

Colore

Il browser utilizza i propri colori per l'elemento Progressi ed è adattivo chiaro e scuro con una sola proprietà CSS. Questo può essere basato su alcuni selettori speciali specifici del browser.

Stili del browser chiaro e scuro

Per attivare per il tuo sito un elemento <progress> adattivo chiaro e scuro: color-scheme è tutto ciò che serve.

progress {
  color-scheme: light dark;
}

Colore colore avanzamento singola proprietà

Per colorare un elemento <progress>, utilizza accent-color.

progress {
  accent-color: rebeccapurple;
}

Il colore di sfondo della traccia cambia da chiaro a scuro a seconda del accent-color. Il browser sta garantendo il giusto contrasto: abbastanza chiaro.

Colori chiari e scuri completamente personalizzati

Imposta due proprietà personalizzate sull'elemento <progress>, una per il colore della traccia e l'altro per il colore di avanzamento della traccia. All'interno prefers-color-scheme una query multimediale, fornire nuovi valori di colore per la traccia e tenere traccia dell'avanzamento.

progress {
  --_track: hsl(228 100% 90%);
  --_progress: hsl(228 100% 50%);
}

@media (prefers-color-scheme: dark) {
  progress {
    --_track: hsl(228 20% 30%);
    --_progress: hsl(228 100% 75%);
  }
}

Imposta lo stato attivo sugli stili

In precedenza abbiamo assegnato all'elemento un indice di tabulazione negativo in modo che potesse essere utilizzato in modo programmatico specifici. Utilizza le funzionalità di Da :focus-visible a personalizza la messa a fuoco per attivare lo stile dell'anello di messa a fuoco più intelligente. In questo modo, clic e lo stato attivo non mostreranno l'anello di messa a fuoco, al contrario dei clic sulla tastiera. La I video di YouTube approfondiscono l'argomento e vale la pena rivedere.

progress:focus-visible {
  outline-color: var(--_progress);
  outline-offset: 5px;
}

Screenshot della barra di caricamento con intorno un anello di messa a fuoco. Tutti i colori corrispondono.

Stili personalizzati nei vari browser

Personalizza gli stili selezionando le parti di un elemento <progress> che ognuna del browser. L'utilizzo dell'elemento Progress è un singolo tag, ma costituito da un alcuni elementi secondari esposti tramite pseudoselettori CSS. Chrome DevTools mostrerà questi elementi se attivi l'impostazione:

  1. Fai clic con il tasto destro del mouse sulla pagina e seleziona Ispeziona elemento per visualizzare DevTools.
  2. Fai clic sull'icona a forma di ingranaggio delle impostazioni nell'angolo in alto a destra della finestra DevTools.
  3. Sotto l'intestazione Elements, trova e attiva l'ombra dello user agent Mostra user agent. DOM.

Screenshot che mostra dove abilitare l&#39;esposizione del DOM shadow dello user agent in DevTools.

Stili di Safari e Chromium

L'esposizione dei browser basati su WebKit come Safari e Chromium ::-webkit-progress-bar e ::-webkit-progress-value, che consentono un sottoinsieme di CSS da utilizzare. Per ora, imposta background-color utilizzando le proprietà personalizzate create in precedenza, che si adattano alla luce e all'oscurità.

/*  Safari/Chromium  */
progress[value]::-webkit-progress-bar {
  background-color: var(--_track);
}

progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
}

Screenshot che mostra gli elementi interni dell&#39;elemento avanzamento.

Stili di Firefox

Firefox espone solo lo pseudoselettore ::-moz-progress-bar sulla Elemento <progress>. Ciò significa anche che non possiamo applicare direttamente la tinta alla traccia.

/*  Firefox  */
progress[value]::-moz-progress-bar {
  background-color: var(--_progress);
}

Screenshot di Firefox e dove trovare le parti dell&#39;elemento progress.

Screenshot dell&#39;angolo di debug in cui Safari, iOS Safari, 
  La barra di caricamento visualizzata in Firefox, Chrome e Chrome su Android funziona tutti.

Tieni presente che per Firefox è impostato un colore delle tracce accent-color per Safari su iOS ha una traccia azzurra. Lo stesso avviene con la modalità Buio: Firefox ha una traccia scura, ma non il colore personalizzato che abbiamo impostato e funziona nei browser basati su Webkit.

Animazione

Quando si lavora con pseudo selettori integrati nel browser, spesso la funzionalità è limitata insieme di proprietà CSS consentite.

Animazione del riempimento della traccia

L'aggiunta di una transizione al inline-size di l'elemento Progressi funziona per Chromium, ma non per Safari. Anche Firefox non utilizzare una proprietà di transizione in ::-moz-progress-bar.

/*  Chromium Only 😢  */
progress[value]::-webkit-progress-value {
  background-color: var(--_progress);
  transition: inline-size .25s ease-out;
}

Animazione dello stato :indeterminate

Qui posso ottenere un po' più di creatività in modo da poter fornire un'animazione. Uno pseudo-elemento di Chromium, viene creato un gradiente che viene animato per tutti e tre i browser.

Proprietà personalizzate

Le proprietà personalizzate sono ideali per molti aspetti, ma una delle mie preferite è semplicemente assegnando un nome a un valore CSS altrimenti magico. Il seguente è un aspetto complesso linear-gradient, ma con un bel nome. Lo scopo e i casi d'uso devono essere compresi chiaramente.

progress {
  --_indeterminate-track: linear-gradient(to right,
    var(--_track) 45%,
    var(--_progress) 0%,
    var(--_progress) 55%,
    var(--_track) 0%
  );
  --_indeterminate-track-size: 225% 100%;
  --_indeterminate-track-animation: progress-loading 2s infinite ease;
}

Le proprietà personalizzate consentono inoltre di mantenere il codice DRY, poiché, ancora una volta, non possiamo raggruppa questi selettori specifici del browser.

I fotogrammi chiave

L'obiettivo è un'animazione infinita che va avanti e indietro. L'inizio e la fine I fotogrammi chiave verranno impostati in CSS. È necessario un solo fotogramma chiave, quello centrale in 50%, per creare un'animazione che riprenda il punto di inizio, il passaggio successivo e di nuovo!

@keyframes progress-loading {
  50% {
    background-position: left; 
  }
}

Scegliere come target ogni browser

Non tutti i browser consentono la creazione di pseudo-elementi su <progress> o consente di animare la barra di avanzamento. Supporto di altri browser animare la traccia rispetto a uno pseudo-elemento, quindi eseguo l'upgrade da pseudo-elementi come una base e in barre animate.

Pseudo-elemento Chromium

Chromium consente lo pseudo-elemento: ::after utilizzato con una posizione per coprire dell'elemento. Vengono utilizzate le proprietà personalizzate indeterminate, mentre i dischi l'animazione funziona molto bene.

progress:indeterminate::after {
  content: "";
  inset: 0;
  position: absolute;
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
Barra di avanzamento di Safari

Per Safari, le proprietà personalizzate e un'animazione vengono applicate barra di avanzamento dello pseudo-elemento:

progress:indeterminate::-webkit-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}
Barra di avanzamento di Firefox

In Firefox, le proprietà personalizzate e un'animazione vengono applicate anche barra di avanzamento dello pseudo-elemento:

progress:indeterminate::-moz-progress-bar {
  background: var(--_indeterminate-track);
  background-size: var(--_indeterminate-track-size);
  background-position: right; 
  animation: var(--_indeterminate-track-animation);
}

JavaScript

JavaScript svolge un ruolo importante con l'elemento <progress>. Controlla il valore inviato all'elemento e garantisce la presenza di informazioni sufficienti nel documento per screen reader.

const state = {
  val: null
}

La demo offre pulsanti per controllare l'avanzamento: aggiornano state.val e quindi richiamare una funzione per aggiornare DOM:

document.querySelector('#complete').addEventListener('click', e => {
  state.val = 1
  setProgress()
})

setProgress()

Questa funzione è il punto in cui avviene l'orchestrazione UI/UX. Per iniziare, crea un Funzione setProgress(). Non sono necessari parametri perché ha accesso ai parametri Oggetto state, elemento di avanzamento e zona <main>.

const setProgress = () => {
  
}

Impostazione dello stato di caricamento nella zona <main>

A seconda che l'avanzamento sia completo o meno, il <main> correlato deve essere aggiornato aria-busy :

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)
}

Cancella gli attributi se la quantità di caricamento è sconosciuta

Se il valore è sconosciuto o non viene impostato, null in questo utilizzo, rimuovi value e aria-valuenow attributi. In questo modo, l'elemento <progress> diventerà indeterminato.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }
}

Risolvere i problemi di matematica decimale in JavaScript

Dato che ho scelto di mantenere il valore predefinito massimo di 1 per i progressi, la demo le funzioni di incremento e decremento usano la matematica decimale. JavaScript e altre lingue diverse, non sempre sono bravissimi a che. Ecco una funzione roundDecimals() che taglia la parte in eccesso dai calcoli matematici risultato:

const roundDecimals = (val, places) =>
  +(Math.round(val + "e+" + places)  + "e-" + places)

Arrotonda il valore in modo che possa essere presentato e leggibile:

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"
}

Imposta il valore per gli screen reader e lo stato del browser

Il valore viene utilizzato in tre posizioni nel DOM:

  1. Attributo value dell'elemento <progress>.
  2. Attributo aria-valuenow.
  3. I contenuti di testo interno di <progress>.
const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent
}

Concentrarsi sul progresso

Con i valori aggiornati, gli utenti vedenti vedranno l'avanzamento del processo, ma agli utenti lettori non è stato ancora dato l'annuncio del cambiamento. Concentra l'attenzione sul L'elemento <progress> e il browser annunceranno l'aggiornamento.

const setProgress = () => {
  zone.setAttribute('aria-busy', state.val < 1)

  if (state.val === null) {
    progress.removeAttribute('aria-valuenow')
    progress.removeAttribute('value')
    progress.focus()
    return
  }

  const val = roundDecimals(state.val, 2)
  const valPercent = val * 100 + "%"

  progress.value = val
  progress.setAttribute('aria-valuenow', valPercent)
  progress.innerText = valPercent

  progress.focus()
}

Screenshot dell&#39;app Voce fuori campo di Mac OS 
  la lettura dell&#39;avanzamento della barra di caricamento all&#39;utente.

Conclusione

Ora che sai come ci ho fatto, come faresti‽ 🙂

Di sicuro vorrei apportare alcune modifiche se ci venisse offerta un'altra possibilità. Penso che ci sia spazio per ripulire il componente corrente e per provare a crearne uno senza le limitazioni di stile pseudo-classe dell'elemento <progress>. Vale la pena esplorare!

Diversificaamo i nostri approcci e impariamo tutti i modi per creare sul web.

Crea una demo, twittami con i link e la aggiungerò alla sezione dei remix della community qui sotto.

Remix della community