Skip to content
StackPractices
intermediate Por Mathias Paulenko

Plantilla de Matriz de Soporte de Navegadores

Una plantilla para trackear targets de soporte de navegadores, compatibilidad de features, requisitos de polyfills y estrategias de fallback.

Temas: testing

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Overview

Un browser support matrix define qué browsers y versions tu application soporta, qué features están available en cada uno y qué polyfills o fallbacks se necesitan. Sin un matrix, teams hacen ad-hoc decisions que lead a inconsistent experiences y difficult debugging.

When to Use

  • For alternatives, see Component API Documentation Template.

  • Empezando un new web project

  • Definiendo support requirements para un client project

  • Evaluando si drop support para un older browser

  • Planificando polyfill y transpilation strategy

  • Communicando support decisions a stakeholders

Solution

# Browser Support Matrix — `<Project Name>`

## Matrix Overview

| Field | Value |
|-------|-------|
| Project | Example Web App |
| Last Updated | 2026-07-05 |
| Owner | Frontend Team |
| Support Strategy | Progressive enhancement |
| Baseline | Last 2 versions + > 0.5% global usage |
| IE11 Support | No |
| Transpilation Target | ES2020 |
| CSS Transpilation | PostCSS + Autoprefixer |

## 1. Supported Browsers

### Tier 1 — Full Support

| Browser | Minimum Version | Global Usage | Testing | Notes |
|---------|----------------|-------------|---------|-------|
| Chrome | 110+ | 65.2% | Automated + manual | Evergreen, auto-updates |
| Edge | 110+ | 5.2% | Automated | Chromium-based, same que Chrome |
| Firefox | 115+ | 3.1% | Automated + manual | ESR + stable |
| Safari | 16+ | 4.3% | Manual + BrowserStack | macOS + iOS |

### Tier 2 — Best Effort Support

| Browser | Minimum Version | Global Usage | Testing | Notes |
|---------|----------------|-------------|---------|-------|
| Samsung Internet | 20+ | 2.6% | Manual | Android, Chromium-based |
| Opera | 95+ | 2.1% | Not tested | Chromium-based, assumed same que Chrome |
| Chrome (Android) | 110+ | 65.2% (mobile) | Manual | Mobile Chrome |

### Tier 3 — Not Supported

| Browser | Version | Global Usage | Notes |
|---------|---------|-------------|-------|
| Internet Explorer | All | 0.3% | EOL June 2022, no soportado |
| Safari < 16 | < 16 | 0.8% | Mostrá upgrade banner |
| Chrome < 110 | < 110 | 0.4% | Mostrá upgrade banner |
| Firefox < 115 | < 115 | 0.2% | Mostrá upgrade banner |

## 2. Feature Compatibility Matrix

### JavaScript Features

| Feature | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | Polyfill Needed | 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+ | Yes | Bundler handles |
| Web Components (custom elements, shadow DOM) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| ResizeObserver | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| IntersectionObserver | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Workers | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Service Workers | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| WebAssembly | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| WebGPU | ⚠️ 113+ | ⚠️ 113+ | ❌ | ❌ | ⚠️ 113+ | Yes | WebGL fallback |

### CSS Features

| Feature | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | Polyfill Needed | Fallback |
|---------|------------|-----------|-------------|-----------|------------|----------------|----------|
| CSS Grid | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Flexbox | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Custom Properties (CSS variables) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Container Queries | ✅ | ✅ | ✅ | ✅ | ✅ | No | Media query fallback |
| :has() selector | ✅ 105+ | ✅ 105+ | ✅ 121+ | ✅ 15.4+ | ✅ | No | Class-based fallback |
| Cascade Layers (@layer) | ✅ | ✅ | ✅ | ✅ | ✅ | No | Specificity fallback |
| Color-mix() | ✅ 111+ | ✅ 111+ | ✅ 113+ | ✅ 16.2+ | ✅ | No | Pre-computed values |
| Nesting | ✅ 112+ | ✅ 112+ | ✅ 117+ | ✅ 16.5+ | ✅ | No | PostCSS nesting |
| Subgrid | ✅ 117+ | ✅ 117+ | ✅ 71+ | ✅ 16+ | ✅ | No | Regular grid fallback |
| View Transitions API | ✅ 111+ | ✅ 111+ | ❌ | ❌ | ✅ | Yes | No transition |
| Scroll-driven animations | ✅ 115+ | ✅ 115+ | ❌ | ❌ | ✅ | Yes | JS-based scroll |

### Web APIs

