StackPractices
intermediate Por Mathias Paulenko

Plantilla de Matriz de Soporte de Navegadores

Una plantilla para registrar los objetivos de soporte de navegadores, la compatibilidad de funciones, los requisitos de polyfills y las estrategias de fallback en toda la matriz de navegadores.

Temas: testing

Descripción General

Una matriz de soporte de navegadores define qué navegadores y versiones soporta tu aplicación, qué funciones están disponibles en cada uno y qué polyfills o fallbacks se necesitan. Sin una, los equipos toman decisiones improvisadas que acaban en experiencias inconsistentes y depuración dolorosa — alguien publica :has() porque funciona en su máquina, y los usuarios de Safari 15 reciben una página rota.

Esta plantilla convierte esas decisiones dispersas en un solo documento: niveles de soporte, matriz de compatibilidad de funciones, estrategia de polyfills, plan de pruebas y política de banner de actualización. Se rellena una vez y se revisa trimestralmente.

Cuándo Usar

Usa esta plantilla cuando:

  • Empiezas un proyecto web nuevo y la línea base de soporte no está escrita
  • Defines requisitos de soporte para un proyecto de cliente
  • Evalúas si abandonar el soporte de un navegador antiguo
  • Planificas la estrategia de polyfills y transpilación
  • Comunicas decisiones de soporte a stakeholders

Combina de forma natural con la plantilla de presupuesto de rendimiento frontend — los polyfills que esta matriz aprueba cuentan dentro de ese presupuesto — y con la checklist de auditoría de accesibilidad para el lado de tecnologías asistivas de la compatibilidad.

Prerequisitos

  • Acceso a analítica: datos reales de uso de navegador/versión de tu audiencia
  • Acuerdo sobre el umbral de uso que justifica el soporte (habitual: > 0.5%)
  • Una lista de las funciones que tu aplicación usa de verdad (o planea usar)
  • Una decisión sobre herramientas de prueba: runner de CI (Playwright) más un servicio de dispositivos reales (BrowserStack, LambdaTest)

Solución

# Matriz de Soporte de Navegadores — `<Nombre del Proyecto>`

## Resumen de la Matriz

| Campo | Valor |
|-------|-------|
| Proyecto | Aplicación Web de Ejemplo |
| Última actualización | 2026-09-21 |
| Responsable | Equipo Frontend |
| Estrategia de soporte | Mejora progresiva |
| Línea base | Últimas 2 versiones + > 0.5% de uso global |
| Soporte IE11 | No |
| Objetivo de transpilación | ES2020 |
| Transpilación CSS | PostCSS + Autoprefixer |

> Los porcentajes de uso de abajo son valores de ejemplo — sustitúyelos por tu propia analítica.

## 1. Navegadores Soportados

### Nivel 1 — Soporte Completo

| Navegador | Versión Mínima | Uso Global | Pruebas | Notas |
|-----------|---------------|------------|---------|-------|
| Chrome | 110+ | 65.2% | Automatizadas + manuales | Evergreen, actualización automática |
| Edge | 110+ | 5.2% | Automatizadas | Basado en Chromium, igual que Chrome |
| Firefox | 115+ | 3.1% | Automatizadas + manuales | ESR + estable |
| Safari | 16+ | 4.3% | Manuales + BrowserStack | macOS + iOS |

### Nivel 2 — Soporte de Mejor Esfuerzo

| Navegador | Versión Mínima | Uso Global | Pruebas | Notas |
|-----------|---------------|------------|---------|-------|
| Samsung Internet | 20+ | 2.6% | Manuales | Android, basado en Chromium |
| Opera | 95+ | 2.1% | No probado | Basado en Chromium, se asume igual que Chrome |
| Chrome (Android) | 110+ | 65.2% (móvil) | Manuales | Chrome móvil |

### Nivel 3 — No Soportado

| Navegador | Versión | Uso Global | Notas |
|-----------|---------|------------|-------|
| Internet Explorer | Todas | 0.3% | Fin de vida junio 2022, no soportado |
| Safari < 16 | < 16 | 0.8% | Mostrar banner de actualización |
| Chrome < 110 | < 110 | 0.4% | Mostrar banner de actualización |
| Firefox < 115 | < 115 | 0.2% | Mostrar banner de actualización |

## 2. Matriz de Compatibilidad de Funciones

