Una descripción general fundamental de cómo compilar una barra de carga adaptable y accesible con el color con el elemento <progress>
En esta publicación, quiero compartir ideas sobre cómo crear un color adaptable y
Es una barra de carga accesible con el elemento <progress>
. Prueba la
demo y vea el
fuente.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
Descripción general
El
<progress>
proporciona comentarios visuales y audibles a los usuarios sobre la finalización. Esta
los comentarios visuales son valiosos para situaciones como el progreso a través de un formulario,
mostrar información sobre descargas o cargas, o incluso mostrar que los
se desconoce la cantidad del progreso, pero el trabajo aún está activo.
Este desafío de la GUI trabajó con
el elemento HTML <progress>
existente para ahorrar cierto esfuerzo en accesibilidad. El
los colores y los diseños desafían los límites de la personalización del elemento integrado, para
modernizar el componente y hacer que se adapte mejor a los sistemas de diseño.
Marca
Elegí unir el elemento <progress>
en una
<label>
entonces
Podría omitir los atributos de relación explícita y reemplazarlos por un modelo
relación.
También etiqueté un elemento superior
afectado por el estado de carga,
las tecnologías de lectura pueden retransmitir esa información a un usuario.
<progress></progress>
Si no hay value
, el progreso del elemento es
indeterminate.
El valor predeterminado del atributo max
es 1, por lo que el progreso está entre 0 y 1. Configurando max
en 100, por ejemplo, establecería el rango en 0-100. Elegí permanecer dentro de 0
y 1, lo que traduce los valores de progreso a 0.5 o 50%.
Progreso de unión de etiquetas
En una relación implícita, un elemento de progreso se une con una etiqueta como la siguiente:
<label>Loading progress<progress></progress></label>
En mi demostración, elijo incluir la etiqueta para lectores de pantalla.
únicamente.
Para ello, se une el texto de la etiqueta a una <span>
y se aplican algunos estilos.
para que esté efectivamente fuera de la pantalla:
<label>
<span class="sr-only">Loading progress</span>
<progress></progress>
</label>
Con el siguiente CSS adjunto de 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;
}
Área afectada por el progreso de carga
Si tienes una visión sana, puede ser fácil asociar un indicador de progreso
con elementos y áreas de página relacionados, pero para los usuarios con discapacidad visual,
tan claro. Mejora esto asignando el
aria-busy
al elemento superior que cambiará cuando se complete la carga.
Además, indica una relación entre el progreso y la zona de carga
con
aria-describedby
<main id="loading-zone" aria-busy="true">
…
<progress aria-describedby="loading-zone"></progress>
</main>
En JavaScript, activa o desactiva aria-busy
por true
al comienzo de la tarea.
false
una vez finalizado.
Adiciones de atributos ARIA
Mientras que el rol implícito de un elemento <progress>
es
progressbar
, configuré explícitamente la opción
para los navegadores que no tienen esa función implícita. También agregué el atributo
indeterminate
para colocar explícitamente el elemento en un estado desconocido, que se
más clara que observar que el elemento no tiene un value
configurado.
<label>
Loading
<progress
indeterminate
role="progressbar"
aria-describedby="loading-zone"
tabindex="-1"
>unknown</progress>
</label>
Usa
tabindex="-1"
para que el elemento de progreso pueda enfocarse desde JavaScript. Esto es importante para
tecnología de lector de pantalla, ya que enfocarse en el progreso a medida que cambia
anunciará al usuario hasta qué punto alcanzó el progreso actualizado.
Estilos
Cuando se trata de diseño, el elemento de progreso es un poco complicado. HTML integrado los elementos tienen partes ocultas especiales que pueden ser difíciles de seleccionar y, a menudo, solo ofrecen un conjunto limitado de propiedades para establecer.
Diseño
Los estilos de diseño están diseñados para permitir cierta flexibilidad en el progreso. el tamaño del elemento y la posición de la etiqueta. Se agrega un estado de finalización especial será un indicador visual adicional útil, pero no obligatorio.
Diseño de <progress>
El ancho del elemento de progreso no se modifica para que se pueda reducir y aumentar
con el espacio necesario en el diseño. Los estilos integrados se eliminan
estableciendo appearance
y border
en none
. Esto se hace para que el elemento se pueda
normalizado en todos los navegadores, ya que cada uno tiene su propio estilo para su
.
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;
}
El valor de 1e3px
para _radius
usa un número científico
notación para expresar un
un número grande, por lo que border-radius
siempre se redondea. Equivale a
1000px
Me gusta usarlo porque mi objetivo es usar un valor lo suficientemente grande como para
Puedo configurarlo y olvidarlo (y su escritura es más corta que 1000px
). También se usa
puede hacerla aún más grande si es necesario: simplemente cambia el 3 a un 4, y luego 1e4px
se
equivalente a 10000px
.
overflow: hidden
es un estilo conflictivo. Hicieron algunos
procesos sencillos, como no tener que pasar valores de border-radius
al
y seguimiento de elementos de relleno; pero tampoco significaba que el progreso
podrían estar fuera del elemento. Otra iteración en este progreso personalizado
se podría hacer sin overflow: hidden
y podría abrir algunas
oportunidades de animaciones o mejores estados de finalización.
Progreso completado
Los selectores CSS hacen el trabajo difícil aquí, ya que comparan el máximo con el valor y, si coinciden, el progreso está completo. Cuando se completa, se genera un seudoelemento y se agrega al final del elemento de progreso, lo que proporciona un buen indicador visual adicional para completarlo.
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);
}
Color
El navegador aporta sus propios colores al elemento de progreso y se adapta al claro y oscuro con solo una propiedad de CSS. Esto se puede construir con algunos selectores especiales específicos del navegador.
Estilos de navegador claro y oscuro
Para habilitar tu sitio en un elemento <progress>
adaptable oscuro y claro, haz lo siguiente:
color-scheme
es todo lo que se requiere.
progress {
color-scheme: light dark;
}
Color de relleno del progreso de una propiedad única
Para ajustar el tono de un elemento <progress>
, usa accent-color
.
progress {
accent-color: rebeccapurple;
}
Observa que el color de fondo del seguimiento cambia de claro a oscuro según la
accent-color
El navegador está garantizando un contraste adecuado: bastante prolijo.
Colores claros y oscuros completamente personalizados
Establece dos propiedades personalizadas en el elemento <progress>
, una para el color del recorrido.
y la otra para el color de progreso del seguimiento. Dentro del
prefers-color-scheme
consulta de medios, proporciona nuevos valores de color para la pista y sigue su progreso.
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%);
}
}
Estilos de enfoque
Anteriormente, le dimos al elemento un índice de pestaña negativo para que pudiera ser programáticamente
enfocados. Usa
:focus-visible
a
personaliza el enfoque para adoptar el estilo de anillo de enfoque más inteligente. De esta forma, un mouse
la opción para hacer clic y enfocar no mostrará el anillo de enfoque, pero los clics del teclado sí. El
En un video de YouTube, se analiza este tema con más detalle y
vale la pena opinar.
progress:focus-visible {
outline-color: var(--_progress);
outline-offset: 5px;
}
Estilos personalizados en los navegadores
Para personalizar los estilos, selecciona las partes de un elemento <progress>
que cada una
del navegador. El uso del elemento de progreso es una sola etiqueta, pero está formada por una
algunos elementos secundarios que se exponen mediante pseudoselectores CSS. Herramientas para desarrolladores de Chrome
te mostrará estos elementos si habilitas la configuración:
- Haz clic con el botón derecho en tu página y selecciona Inspeccionar elemento para abrir las Herramientas para desarrolladores.
- Haz clic en el ícono de configuración ubicado en la esquina superior derecha de la ventana de Herramientas para desarrolladores.
- En el encabezado Elements, busca y habilita la opción Mostrar sombra de usuario-agente. DOM.
Estilos de Safari y Chromium
Los navegadores basados en WebKit, como Safari y Chromium, exponen
::-webkit-progress-bar
y ::-webkit-progress-value
, que permiten un subconjunto de
CSS que se usará. Por ahora, configura background-color
con las propiedades personalizadas.
creados anteriormente, que se adaptan
a la luz y la oscuridad.
/* Safari/Chromium */
progress[value]::-webkit-progress-bar {
background-color: var(--_track);
}
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
}
Estilos de Firefox
Firefox solo expone el pseudoselector ::-moz-progress-bar
en el
elemento <progress>
. Esto también significa que no podemos cambiar el tono de la pista directamente.
/* Firefox */
progress[value]::-moz-progress-bar {
background-color: var(--_progress);
}
Ten en cuenta que Firefox tiene un color de seguimiento configurado a partir de accent-color
mientras que Safari en iOS
tiene una pista celeste. Ocurre lo mismo en modo oscuro: Firefox
tiene una pista oscura, pero
no es el color personalizado que configuramos
y funciona en navegadores basados en Webkit.
Animación
Cuando se trabaja con seudoselectores integrados en el navegador, suele ser un proceso conjunto de propiedades de CSS permitidas.
Cómo animar que la pista se llene
Agregar una transición al
inline-size
de
el elemento de progreso funciona en Chromium, pero no en Safari. Firefox también hace
No debes usar una propiedad de transición en su ::-moz-progress-bar
.
/* Chromium Only 😢 */
progress[value]::-webkit-progress-value {
background-color: var(--_progress);
transition: inline-size .25s ease-out;
}
Cómo animar el estado :indeterminate
Aquí puedo poner más creatividad para ofrecer una animación. Un seudoelemento para Chromium y se aplica un gradiente que se anima hacia atrás y para los tres navegadores.
Las propiedades personalizadas
Las propiedades personalizadas son excelentes para muchas cosas, pero una de mis favoritas es simplemente
lo que le da un nombre a un valor de CSS que, de otro modo, parecería mágico. A continuación, se muestra una
complejo
linear-gradient
:
pero con un lindo nombre. Su propósito y casos de uso se pueden entender con claridad.
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;
}
Las propiedades personalizadas también ayudarán a que el código se mantenga DRY agrupar estos selectores específicos del navegador.
Los fotogramas clave
El objetivo es una animación infinita que va y viene. El inicio y el fin
los fotogramas clave se establecerán en CSS. Solo se necesita un fotograma clave, el del medio.
en 50%
, para crear una animación que regrese al punto en que comenzó, una y
de nuevo.
@keyframes progress-loading {
50% {
background-position: left;
}
}
Orientación a cada navegador
No todos los navegadores permiten la creación de seudoelementos en el <progress>
.
elemento en sí o permite animar la barra de progreso. Más navegadores compatibles
que un seudoelemento, así que actualizo a partir de los pseudoelementos
una base y en barras de animación.
Pseudoelemento de Chromium
Chromium permite el seudoelemento: ::after
, que se usa con una posición para cubrir
el elemento. Se usan las propiedades personalizadas indeterminadas, y las funciones
animación funciona muy bien.
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 de progreso de Safari
En Safari, se aplican las propiedades personalizadas y una animación al barra de progreso del seudoelemento:
progress:indeterminate::-webkit-progress-bar {
background: var(--_indeterminate-track);
background-size: var(--_indeterminate-track-size);
background-position: right;
animation: var(--_indeterminate-track-animation);
}
Barra de progreso de Firefox
En Firefox, las propiedades personalizadas y una animación también se aplican al barra de progreso del seudoelemento:
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 tiene un rol importante con el elemento <progress>
. Controla
el valor enviado al elemento y garantiza que haya suficiente información
documento para lectores de pantalla.
const state = {
val: null
}
La demostración ofrece botones para controlar el progreso. actualizan state.val
y, luego, llama a una función para actualizar
DOM
document.querySelector('#complete').addEventListener('click', e => {
state.val = 1
setProgress()
})
setProgress()
Esta función es donde se produce la organización de la IU/UX. Para comenzar, crea un
función setProgress()
. No se necesitan parámetros porque tiene acceso al
Objeto state
, elemento de progreso y zona <main>
.
const setProgress = () => {
}
Configura el estado de carga en la zona <main>
Dependiendo de si el progreso está completo o no, la <main>
relacionada
el elemento necesita una actualización del
aria-busy
atributo:
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
}
Borrar los atributos si se desconoce el importe de la carga
Si el valor es desconocido o no se establece, null
en este uso, quita value
y
aria-valuenow
Esto convertirá <progress>
en "indeterminate".
const setProgress = () => {
zone.setAttribute('aria-busy', state.val < 1)
if (state.val === null) {
progress.removeAttribute('aria-valuenow')
progress.removeAttribute('value')
progress.focus()
return
}
}
Soluciona problemas de matemáticas decimales en JavaScript
Como elegí seguir con el máximo predeterminado de progreso de 1, la demostración
las funciones de incremento y disminución usan matemáticas decimales. JavaScript y otras
idiomas, no siempre son buenos para
el contenido.
Esta es una función roundDecimals()
que recortará el exceso de las matemáticas
resultado:
const roundDecimals = (val, places) =>
+(Math.round(val + "e+" + places) + "e-" + places)
Redondea el valor para que se pueda presentar y sea legible:
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 + "%"
}
Establece un valor para los lectores de pantalla y el estado del navegador
El valor se usa en tres ubicaciones del DOM:
- El atributo
value
del elemento<progress>
- El atributo
aria-valuenow
- El contenido de texto interno de
<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
}
Priorizar el progreso
Con los valores actualizados, los usuarios videntes verán el cambio en el progreso, pero
los usuarios lectores aún no reciben el anuncio del cambio. Enfoca el
<progress>
y el navegador anunciará la actualización.
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()
}
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías?‽ 🙂
Si tienes otra oportunidad, me gustaría hacer algunos cambios. Creo que hay espacio para limpiar el componente actual y también hay espacio para intentar compilar uno sin las limitaciones de estilo de la pseudoclase del elemento <progress>
. Vale la pena explorarlo.
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web.
Crear una demostración, twittearme vínculos y la agregaré. a la sección de remixes de la comunidad.