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.
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.
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;
}
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);
}
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;
}
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:
- Fai clic con il tasto destro del mouse sulla pagina e seleziona Ispeziona elemento per visualizzare DevTools.
- Fai clic sull'icona a forma di ingranaggio delle impostazioni nell'angolo in alto a destra della finestra DevTools.
- Sotto l'intestazione Elements, trova e attiva l'ombra dello user agent Mostra user agent. DOM.
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);
}
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);
}
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:
- Attributo
value
dell'elemento<progress>
. - Attributo
aria-valuenow
. - 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()
}
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.