### Funciones JavaScript

| Función | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | ¿Polyfill? | Fallback |
|---------|------------|-----------|-------------|-----------|------------|------------|----------|
| ES2020 (optional chaining, nullish coalescing) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| ES2021 (logical assignment, Promise.any) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| ES2022 (top-level await, class fields) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| ES2023 (array findLast, hashbang) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| structuredClone | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Intl.Segmenter | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Import maps | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| CSS Modules (import attributes) | ⚠️ 123+ | ⚠️ 123+ | ❌ | ❌ | ⚠️ 123+ | Sí | El bundler lo gestiona |
| Web Components (custom elements, shadow DOM) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| ResizeObserver | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| IntersectionObserver | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Workers | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Service Workers | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| WebAssembly | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| WebGPU | ⚠️ 113+ | ⚠️ 113+ | ❌ | ❌ | ⚠️ 113+ | Sí | Fallback a WebGL |

### Funciones CSS

| Función | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | ¿Polyfill? | Fallback |
|---------|------------|-----------|-------------|-----------|------------|------------|----------|
| CSS Grid | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Flexbox | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Custom Properties (variables CSS) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Container Queries | ✅ | ✅ | ✅ | ✅ | ✅ | No | Fallback con media query |
| Selector :has() | ✅ 105+ | ✅ 105+ | ✅ 121+ | ✅ 15.4+ | ✅ | No | Fallback con clases |
| Cascade Layers (@layer) | ✅ | ✅ | ✅ | ✅ | ✅ | No | Fallback por especificidad |
| Color-mix() | ✅ 111+ | ✅ 111+ | ✅ 113+ | ✅ 16.2+ | ✅ | No | Valores precomputados |
| Nesting | ✅ 112+ | ✅ 112+ | ✅ 117+ | ✅ 16.5+ | ✅ | No | PostCSS nesting |
| Subgrid | ✅ 117+ | ✅ 117+ | ✅ 71+ | ✅ 16+ | ✅ | No | Fallback a grid normal |
| View Transitions API | ✅ 111+ | ✅ 111+ | ❌ | ❌ | ✅ | Sí | Sin transición |
| Scroll-driven animations | ✅ 115+ | ✅ 115+ | ❌ | ❌ | ✅ | Sí | Scroll con JS |

### APIs Web

| Función | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | ¿Polyfill? | Fallback |
|---------|------------|-----------|-------------|-----------|------------|------------|----------|
| Fetch API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Crypto API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Storage (localStorage, sessionStorage) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| IndexedDB | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Cache API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Push API | ✅ | ✅ | ✅ | ⚠️ 16.4+ | ✅ | No | Fallback con polling |
| Web Notifications | ✅ | ✅ | ✅ | ⚠️ 16.4+ | ✅ | No | Notificaciones in-app |
| Geolocation | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Share API | ✅ | ✅ | ❌ | ✅ | ✅ | Sí | Diálogo de compartir propio |
| File System Access API | ✅ | ✅ | ❌ | ❌ | ✅ | Sí | Fallback descarga/subida |
| Web Authentication (WebAuthn) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Payment Request API | ✅ | ✅ | ❌ | ✅ | ✅ | Sí | Checkout tradicional |

## 3. Estrategia de Polyfills

### Polyfills Requeridos

| Polyfill | Navegadores Objetivo | Tamaño (gzipped) | Estrategia de Carga | Motivo |
|----------|---------------------|------------------|---------------------|--------|
| core-js (funciones ES) | Todos | 12 KB | En build | Transpilación de funciones ES |
| regenerator-runtime | Todos | 3 KB | En build | Transpilación async/await |
| web-share-polyfill | Firefox | 2 KB | Import dinámico | Web Share API |
| webgpu-polyfill | Firefox, Safari | 8 KB | Import dinámico | Fallback WebGPU a WebGL |

### Configuración de Carga de Polyfills

```javascript
// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        chrome: '110',
        edge: '110',
        firefox: '115',
        safari: '16',
        samsung: '20',
      },
      useBuiltIns: 'usage',
      corejs: 3,
    }],
  ],
};
```

```javascript
// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')({
      overrideBrowserslist: [
        'Chrome >= 110',
        'Edge >= 110',
        'Firefox >= 115',
        'Safari >= 16',
        'Samsung >= 20',
      ],
    }),
    require('postcss-nesting'),
  ],
};
```