| Feature | Chrome 110+ | Edge 110+ | Firefox 115+ | Safari 16+ | Samsung 20+ | Polyfill Needed | Fallback |
|---------|------------|-----------|-------------|-----------|------------|----------------|----------|
| Fetch API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Crypto API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Storage (localStorage, sessionStorage) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| IndexedDB | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Cache API | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Push API | ✅ | ✅ | ✅ | ⚠️ 16.4+ | ✅ | No | Polling fallback |
| Web Notifications | ✅ | ✅ | ✅ | ⚠️ 16.4+ | ✅ | No | In-app notifications |
| Geolocation | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Web Share API | ✅ | ✅ | ❌ | ✅ | ✅ | Yes | Custom share dialog |
| File System Access API | ✅ | ✅ | ❌ | ❌ | ✅ | Yes | Download/upload fallback |
| Web Authentication (WebAuthn) | ✅ | ✅ | ✅ | ✅ | ✅ | No | — |
| Payment Request API | ✅ | ✅ | ❌ | ✅ | ✅ | Yes | Traditional checkout |

## 3. Polyfill Strategy

### Required Polyfills

| Polyfill | Target Browsers | Bundle Size (gzipped) | Load Strategy | Reason |
|----------|----------------|----------------------|---------------|--------|
| core-js (ES features) | All | 12 KB | Build-time | ES feature transpilation |
| regenerator-runtime | All | 3 KB | Build-time | Async/await transpilation |
| web-share-polyfill | Firefox | 2 KB | Dynamic import | Web Share API |
| webgpu-polyfill | Firefox, Safari | 8 KB | Dynamic import | WebGPU fallback a WebGL |

### Polyfill Loading Configuration

