El elemento <progress> es la forma semántica de representar el progreso de finalización de una tarea, como descargar o subir información, o completar parte de un proceso de varios pasos. El uso del elemento nativo ayuda con la accesibilidad, ya que los lectores de pantalla tienen un role="progressbar" implícito y pueden anunciar valores.
En esta guía, se implementa una barra de progreso horizontal con un diseño completo y personalizaciones de estilo.
¿Barra de progreso o círculo de progreso?
- Si el espacio es limitado o se prefiere una relación de aspecto cuadrada, considera usar una barra de progreso circular (círculo de progreso): Círculo de progreso
- Se prefieren los íconos giratorios para la mayoría de las situaciones de progreso indeterminado (Ícono giratorio de carga). Usa barras de progreso indeterminadas cuando el estado indeterminado sea temporal y pronto se reemplace por una barra de progreso determinada (p. ej., cuando se esperan metadatos sobre una carga antes de que se pueda mostrar un porcentaje de progreso).
Marca
- Usa un elemento
<progress>nativo: Proporciona accesibilidad integrada y un comportamiento coherente con la plataforma. - Usa
maxsi se necesita una escala diferente de la predeterminada de 0 a 1. - Para un valor de progreso específico, establece un atributo
value. Omitevaluepara un estado indeterminado (p.ej., antes de que comience un feedback más concreto). - Usa
<label for="...">,aria-labelledbyoaria-labelpara asociar el elemento de progreso con una etiqueta.
¿Qué tipo de personalización se necesita?
Para aplicar un diseño limitado, estas propiedades son suficientes:
accent-colorpara cambiar el color de la barra de progresowidth/inline-sizepara cambiar el ancho de la barra de progresoheight/block-sizese aplica de forma inconsistente; realiza una prueba primerowriting-mode: vertical-lrpara las barras verticales.- Efectos que se aplican después de pintar:
opacity,filter,clip-path, máscaras, transformaciones,box-shadow, etcétera.
Si el diseño limitado es suficiente, omite la siguiente sección.
ADVERTENCIA: Ciertas propiedades (p.ej., border-radius, background, border) inhabilitarán de forma silenciosa el diseño predeterminado de la barra de progreso. Si necesitas un diseño que vaya más allá de las propiedades anteriores, debes implementar un diseño completamente personalizado, como se describe en la siguiente sección. NO apliques un diseño más extenso "por si acaso funciona".
Estilo completamente personalizado
1. Configuración básica
- OBLIGATORIO: Usa
appearance: nonepara habilitar el diseño personalizado. - Restablece los límites de UA con
border: 0 solid - Para recortar la barra activa según el radio del borde del elemento, usa
overflow: clipO aplica el mismoborder-radiusa la barra. NO usesborder-radius: inheriten la barra, ya que depende de la estructura específica de los elementos en el DOM de sombra.
progress {
/* Reset default browser styles */
appearance: none;
border: 0 solid;
/* Clip bar to element’s border-radius */
overflow: clip;
}
2. Diseño
- Aplica diseño a la pista en el elemento
progress. - Usa el seudoelemento adecuado para el navegador para orientar la barra de progreso:
::-webkit-progress-valuepara WebKit/Blink y::-moz-progress-barpara Firefox. - OBLIGATORIO: NO agrupes los seudoelementos
-webkit-y-moz-en la misma regla. La regla se considerará no válida y se ignorará.:is()y:where()no ayudan en este caso, ya que no admiten seudoelementos. - Cada seudoelemento orientado al navegador debe aparecer exactamente una vez. Las personalizaciones específicas del estado (como el tamaño del segmento indeterminado y la animación) se deben enrutar a través de las propiedades personalizadas establecidas en el propio elemento
progress. - Opcional: Agrega una transición
inline-sizeen el relleno para que los cambios de valor determinados se animen en lugar de saltar. Firefox ajusta el tamaño del relleno en el diseño y lo ignora, lo que no causa problemas.
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. Estilo de estado indeterminado
Agrega una animación personalizada para el estado indeterminado: Convierte el pseudoelemento de relleno en un segmento corto y deslízalo por la pista.
- Aplica diseño al relleno en sí; NO lo vuelvas a crear como fondo en
progress. - El relleno indeterminado solo existe a través de su animación: los fotogramas clave establecen su longitud con un
inline-sizereal (esto supera alinline-sizeintercalado del UA de Chromium) y lo mueven contranslate. NO usesscalepara la longitud, ya que distorsiona cualquier patrón pintado en el relleno. - Los porcentajes de
translatese resuelven en función del ancho del relleno, por lo que debes expresar las posiciones en unidades de pista: convierteprogressen un contenedor de tamaño intercalado y usacqien los fotogramas clave. Incluyemargin-right: autoenfrom, topara que Chromium también coloque el relleno en la parte física izquierda en RTL. - Usa transformaciones, no márgenes, para mover el relleno.
- Las transformaciones mantienen la animación en el compositor (a menos que también se esté animando
inline-size). - Los márgenes se rompen en RTL por dos motivos distintos:
- Firefox posiciona el relleno indeterminado usando solo su margen izquierdo físico, por lo que
margin-inline-startno tiene efecto en RTL y el relleno nunca se mueve. - En Chromium y WebKit, que disponen el relleno como un bloque normal, una vez que un margen empuja el relleno más allá del final de la pista, los márgenes se suman a más de la pista. El CSS estándar resuelve ese problema ignorando
margin-leften RTL, por lo que el segmento salta al borde en lugar de deslizarse fuera de él. translatese aplica después del diseño, por lo que no se aplica ninguno de los problemas.
- Las transformaciones mantienen la animación en el compositor (a menos que también se esté animando
- Para duplicar el movimiento en la escritura de derecha a izquierda (como lo hace la barra nativa de Chrome), invierte la animación en
:dir(rtl):
progress:dir(rtl) {
--progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
- Define las propiedades públicas de
--progress-bar-indeterminate-*enprogress(su nombre ya las limita al estado) para que los consumidores puedan anularlas sin conocer:indeterminate. - Para variar la longitud del segmento (p.ej., el borde inicial se adelanta y el borde final lo alcanza), configura
--progress-bar-indeterminate-animationen dos animaciones simultáneas: una cuyos fotogramas clave animentranslateencqi(posición) y otra cuyos fotogramas clave animeninline-size(longitud), de modo que cada una obtenga sus propios fotogramas clave y aceleración. NO intentes dar forma a ambos bordes en una sola lista de fotogramas clave; elanimation-timing-functionpor fotograma clave se aplica a todas las propiedades de ese fotograma clave. --progress-bar-animationse aplica en ambos estados y se puede usar para efectos como un brillo determinado.revert-rulese usa para anular solo cuando se establece, para maximizar la componibilidad.- Nunca inhabilites la animación indeterminada (
animation: nonedeja un relleno de ancho cero en Chromium). Para reducir el movimiento, disminuye la velocidad.
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 */
}
Consideraciones adicionales de accesibilidad
Además de los lineamientos mencionados anteriormente, ten en cuenta lo siguiente:
- Anuncia la finalización sin robar el foco: No configures
tabindexni llames a.focus()en<progress>, ya que no es interactivo y mover el foco a él interrumpe a los usuarios del teclado. Para anunciar cuando finaliza una tarea en segundo plano, actualiza un elemento de estado cortés (role="status"oaria-live="polite"). - No agregues texto de resguardo dentro de
<progress>: Los navegadores actualizados ignoran el texto dentro de<progress>, y las tecnologías de accesibilidad no lo usan. - Evita
aria-busy+aria-describedbyen un<div>genérico sin etiqueta.aria-busytiene una compatibilidad inconsistente con el lector de pantalla, y las tecnologías de accesibilidad ignoranaria-describedbyen un<div>sin nombre y sin un rol. Si el contenido de una región no está listo mientras se carga, ocúltalo conhiddenoaria-hidden="true". - SÍ asegúrate de que haya un contraste adecuado entre la barra de progreso y la pista cuando agregues estilos personalizados.
- Considera alternativas para representar el estado indeterminado: Las animaciones de la barra de progreso indeterminada que se muestran en paralelo a otro contenido pueden considerarse una distracción para algunos usuarios.
Estrategias de resguardo
Resguardos y compatibilidad de navegadores con accent-color
La propiedad accent-color es una mejora progresiva.
Los navegadores que no admitan esta propiedad la ignorarán y usarán los colores predeterminados de su IU.
Si no se admite accent-color y se requiere aplicar estilo a la barra de progreso, usa el método de estilo personalizado para aplicar el estilo preferido a la barra de progreso.
Resguardos y compatibilidad de navegadores para revert-rule
Si no se admite revert-rule, animation: var(--progress-bar-animation, revert-rule) recurre a animation-name: revert-rule con una duración de 0s cuando no se establece --progress-bar-animation, lo que es una operación nula inofensiva cuando no existe @keyframes revert-rule.