### Carga Condicional de Polyfills

```javascript
// Carga dinámica del polyfill para Web Share API
async function share(data) {
  if (navigator.share) {
    try {
      await navigator.share(data);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error('Share failed:', err);
      }
    }
  } else {
    const { sharePolyfill } = await import('./polyfills/web-share.js');
    sharePolyfill(data);
  }
}

// Detección de función para WebGPU
async function getRenderingContext(canvas) {
  if (await navigator.gpu?.requestAdapter()) {
    const context = canvas.getContext('webgpu');
    return { type: 'webgpu', context };
  }
  const context = canvas.getContext('webgl2') || canvas.getContext('webgl');
  return { type: 'webgl', context };
}
```

## 4. Estrategias de Fallback

| Función | Estrategia de Fallback | Impacto en Usuario | Esfuerzo de Implementación |
|---------|---------------------|--------------------|-----------------------------|
| View Transitions | Sin transición, cambio de página instantáneo | Sin animación | Bajo — simplemente no usarla |
| Scroll-driven animations | Basada en IntersectionObserver con JS | Timing ligeramente distinto | Medio |
| Web Share API | Diálogo propio de compartir con copiar enlace | UI distinta, misma función | Medio |
| File System Access | Flujo de descarga + subida | Flujo distinto | Medio |
| WebGPU | Renderizador WebGL | Menor rendimiento en 3D | Alto |
| Payment Request API | Formulario de checkout tradicional | Más pasos, misma función | Bajo |
| Push API (Safari < 16.4) | Polling de actualizaciones | Impacto en batería, menos tiempo real | Medio |

## 5. Matriz de Pruebas

| Navegador | Versión | SO | Método de Prueba | Frecuencia | Cobertura |
|-----------|---------|-----|------------------|------------|-----------|
| Chrome | Última | Windows | Playwright CI | Cada PR | E2E + visual |
| Chrome | Última | macOS | Playwright CI | Cada PR | E2E + visual |
| Chrome | Última | Android | BrowserStack | Semanal | Smoke test |
| Edge | Última | Windows | Playwright CI | Cada PR | E2E |
| Firefox | Última | Windows | Playwright CI | Cada PR | E2E |
| Firefox | Última | macOS | Manual | Quincenal | Smoke test |
| Firefox | ESR | Windows | Manual | Mensual | Smoke test |
| Safari | Última | macOS | BrowserStack | Semanal | E2E + visual |
| Safari | Última | iOS | BrowserStack | Semanal | Smoke test |
| Safari | 16.0 | iOS | BrowserStack | Mensual | Smoke test |
| Samsung Internet | Última | Android | BrowserStack | Mensual | Smoke test |

## 6. Configuración de Build

### Configuración de Browserslist

```text
[production]
Chrome >= 110
Edge >= 110
Firefox >= 115
Safari >= 16
Samsung >= 20

[development]
last 2 Chrome versions
last 2 Firefox versions
last 2 Safari versions
```

### Configuración de Vite

```javascript
// vite.config.js
import { defineConfig } from 'vite';
import autoprefixer from 'autoprefixer';

export default defineConfig({
  build: {
    target: 'es2020',
    css: {
      postcss: {
        plugins: [autoprefixer()],
      },
    },
  },
  legacy: {
    targets: ['Chrome >= 110', 'Firefox >= 115', 'Safari >= 16'],
    additionalLegacyPolyfills: ['regenerator-runtime/runtime'],
  },
});
```

## 7. Banner de Actualización

Para navegadores por debajo de la versión mínima soportada, muestra un banner de actualización. El banner usa una prueba de función en lugar de parsear el user-agent — un navegador que no puede ejecutar `structuredClone` está por debajo de cualquier línea base de esta matriz:

```html
<div id="upgrade-banner" hidden>
  <p>Actualiza tu navegador para usar esta aplicación.</p>
</div>

<script>
  // Prueba de función: los navegadores sin estas están bajo nuestra línea base.
  // (El UA sniffing es frágil; el banner solo necesita una señal gruesa.)
  if (!window.structuredClone || !window.fetch || !('loading' in HTMLImageElement.prototype)) {
    document.getElementById('upgrade-banner').hidden = false;
  }
</script>
```

Definir la Línea Base