```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,
    }],
  ],
};
// PostCSS configuration
module.exports = {
  plugins: [
    require('autoprefixer')({
      overrideBrowserslist: [
        'Chrome >= 110',
        'Edge >= 110',
        'Firefox >= 115',
        'Safari >= 16',
        'Samsung >= 20',
      ],
    }),
    require('postcss-nesting'),
  ],
};

Conditional Polyfill Loading

// Dynamic polyfill loading 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);
  }
}

// Feature detection 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. Fallback Strategies

FeatureFallback StrategyUser ImpactImplementation Effort
View TransitionsNo transition, instant page swapNo animationLow — justo no uses
Scroll-driven animationsJS IntersectionObserver-basedSlightly different timingMedium
Web Share APICustom share dialog con copy linkDifferent UI, same functionMedium
File System AccessDownload + upload flowDifferent workflowMedium
WebGPUWebGL rendererLower performance para 3DHigh
Payment Request APITraditional checkout formMore steps, same functionLow
Push API (Safari < 16.4)Polling for updatesBattery impact, less real-timeMedium

5. Testing Matrix

BrowserVersionOSTesting MethodFrequencyCoverage
ChromeLatestWindowsPlaywright CIEvery PRE2E + visual
ChromeLatestmacOSPlaywright CIEvery PRE2E + visual
ChromeLatestAndroidBrowserStackWeeklySmoke test
EdgeLatestWindowsPlaywright CIEvery PRE2E
FirefoxLatestWindowsPlaywright CIEvery PRE2E
FirefoxLatestmacOSManualBi-weeklySmoke test
FirefoxESRWindowsManualMonthlySmoke test
SafariLatestmacOSBrowserStackWeeklyE2E + visual
SafariLatestiOSBrowserStackWeeklySmoke test
Safari16.0iOSBrowserStackMonthlySmoke test
Samsung InternetLatestAndroidBrowserStackMonthlySmoke test

6. Build Configuration

Browserslist Configuration

# .browserslistrc
[production]
Chrome >= 110
Edge >= 110
Firefox >= 115
Safari >= 16
Samsung >= 20

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

Vite Configuration

// 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. Upgrade Banner

Para browsers below el minimum supported version, mostrá un upgrade banner:

<!-- Shown solo para unsupported browsers -->
<noscript>
  <div class="upgrade-banner">
    <p>Please update your browser to use this application.</p>
  </div>
</noscript>

<script>
  function isSupported() {
    const ua = navigator.userAgent;
    const versions = {
      chrome: /Chrome\/(\d+)/,
      firefox: /Firefox\/(\d+)/,
      safari: /Version\/(\d+).*Safari/,
      edge: /Edg\/(\d+)/,
    };

    const minVersions = { chrome: 110, firefox: 115, safari: 16, edge: 110 };

    for (const [browser, regex] of Object.entries(versions)) {
      const match = ua.match(regex);
      if (match && parseInt(match[1]) < minVersions[browser]) {
        return false;
      }
    }
    return true;
  }

  if (!isSupported()) {
    document.getElementById('upgrade-banner').style.display = 'block';
  }
</script>

## Explanation

Un browser support matrix sirve a three purposes: setea expectations para stakeholders, guide development decisions y define el testing strategy. El tiered approach (full support, best effort, not supported) acknowledge que no todos los browsers merecen el same investment.

Tier 1 browsers get full testing: automated E2E, visual regression y manual testing. Bugs en estos browsers blockean release. Tier 2 browsers get best-effort testing: son Chromium-based y assumed que funcionan como Chrome, pero get periodic smoke tests. Tier 3 browsers get un upgrade banner.

El feature compatibility matrix mapea cada feature a browser support. Features con gaps get polyfills o fallbacks. La key decision es: polyfill (add bundle size) o fallback (degraded experience). Polyfills son appropriate para small, critical features. Fallbacks son appropriate para large, non-critical features.

La polyfill strategy section documentea qué polyfills se necesitan, su size y cómo se load. Build-time polyfills (via Babel) se incluyen en el main bundle. Dynamic polyfills se load on demand via `import()`, reduciendo initial bundle size para users que no los necesitan.

El testing matrix define qué browsers se testean, cómo y qué tan seguido. CI testing coverea los most important browsers en every PR. BrowserStack coverea Safari y mobile browsers weekly. Esto catchea regressions antes de que users los reporten.

## Variants

| Context | Approach | Notes |
|---------|----------|-------|
| Enterprise app | Soportá older browsers (IE11 si required) | Puede necesitar heavier polyfills |
| Consumer app | Evergreen only, latest 2 versions | Minimal polyfills |
| Mobile web app | Focus en mobile browsers | Safari iOS, Chrome Android, Samsung |
| Government site | Strict accessibility + legacy support | Section 508 puede require older browser support |
| Internal tool | Chrome only | Minimal cross-browser testing |
| Global audience | Incluí regional browsers (UC, Opera Mini) | Checkeá regional usage stats |

## What Works

1. Usá Browserslist como el single source of truth — feedea Babel, Autoprefixer y ESLint
2. Testeá en real browsers — emulators y simulators missean rendering differences
3. Feature-detect, no browser-detect — `if ('share' in navigator)` no `if (isSafari)`
4. Loadé polyfills dynamically — no penalice modern browsers con polyfill bytes
5. Revieweá el matrix quarterly — browser usage shifts, new versions drop
6. Trackeá browser usage analytics — drop support cuando usage cae below threshold
7. Mostrá upgrade banners para unsupported browsers — no silently breakees

## Common Mistakes

1. Soportar too many browsers — cada browser addea testing y polyfill cost
2. No testear Safari — Safari tiene unique CSS y JS quirks a pesar de ser WebKit
3. Browser detection en vez de feature detection — fragile y breakea en new versions
4. No fallback strategy — features que silently failen son worse que degraded experiences
5. Ignorar mobile browsers — mobile usage often excede desktop
6. No analytics para inform decisions — soportar browsers que nadie usa wastean effort
7. No updatear el matrix — browser versions mueven fast, stale matrices dan false confidence

## Frequently Asked Questions

### ¿Cómo decidimos qué browsers soportar?

Empezá con analytics data desde tu existing site. Identificá browsers con > 0.5% usage. Checkeá caniuse.com para feature compatibility. Considerá tu audience: enterprise users pueden estar locked a older browsers. Seteá el baseline en los last 2 versions de cada major browser plus cualquier browser above tu usage threshold.

### ¿Deberíamos soportar Internet Explorer?

No. IE11 reached end of life en June 2022. Microsoft activamente redirects IE11 users a Edge. Soportar IE11 require heavy polyfills, limite tu CSS y JS feature set y increase testing burden. Mostrá un upgrade banner en vez.

### ¿Cómo handleamos Safari's slower feature adoption?

Safari adopt web features slower que Chrome y Firefox. Testeá en Safari early y often. Usá caniuse.com para checkear Safari support antes de usar un feature. Para features que Safari les falta, implementá un fallback o loadé un polyfill dinámicamente. No asumas "funciona en Chrome así que funciona everywhere."

### ¿Cuál es el cost de soportar un additional browser?

Cada additional browser addea: testing time (manual o automated), potential polyfill bundle size, debugging time para browser-specific bugs y CSS compatibility work. Estimá 5-10% de frontend development time per additional browser tier. Weigheá esto contra el user percentage que ese browser representa.

### ¿Qué tan seguido deberíamos reviewear el matrix?

Quarterly at minimum. Browser versions update every 4-6 weeks. Usage data shifts a medida que users upgrade. Revieweá el matrix, checkeá analytics para usage changes, verify caniuse data para new features y updateá el Browserslist configuration. Droppeá browsers que caen below el usage threshold.