Product Craft Bible
Video & Media
Inicio Media y Archivos Video & Media
Media y Archivos

Video & Media

8 reglas W3C APG Media Player Pattern · Plyr.js ARIA docs · Mux Player accessibility · WCAG 2.1 SC 4.1.2 (Name, Role, Value)Doherty Threshold (300ms), Miller/Doherty 1982 · MDN HTMLMediaElement.error · YouTube buffering UX · Material Design: Loading statesChrome autoplay policy (2018) · Web.dev: Video and LCP · Apple HIG Auto-Playing Video · IntersectionObserver API (MDN)W3C WebVTT spec · WCAG 2.1 SC 1.2.2 Captions (Level A) · Facebook video data 2016 · MDN <track> element
120

Video & Media

8 reglas
1070

Controles custom vs. native: decisión y ARIA

Los controles nativos del navegador ofrecen accesibilidad sin costo, pero sacrifican branding y consistencia cross-browser. Los controles custom son obligatorios cuando el sistema de diseño lo exige, pero requieren implementación explícita de ARIA, focus management y keyboard events. La regla de oro: si no puedes invertir en accesibilidad custom, usa native controls.

W3C APG Media Player Pattern · Plyr.js ARIA docs · Mux Player accessibility · WCAG 2.1 SC 4.1.2 (Name, Role, Value)
Preferir
role="toolbar" aria-label="Controles del video" button aria-label="Pausar" aria-pressed="true" slider aria-valuetext="1:22 de 3:58" role="timer" aria-live="off"
role="toolbar" + aria-label en wrapper · aria-label dinámico en play/pause · aria-valuetext en progress · role="timer" con aria-live="off"
Evitar
1:22 / 3:58 Sin ARIA
Divs con onclick · Sin role · Sin tabindex · Invisible para teclado y screen readers · Viola WCAG 2.1 SC 4.1.2
1071

Estados del player: loading, error y sin fuente

Un player sin estados visuales definidos genera percepción de UI rota. El usuario no distingue entre "cargando", "error de red" y "video no disponible" si todos lucen igual (pantalla negra). Cada estado necesita feedback visual y, cuando aplique, una acción de recuperación. El skeleton debe preservar el aspect-ratio para evitar CLS.

Doherty Threshold (300ms), Miller/Doherty 1982 · MDN HTMLMediaElement.error · YouTube buffering UX · Material Design: Loading states
Preferir
Cargando
Error de red
Reintentar
Error
No disponible
Sin fuente
Skeleton shimmer preserva aspect-ratio · Error con mensaje legible + acción · Empty state ilustrado, nunca pantalla negra
Evitar
Cargando
Error
Sin fuente
Los 3 estados lucen idénticos · El usuario no sabe si carga, hay error o el player está vacío · Sin acción de recuperación
1072

Autoplay: cuándo es aceptable y CWV impact

El autoplay no solicitado destruye la confianza del usuario y contamina el entorno sonoro. Solo es aceptable muted en hero videos de fondo, feeds tipo TikTok/Reels con IntersectionObserver, y videos iniciados por el usuario en otra pantalla. El autoplay con sonido está bloqueado por defecto en Chrome, Safari y Firefox desde 2018. Un video como elemento LCP impacta directamente Core Web Vitals.

Chrome autoplay policy (2018) · Web.dev: Video and LCP · Apple HIG Auto-Playing Video · IntersectionObserver API (MDN)
Preferir
Hero Video
autoplay muted loop playsinline
Activar sonido
<video autoplay muted loop playsinline>
preload="none" para no bloquear recursos críticos
poster= imagen como LCP candidate optimizada
Botón unmute accesible en esquina inferior derecha
Evitar
[video bloqueado por el navegador]
<video autoplay> sin muted, bloqueado desde Chrome 66
Resultado: pantalla negra estática silenciosa
1073

Captions y subtítulos: WebVTT, posición y estilos

Los captions son requisito legal en muchas jurisdicciones (ADA, WCAG 2.1 SC 1.2.2) y el 85% del video en redes sociales se consume sin sonido (Facebook 2016), los captions aumentan retención hasta 80%. La diferencia técnica: los captions incluyen descripción de sonidos no verbales ("Música dramática"), los subtítulos solo transcriben diálogo.