Todo en la matriz depende de una decisión: dónde se pone la línea de soporte. Tres entradas la deciden:

  1. Tu analítica, no las estadísticas globales. El uso global sitúa a Safari cerca del 20% y a Samsung Internet en torno al 2%, pero lo que importa es tu audiencia — una herramienta interna de empresa puede ver un 80% de Chrome, mientras que una app de consumo en India ve KaiOS y Opera Mini. Extrae los últimos 90 días de tráfico real por navegador/versión antes de escribir un solo nivel.
  2. Un umbral de uso. Elige el número que justifica un nivel — habitualmente >0.5% de sesiones para Nivel 1, >0.1% para Nivel 2, y por debajo un banner. El número exacto importa menos que escribirlo: “soportamos lo que medimos” es una política; “soportamos lo que sea que use la gente” no lo es.
  3. El suelo evergreen. La mayoría de líneas base modernas usan “últimas 2 versiones” de cada navegador importante — Browserslist lo entiende directamente, así que tu configuración de build y tu documento pueden compartir la misma frase.

Una vez fijada la línea base, la asignación de niveles es mecánica: los navegadores en o por encima del umbral con soporte completo de funciones van a Nivel 1; los navegadores sobre el umbral que comparten motor con uno de Nivel 1 (Samsung Internet, Opera, Chrome Android frente a Chrome) van a Nivel 2 con smoke tests periódicos; todo lo demás recibe el banner.

Cómo Rellenarla

Sección 1 (Navegadores Soportados) es una tabla de datos, no de opiniones. Cada fila necesita una versión mínima, una cifra de uso medida de tu analítica y un método de prueba que exista de verdad — si nadie tiene un dispositivo Samsung, escribe “BrowserStack”, no “manual”.

Sección 2 (Compatibilidad de Funciones) es la sección que caduca más rápido, así que acótala: lista solo las funciones que tu app usa o planea usar en los próximos dos trimestres. Una matriz que rastrea todo CSS es inmantenible; una que rastrea tus 15 dependencias reales es una herramienta de decisión. Cada hueco recibe exactamente uno de dos resultados — un polyfill o un fallback — nunca ambos, y nunca en blanco.

Secciones 3–4 (Polyfills y Fallbacks) son una negociación de presupuesto. Cada fila de polyfill lleva su tamaño gzipped porque esos bytes cuentan contra el presupuesto de rendimiento — si el polyfill cuesta más de lo que la función vale para la porción de usuarios afectados, elige el fallback.

Sección 5 (Matriz de Pruebas) es lo que hace real la afirmación de soporte. Si Safari iOS es Nivel 1 pero nadie lo prueba, la matriz es ficción — o se paga el tiempo de BrowserStack o se degrada el nivel. La versión honesta de esta sección nombra la herramienta, la frecuencia y la cobertura de cada fila.

Sección 7 (Banner de Actualización) es la salida elegante para el Nivel 3. Que sea descartable e informativo — un banner que dice “tu navegador es viejo” sin enlace de actualización solo molesta a la gente en equipos corporativos bloqueados que no pueden actualizar de todos modos.

Dos hábitos mantienen el documento honesto una vez escrito. Versiona el documento con el código — la matriz vive en el repo junto a .browserslistrc, y un cambio en uno debería venir con un cambio en el otro en el mismo PR. Y fecha cada número — los porcentajes de uso y las versiones mínimas envejecen rápido; una cifra sin fecha de “a día de” es una afirmación que nadie puede verificar.

Cómo Funcionan los Niveles en la Práctica

La plantilla separa la decisión (asignación de nivel) del mecanismo (polyfills y fallbacks) de la aplicación (matriz de pruebas y banner). Un navegador cae en un nivel según tu analítica, no por preferencia — un navegador con el 3% de tu audiencia recibe soporte completo; el mismo navegador al 0.05% recibe un banner.

Los navegadores de Nivel 1 reciben pruebas completas: E2E automatizado, regresión visual y pases manuales. Los bugs ahí bloquean el release. Los de Nivel 2 reciben pruebas de mejor esfuerzo — suelen estar basados en Chromium y se asume que se comportan como Chrome, pero igualmente reciben smoke tests periódicos. Los de Nivel 3 reciben el banner de actualización y nada más.

