Upload Patterns
Upload Patterns
8 reglasDrag & drop siempre con botón de fallback visible
El arrastrar y soltar es una interacción de raton por defecto: ni el teclado, ni el lector de pantalla, ni el móvil pueden activarla. Por eso toda zona de carga debe incluir un <input type="file"> con label visible como mecanismo equivalente, no como mejora opcional. WCAG 2.1.1 (Keyboard) exige que la selección de archivos sea operable por teclado, y WCAG 2.5.7 (Dragging Movements) pide que toda función con arrastre se logre con un solo puntero sin arrastrar. Nunca implementes el drop como único camino de carga.
La dropzone comunica sus cuatro estados, no solo con color
Una zona de carga sin feedback al arrastrar deja al usuario sin saber si el blanco esta activo. Debe distinguir cuatro estados (inactivo, drag-over, subiendo y resultado) mediante borde, icono y texto, no solo color: WCAG 1.4.1 (Use of Color) prohibe que el color sea el único medio, y 1.4.11 (Non-text Contrast) exige 3:1 en el borde y los iconos de estado. El patrón reconocido es un borde punteado neutro que se rellena con un fondo tintado al arrastrar; durante la subida, reemplaza el contenido con el progreso para evitar re-drops a media carga.
WCAG 1.4.1 Use of Color · WCAG 1.4.11 Non-text Contrast · USWDS File InputValida tipo, tamaño y cantidad en el cliente antes de subir
Subir un archivo de tipo prohibido o sobredimensionado para que falle en el servidor desperdicia tiempo y ancho de banda. La validación debe ocurrir justo después de seleccionar, antes de transferir bytes, con un mensaje específico por archivo. WCAG 3.3.1 (Error Identification) exige identificar en texto el elemento erroneo y 3.3.3 (Error Suggestion) sugerir la corrección. El atributo accept filtra el picker pero es saltable, así que refuerzalo con JS, y empareja el error con una region aria-live porque el error visual solo no se anuncia al lector.
Muestra progreso por archivo y total, con acción de cancelar
El usuario necesita saber que su carga avanza, cuanto falta y mantener el control para abortarla. Una barra de progreso se vuelve necesaria para cargas que superen ~10 segundos; por debajo basta un spinner (Smashing Magazine, citando a Nielsen). Cada archivo de una cola multiple lleva su propio indicador, y un resumen muestra el total ("2 de 3 archivos · 78%"). Nunca congeles la barra al 99%: disimula la espera con movimiento constante. El botón de cancelar debe estar presente y no pedir confirmación mientras la carga no termina.
Smashing Magazine "Animated Progress Indicators" (2016) · Uploadcare UX · Eleken "File Upload UI"Previsualiza lo seleccionado con acción de eliminar por item
Tras seleccionar archivos, el usuario necesita confirmar visualmente que escogio los correctos antes de subir. Muestra miniaturas para imagenes e iconos por tipo con nombre y tamaño para el resto; los thumbnails atrapan errores de archivo equivocado (subir "contract_v1.pdf" en vez de "contract_final.pdf") antes de gastar ancho de banda. Cada item debe ser eliminable individualmente, y la acción de borrar debe ser un botón enfocable y etiquetado (con aria-label="Quitar foto.jpg"), no una X muda, para cumplir WCAG 2.4.6 (Labels).
Errores por archivo con reintento, nunca falles todo el lote
Cuando un archivo falla (timeout de red, error de servidor, tipo rechazado), solo su fila entra en estado de error con un mensaje específico y acción de reintentar; el resto de la cola sigue subiendo sin interrupción. WCAG 3.3.1 y 3.3.3 piden identificar el error en texto y sugerir la corrección. Ofrece un "Reintentar" para problemas temporales como timeouts, o quitar el archivo y probar otro, manteniendo un tono amable. Distingue errores corregibles por el usuario ("Tipo no soportado, usa JPG o PNG") de errores de sistema ("Error del servidor, reintenta").
WCAG 3.3.1 Error Identification · Eleken "File Upload UI" · Uploadcare UX · Smashing Magazine "Offline-Friendly Upload" (2025)Multi-archivo en paralelo y en segundo plano: no bloquees la UI
Las cargas nunca deben bloquear la interacción del usuario con el resto de la página. Deben correr asincronas en la capa de red del navegador mientras el hilo principal queda libre para renderizar, responder a clics y actualizar la UI en tiempo real (es el estandar de la industria para mantener una interfaz responsiva). El estado de la cola debe persistir aunque el usuario cambie de pestana o se desplace: un widget flotante de estado, al estilo del panel de Google Drive, que sobreviva a la navegación, es el patrón de referencia. Navegar fuera no debe cancelar ni pedir confirmación.
Readability "JS File Upload Patterns" · Page Flows "File Upload" · Smashing Magazine "Offline-Friendly Upload" (2025)Carga accesible: input real, label asociado y aria-live para cada cambio
Un componente de carga con estilo personalizado debe construirse sobre un <input type="file"> real con un <label> asociado por for/id, nunca reemplazado por un <div> con onclick. El rol progressbar no es una live region, así que un lector no anuncia sus cambios: hay que emparejarlo con un <div role="status" aria-live="polite"> que se actualice con texto como "67% completado". Usa polite para progreso rutinario y role="alert" (assertive) solo para errores duros. La region debe existir en el DOM antes de inyectar contenido; anadirla al vuelo es poco fiable entre lectores.
<input type="file" id="up" multiple
accept=".pdf,.jpg,.png">
<div role="status" aria-live="polite"
class="sr-only"></div> // la region existe antes de inyectar "67%"
Subir
</div> // sin label, sin input real, sin aria-live
- R-791 Drag & drop siempre con botón de fallback visible
- R-792 La dropzone comunica sus cuatro estados, no solo con color
- R-793 Valida tipo, tamaño y cantidad en el cliente antes de subir
- R-794 Muestra progreso por archivo y total, con acción de cancelar
- R-795 Previsualiza lo seleccionado con acción de eliminar por item
- R-796 Errores por archivo con reintento, nunca falles todo el lote
- R-797 Multi-archivo en paralelo y en segundo plano: no bloquees la UI
- R-798 Carga accesible: input real, label asociado y aria-live para cada cambio