W3C WebVTT spec · WCAG 2.1 SC 1.2.2 Captions (Level A) · Facebook video data 2016 · MDN <track> element
Preferir
[Música dramática] Bienvenidos al producto.
CC
1:48 / 4:00
<track kind="captions" src="es.vtt" srclang="es" label="Español"> ::cue { background-color: rgba(0,0,0,.8); color: #fff; font-size:1rem; } CC button: aria-pressed="true/false"
Fondo rgba(0,0,0,.8) · color #fff · min 16px · CC con aria-pressed · kind="captions" incluye sonidos no verbales
Evitar
Bienvenidos al producto.
text-shadow sin fondo · Ilegible sobre frames claros · Sin sonidos no verbales · Sin botón CC · Sin ARIA
1074

PiP, fullscreen y keyboard shortcuts estándar

Los keyboard shortcuts son el diferenciador entre un player funcional y uno profesional. YouTube estableció el vocabulario estándar: K/Space para play/pause, J para -10s, L para +10s, F para fullscreen, M para mute. El OSD (on-screen display) de 1.5s confirma visualmente que el shortcut se registró. Interceptar keydown globalmente sin verificar foco es el anti-patrón más común.

YouTube Keyboard Shortcuts, estándar de facto · MDN Fullscreen API · MDN Picture-in-Picture API
Preferir
-10s
1:48 / 4:00
K / Space
Play/Pause
J / L
±10s
F
Fullscreen
M
Mute
OSD 1.5s al presionar shortcut · Shortcuts estándar YouTube · Verificar playerEl.contains(activeElement) · PiP + Fullscreen APIs
Evitar
Click para play
0:00 / 4:00
Sin keyboard shortcuts · Sin PiP · Sin fullscreen · Sin OSD · Espacio global capturado sin verificar foco
1075

Progress bar: scrubbing, thumbnails y chapter markers

La progress bar concentra la mayor densidad de interacción: scrubbing, seek por click, preview de contenido y navegación por capítulos. El error más común es implementarla como input[type=range] sin customización, cuya apariencia varía radicalmente entre navegadores. Un hit área de 44px es obligatorio para uso táctil aunque la barra visual sea de 4px. El thumbnail preview convierte la exploración de contenido de lineal a espacial.

Mux Thumbnail Sprites · Apple HIG Buttons (44pt touch target) · WebVTT chapters (MDN) · Cloudflare Stream
Preferir
2:12 / 6:30 Cap. 2: Intro
Thumbnail preview en hover 2:12
2:12
Hit área 44px · Barra visual 4px expandible a 6px en hover · Buffer diferenciado · Chapter dots · Thumbnail preview · Ball thumb visible
Evitar
2:12 / 6:30
input[type=range] nativo · Aspecto varía por navegador · Sin thumbnail preview · Sin chapter markers · Hit área = solo la barra visual
1076

Playback speed y quality selector: settings panel UX

Exponer los controles de velocidad y calidad directamente en la toolbar destruye el espacio en mobile y crea ruido visual en desktop. El patrón correcto (YouTube, Vimeo, Mux) es un único botón de engrane que despliega un panel con secciones. La persistencia de preferencias vía localStorage es fundamental: el usuario que siempre escucha a 1.5x no debería configurarlo en cada sesión.

YouTube Settings Panel, referencia de facto · hls.js API Quality Switch Control · Plyr.js settings panel
Preferir
1:48 / 6:30 1.5×
Velocidad
0.5×
0.75×
Normal
1.25×
1.5×
Calidad
Auto
1080p
720p
360p
Botón engrane · Panel con secciones · role="option" + aria-selected · Persistir en localStorage · Auto por defecto en calidad
Evitar
Velocidad: Calidad:
<select> nativo · Aspecto varía por OS · Toolbar saturada · Sin persistencia · No estilizable en todos los browsers
1077

Audio players: waveform, playlist y mini-player persistente

El audio player tiene un caso de uso fundamental que lo distingue del video: el usuario necesita navegar por la app mientras escucha. Usar múltiples instancias de audio[controls] o un player que se destruye al navegar convierte al reproductor en un componente decorativo. El mini-player persistente en posición fija es la solución arquitectural. La Media Sessión API expone controles nativos en lock screen y notificaciones.

WaveSurfer.js · Media Sessión API (MDN) · Spotify Web Player, referencia de mini-player cross-page
Preferir
Deep Focus · Sessión 3
Lo-fi Beats · Ambient
1Deep Focus · Session 34:22
2Rain on Glass3:18
3Midnight Study5:01
Deep Focus · Session 3
Waveform canvas como progress · Mini-player position:fixed en producción · Media Session API para lock screen · padding-bottom en body cuando activo
Evitar
Deep Focus · Session 3
Rain on Glass
Midnight Study
Múltiples audio[controls] · Se destruyen al navegar · Sin estado compartido · Sin waveform · Sin Media Session API