La matriz de compatibilidad de funciones es donde los niveles se encuentran con el código. Cada función que usa la app recibe una fila, y las funciones con huecos reciben uno de dos tratamientos: polyfill (añadir bytes para soportar la función) o fallback (degradar con elegancia). Los polyfills encajan en funciones pequeñas y críticas; los fallbacks en funciones grandes o no críticas. Un visor WebGPU degrada a WebGL; un botón de compartir degrada a un diálogo de copiar enlace — ninguno merece 30 KB de polyfill para un 2% de usuarios.

La estrategia de polyfills documenta luego cuáles existen, su tamaño y cómo se cargan. Los polyfills de build (Babel useBuiltIns: 'usage') viajan en el bundle principal. Los dinámicos se cargan bajo demanda con import() — los usuarios con navegadores modernos nunca pagan por bytes que no necesitan.

¿Polyfill o Fallback? La Decisión

Cada hueco en la matriz de funciones se resuelve en una de estas. Los factores decisivos, en orden:

  1. ¿A cuántos usuarios afecta el hueco? Una función ausente en un navegador con 0.5% de tu tráfico recibe la opción barata. Un hueco en un navegador de Nivel 1 (raro, pero Safari suele jugar ese papel) justifica ingeniería de verdad.
  2. ¿Cuánto pesa el polyfill? core-js para un método de Array.prototype ausente cuesta unos pocos cientos de bytes tras tree-shaking; un shim de WebGPU cuesta megabytes y aun así no funcionará bien. Comprueba el peso gzipped antes de comprometerte — la columna de polyfill existe precisamente para forzar esa comprobación.
  3. ¿Puede la función degradar con elegancia? View Transitions degrada a cambios de página instantáneos a coste cero — un fallback perfecto. Web Share degrada a un diálogo de copiar enlace — sigue siendo fallback, pero visible para el usuario y necesita tiempo de diseño. IndexedDB no puede degradar en absoluto; si tu app guarda datos offline, eso es polyfill o “no soportado”.

Cuando ambas opciones son feas, hay una tercera puerta que la plantilla no lista explícitamente: no usar la función. Elegir otra implementación (CSS scroll-snap en lugar de scroll-driven animations) suele ser la estrategia de compatibilidad más barata de todas.

Un Ejemplo Rellenado

Un producto SaaS mediano extrae su analítica de los últimos 90 días y ve: Chrome 71%, Safari 18% (repartido entre macOS e iOS), Edge 4%, Firefox 3%, Samsung Internet 1.5%, todo lo demás por debajo del 0.2%. Con un umbral del 0.5%, eso da cuatro navegadores de Nivel 1 y uno de Nivel 2. IE11 aparece al 0.1% — todo de un único piloto con un cliente — así que cae en Nivel 3 con banner en lugar de un compromiso de soporte, que es exactamente la negociación que la matriz existe para hacer explícita.

La matriz de funciones marca dos huecos reales: Push API necesita Safari 16.4 (sus usuarios iOS en 16.0–16.3 son el 4% del tráfico — merece el fallback de notificaciones in-app) y File System Access está ausente en Firefox y Safari (el flujo de exportación degrada a descarga/subida — mismo resultado, camino distinto). WebGPU se queda experimental detrás de un fallback a WebGL porque la vista previa 3D es un extra, no el producto.

Proyección de coste: la lista de polyfills suma ~15 KB gzipped, todo cargado dinámicamente salvo core-js; el fallback de push para Safari son dos días de trabajo; el tiempo de BrowserStack para Safari y móvil ya está en el presupuesto. Total: un documento que el equipo de soporte puede citar cuando un cliente en Firefox ESR pregunte por qué el botón de compartir se ve distinto.

Fíjate en lo que el ejemplo hace y una lista de bullets no puede: adjunta una decisión a cada número. “Samsung Internet al 1.5%” no es trivia — es por qué ese navegador está en Nivel 2 en lugar de Nivel 3. “4% de usuarios iOS en 16.0–16.3” no es una nota al pie — es el argumento para financiar el fallback de notificaciones push. Cada celda de la matriz debería sobrevivir a ese mismo test de “¿y qué?”; una fila que no cambia una decisión es decoración.

Mantener la Matriz Viva

