Cómo compilar un componente de barra de carga

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.

. Demostración clara y oscura, indeterminada, creciente y de finalización en Chrome.

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.

Las pestañas claras y oscuras en cada navegador proporcionan una 
    descripción general del ícono adaptable de arriba abajo: 
    Safari, Firefox y Chrome.
La demostración se muestra en Firefox, Safari, Safari en iOS, Chrome y Chrome para Android en esquemas claros y oscuros.

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;
}

Captura de pantalla de las Herramientas para desarrolladores que muestra el elemento Screen ready only.

Á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);
}

Captura de pantalla de la barra de carga al 100% y una marca de verificación al final.

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;
}

Captura de pantalla de la barra de carga con un anillo de enfoque alrededor. Todos los colores coinciden.

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:

  1. Haz clic con el botón derecho en tu página y selecciona Inspeccionar elemento para abrir las Herramientas para desarrolladores.
  2. Haz clic en el ícono de configuración ubicado en la esquina superior derecha de la ventana de Herramientas para desarrolladores.
  3. En el encabezado Elements, busca y habilita la opción Mostrar sombra de usuario-agente. DOM.

Captura de pantalla de dónde se habilita la exposición del shadow DOM del usuario-agente en Herramientas para desarrolladores.

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);
}

Captura de pantalla que muestra los elementos internos del elemento de progreso.

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);
}

Captura de pantalla de Firefox y dónde encontrar las partes del elemento de progreso.

Captura de pantalla de la esquina de depuración (Debugging Corner) en la que se usan Safari para iOS y iOS. 
  En Firefox, Chrome y Chrome en Android, la barra de carga funciona.

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:

  1. El atributo value del elemento <progress>
  2. El atributo aria-valuenow
  3. 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()
}

Captura de pantalla de la app Voz en off para Mac OS 
  leer el progreso de la barra de carga al usuario.

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.

Remixes de la comunidad