Una matriz obsoleta es peor que no tenerla porque se lee como actual. La revisión trimestral son quince minutos:

  1. Revisa la analítica — ¿algún navegador cruzó tu umbral de uso, en cualquier dirección?
  2. Revisa caniuse — ¿alguna celda ”❌” pasó a soportada? (Safari y Firefox publican constantemente.)
  3. Actualiza Browserslist — la línea base last 2 versions se mueve cada 6 semanas la edites o no.
  4. Re-ejecuta los smoke tests en navegadores de Nivel 2 que no hayas tocado.

La configuración de Browserslist es el mecanismo de aplicación — alimenta a Babel, Autoprefixer y ESLint, así que actualizarla cambia lo que produce el build. Mantenla como fuente única de verdad y trata el documento de la matriz como su espejo legible por humanos.

Dos casos límite que la revisión trimestral debería seguir cazando. Webviews embebidos — los enlaces abiertos dentro de Facebook, Slack o tu propia app nativa corren en un webview que no coincide con la versión ni las funciones del navegador que lo rodea, y los usuarios atrapados ahí lo reportan como “tu web está rota”, no como “mi webview es viejo”. Si el tráfico in-app es significativo, los webviews merecen su propia fila. Flotas bloqueadas — los clientes enterprise en dispositivos gestionados no pueden actualizar aunque quieran; un abandono de soporte para ellos es una conversación de contrato, no un banner.

La matriz también se gana la vida fuera de ingeniería. Soporte la cita cuando un usuario reporta un fallo en un navegador no soportado (“no probamos esa configuración — así se actualiza”). Producto la usa para responder “¿podemos lanzar esta función?” antes de que empiece el desarrollo. Y la dirección lee la tabla de niveles como la respuesta a “¿por qué gastamos minutos de CI en Firefox ESR?” — el documento convierte trabajo de mantenimiento invisible en una política visible.

Flujo de decisión de soporte de navegadores — la analítica de uso asigna cada navegador a un nivel, la matriz de funciones marca los huecos, los huecos se convierten en polyfills o fallbacks, y la matriz de pruebas más la revisión trimestral mantienen la línea base actualizada

Variantes

ContextoEnfoqueNotas
App enterpriseSoportar navegadores antiguos (IE11 si es requerido)Puede necesitar polyfills más pesados
App de consumoSolo evergreen, últimas 2 versionesPolyfills mínimos
App web móvilCentrarse en navegadores móvilesSafari iOS, Chrome Android, Samsung
Sitio gubernamentalAccesibilidad estricta + soporte legacySection 508 puede requerir soporte de navegadores antiguos
Herramienta internaSolo ChromeMínimas pruebas cross-browser
Audiencia globalIncluir navegadores regionales (UC, Opera Mini)Comprobar estadísticas de uso regionales

Las variantes cambian sobre todo qué navegadores aparecen, no cómo funciona la matriz. Una herramienta interna colapsa todo en una política de una línea “Chrome último” — e incluso eso merece una línea escrita, porque es la diferencia entre una elección deliberada y un accidente. Una audiencia gubernamental o sanitaria va en dirección contraria: la ley de accesibilidad y las flotas bloqueadas pueden mantener vivo un nivel legacy durante años, que es exactamente cuando la columna de fallback se gana su espacio.

Lo Que Funciona

Estos hábitos vienen de equipos que mantienen una matriz en lugar de escribirla una vez y olvidarla. El hilo común: el documento solo se mantiene útil si los números que lleva dentro son medidos, fechados y aplicados por herramientas — cualquier cosa escrita a mano y sin aplicación deriva hacia la ficción en dos trimestres.

  1. Usa Browserslist como fuente única de verdad — alimenta Babel, Autoprefixer y ESLint
  2. Prueba en navegadores reales — los emuladores y simuladores pierden diferencias de renderizado
  3. Detecta funciones, no navegadores — if ('share' in navigator), no if (isSafari)
  4. Carga los polyfills dinámicamente — no penalices a los navegadores modernos con bytes de polyfill
  5. Revisa la matriz trimestralmente — el uso de navegadores cambia, caen versiones nuevas
  6. Rastrea la analítica de uso de navegadores — abandona el soporte cuando el uso cae bajo el umbral
  7. Muestra banners de actualización a navegadores no soportados — no rompas en silencio

Errores Comunes

  1. Soportar demasiados navegadores — cada navegador añade coste de pruebas y polyfills
  2. No probar Safari — Safari tiene peculiaridades propias de CSS y JS a pesar de ser WebKit
  3. Detección de navegador en lugar de detección de función — frágil y se rompe con versiones nuevas
  4. Sin estrategia de fallback — las funciones que fallan en silencio son peores que las experiencias degradadas
  5. Ignorar los navegadores móviles — el uso móvil suele superar al de escritorio
  6. Sin analítica para informar decisiones — soportar navegadores que nadie usa desperdicia esfuerzo
  7. No actualizar la matriz — las versiones de navegador se mueven rápido; las matrices obsoletas dan falsa confianza

Solución de Problemas

Estos son los modos de fallo con los que se topa el propio documento — la mayoría son bugs de proceso, no de navegador.

  • La matriz dice “soportado” pero los usuarios reportan fallos: la matriz derivó respecto a la configuración de Browserslist — compara el documento con .browserslistrc y recompila. El documento es un espejo; la configuración es la verdad.
  • El bundle de polyfills no deja de crecer: estás enviando polyfills de build para funciones que solo les faltan a navegadores antiguos. Muévelos a import() dinámico detrás de una comprobación de función para que los navegadores modernos se salten los bytes.
  • Safari falla en una función que Chrome tiene: Safari adopta las APIs más tarde y con peculiaridades (Push API necesita 16.4+, File System Access está ausente). Consulta caniuse antes de adoptar, no después del reporte de bug.
  • El banner de actualización nunca aparece: el script apunta a un elemento que no existe o la prueba es incorrecta — verifica que el id coincide y prueba la comprobación en un perfil de navegador antiguo, no solo en el override de UA de devtools.
  • Las cifras de uso de la matriz parecen viejas: los porcentajes de ejemplo de esta plantilla son ilustrativos — sustitúyelos por tu analítica; las medias globales difieren salvajemente de, por ejemplo, una audiencia enterprise anclada a builds ESR.

Lecturas Adicionales

Preguntas frecuentes

¿Cómo decidimos qué navegadores soportar?

Empieza con los datos de analítica de tu sitio actual. Identifica los navegadores con > 0.5% de uso. Consulta caniuse.com para la compatibilidad de funciones. Considera tu audiencia: los usuarios enterprise pueden estar anclados a navegadores antiguos. Fija la línea base en las últimas 2 versiones de cada navegador importante más cualquier navegador por encima de tu umbral de uso.

¿Deberíamos soportar Internet Explorer?

No. IE11 llegó a su fin de vida en junio de 2022, y Microsoft redirige a los usuarios de IE11 a Edge. Soportar IE11 exige polyfills pesados, limita tu conjunto de funciones CSS y JS, y aumenta la carga de pruebas. Muestra un banner de actualización en su lugar.

¿Cómo gestionamos la adopción más lenta de funciones en Safari?

Safari adopta las funciones web más despacio que Chrome y Firefox, y algunas APIs llegan una o dos versiones después (Push API necesitó la 16.4). Prueba en Safari pronto, consulta caniuse antes de adoptar una función, e implementa un fallback o un polyfill dinámico para lo que a Safari le falte. No asumas "funciona en Chrome, así que funciona en todas partes".

¿Cuál es el coste de soportar un navegador adicional?

Cada navegador adicional suma tiempo de pruebas (manuales o automatizadas), tamaño potencial de bundle de polyfills, tiempo de depuración para bugs específicos del navegador y trabajo de compatibilidad CSS. Estima un 5-10% del tiempo de desarrollo frontend por nivel de navegador adicional. Ponlo en la balanza contra el porcentaje de usuarios que ese navegador representa.

¿Cómo sabemos cuándo abandonar un navegador?

Cuando cae por debajo de tu umbral de uso durante dos trimestres consecutivos — un único descenso puede ser ruido. Revisa tanto las estadísticas globales como tu propia analítica; tu audiencia puede sesgar de otra forma. Anuncia el abandono en la matriz, actualiza la configuración de Browserslist y deja que el banner de actualización cubra a los rezagados.

¿Con qué frecuencia deberíamos revisar la matriz?

Trimestralmente como mínimo. Las versiones de navegador se actualizan cada 4-6 semanas y los datos de uso se mueven a medida que los usuarios actualizan. Revisa la matriz, comprueba la analítica por cambios de uso, verifica los datos de caniuse para funciones nuevas y actualiza la configuración de Browserslist. Abandona los navegadores que caen por debajo del umbral de uso.