Browser Compatibility Modern Implementation

1. Babel Polyfill Core-js ES6+ Support

Tool/Feature Purpose Configuration Best Practices
Babel Transpile modern JS to ES5. Transform JSX, TypeScript. Plugin ecosystem. Source maps .babelrc or babel.config.js. Presets: @babel/preset-env, -react, -typescript Use preset-env with browserslist. Enable modules: false for tree-shaking. Source maps in dev
@babel/preset-env Smart transpilation based on target browsers. Only polyfill what's needed. Auto browser detection targets: { browsers: ['>0.5%', 'not dead'] }. useBuiltIns: 'usage' or 'entry' Use useBuiltIns: 'usage' for optimal size. Set browserslist in package.json. Update targets yearly
core-js Polyfill library. ES6+ features, Promise, Symbol, Array methods, Object methods, 90KB minified npm install core-js@3. Import: import 'core-js/stable' or auto with useBuiltIns Use core-js@3. Automatic imports with babel useBuiltIns: 'usage'. Only polyfill used features
Polyfill Strategies Entry point, Usage-based, Feature detection, Polyfill.io service, Conditional loading Usage: smallest bundle. Entry: explicit control. Polyfill.io: CDN-based, dynamic Usage-based for apps. Entry for libraries. Polyfill.io for multi-browser. Test without polyfills
Modern Features Support async/await, Promises, Class properties, Optional chaining, Nullish coalescing, BigInt Babel transforms syntax. core-js polyfills APIs. regenerator-runtime for async/await Check caniuse.com. Feature detection before use. Graceful degradation. Progressive enhancement
Bundle Size Impact Polyfills add 20-50KB. Target modern browsers. Use differential loading. Monitor size webpack-bundle-analyzer. Monitor polyfill size. Exclude from modern bundle Target last 2 versions. 20KB polyfills for legacy. 0KB for modern. Use type="module"

Example: Babel configuration with optimal polyfilling

// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        // Target browsers from browserslist
        targets: {
          browsers: [
            '>0.5%',
            'not dead',
            'not op_mini all',
            'not IE 11'
          ]
        },
        
        // Automatic polyfill injection based on usage
        useBuiltIns: 'usage',
        
        // Use core-js version 3
        corejs: { version: 3, proposals: true },
        
        // Preserve ES modules for tree-shaking
        modules: false,
        
        // Include regenerator for async/await
        exclude: ['transform-regenerator'],
      }
    ],
    '@babel/preset-react',
    '@babel/preset-typescript'
  ],
  
  plugins: [
    // Optional chaining and nullish coalescing (if not in preset-env)
    '@babel/plugin-proposal-optional-chaining',
    '@babel/plugin-proposal-nullish-coalescing-operator',
    
    // Class properties
    ['@babel/plugin-proposal-class-properties', { loose: true }],
    
    // Runtime helpers (reduce duplication)
    ['@babel/plugin-transform-runtime', {
      corejs: false, // Don't polyfill (handled by useBuiltIns)
      helpers: true, // Extract helpers
      regenerator: true,
      useESModules: true
    }]
  ],
  
  env: {
    production: {
      plugins: [
        // Remove console.log in production
        'transform-remove-console',
        // Dead code elimination
        'transform-remove-undefined'
      ]
    }
  }
};

// package.json - browserslist
{
  "browserslist": {
    "production": [
      ">0.5%",
      "not dead",
      "not op_mini all",
      "not IE 11"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ],
    "legacy": [
      "IE 11"
    ]
  }
}

Example: Feature detection and polyfill loading

// polyfills.ts - Conditional polyfill loading
export async function loadPolyfills() {
  const polyfills: Promise<void>[] = [];

  // Check for Promise support
  if (!window.Promise) {
    polyfills.push(import('core-js/features/promise'));
  }

  // Check for fetch support
  if (!window.fetch) {
    polyfills.push(import('whatwg-fetch'));
  }

  // Check for IntersectionObserver
  if (!('IntersectionObserver' in window)) {
    polyfills.push(import('intersection-observer'));
  }

  // Check for ResizeObserver
  if (!('ResizeObserver' in window)) {
    polyfills.push(import('@juggle/resize-observer').then(m => {
      window.ResizeObserver = m.ResizeObserver;
    }));
  }

  // Wait for all polyfills to load
  await Promise.all(polyfills);
}

// main.tsx - Load polyfills before app
import { loadPolyfills } from './polyfills';

loadPolyfills().then(() => {
  // Import and render app after polyfills loaded
  import('./App').then(({ App }) => {
    const root = document.getElementById('root');
    ReactDOM.createRoot(root!).render(<App />);
  });
});

// Alternative: Polyfill.io service (CDN-based)
// index.html
<script src="https://polyfill.io/v3/polyfill.min.js?features=default,fetch,IntersectionObserver,ResizeObserver"></script>

2. PostCSS Autoprefixer Vendor Prefixes

Tool Purpose Configuration Best Practices
PostCSS CSS transformation pipeline. Plugin architecture. Autoprefixer, CSS modules, nesting postcss.config.js. Plugins array. Integrate with webpack/Vite/Next Use with bundler. Enable source maps. Chain plugins efficiently. Cache in production
Autoprefixer Auto vendor prefixes (-webkit, -moz, -ms). Based on browserslist. Remove outdated prefixes autoprefixer({ grid: 'autoplace' }). Uses browserslist config Always use with browserslist. Enable CSS Grid support. Update regularly. No manual prefixes
CSS Modern Features Grid, Flexbox, Custom Properties, calc(), clamp(), aspect-ratio, container queries Autoprefixer handles most. Polyfills for custom properties (IE11). Progressive enhancement Write modern CSS. Autoprefixer adds prefixes. Test in target browsers. Use @supports
postcss-preset-env Use future CSS today. Stage 3 features. Nesting, custom media queries, color functions postcss-preset-env({ stage: 3, features: { 'nesting-rules': true } }) Stage 3 is stable. Enable specific features. More than autoprefixer. Use with caution
CSS Modules Scoped CSS, Local class names, Composition, No global conflicts, Type-safe with TS postcss-modules. Import: import styles from './App.module.css' Use for component styles. Global for resets. Compose common styles. Generate types
PostCSS Plugins cssnano (minify), postcss-nesting, postcss-custom-properties, postcss-import Chain plugins. Order matters. cssnano last. Import first. Optimize for prod Minimal plugins. cssnano in prod. Import for @import. Nesting for cleaner CSS

Example: Complete PostCSS configuration

// postcss.config.js
module.exports = {
  plugins: [
    // Import support
    require('postcss-import')({
      path: ['src/styles']
    }),
    
    // Nesting (like Sass)
    require('postcss-nesting'),
    
    // Modern CSS features with autoprefixer included
    require('postcss-preset-env')({
      stage: 3,
      features: {
        'nesting-rules': true,
        'custom-media-queries': true,
        'custom-properties': false, // Use native CSS variables
      },
      autoprefixer: {
        grid: 'autoplace',
        flexbox: 'no-2009'
      }
    }),
    
    // Or standalone autoprefixer
    // require('autoprefixer'),
    
    // Minification in production
    process.env.NODE_ENV === 'production' ? require('cssnano')({
      preset: ['default', {
        discardComments: { removeAll: true },
        normalizeWhitespace: true,
        reduceIdents: false, // Keep animation names
        zindex: false // Don't optimize z-index
      }]
    }) : false
  ].filter(Boolean)
};

// Example CSS that will be prefixed
/* Input CSS */
.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
  
  & .item {
    display: flex;
    align-items: center;
    
    &:hover {
      transform: scale(1.05);
    }
  }
}

.modern {
  aspect-ratio: 16 / 9;
  container-type: inline-size;
  
  @supports (aspect-ratio: 1) {
    /* Modern browsers */
  }
}

/* Output CSS (with autoprefixer) */
.container {
  display: -ms-grid;
  display: grid;
  -ms-grid-columns: (minmax(250px, 1fr))[auto-fit];
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
}

.container .item {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
}

.container .item:hover {
  -webkit-transform: scale(1.05);
  -ms-transform: scale(1.05);
  transform: scale(1.05);
}

3. Can I Use Browser Feature Detection

Tool/Approach Purpose Implementation Best Practices
caniuse.com Browser support database. Check feature support. Usage statistics. Known issues Search feature. Check target browsers. Review known issues. Export to browserslist Check before using new features. 95%+ support is safe. Check mobile support. Review notes
@supports CSS Feature queries in CSS. Progressive enhancement. Fallbacks. Test property support @supports (display: grid) { }. Test property:value pairs. Combine with and/or Provide fallbacks first. Enhance with @supports. Test one property. Use for critical features
JavaScript Feature Detection Check API availability. if ('feature' in object). Modernizr library. Dynamic polyfilling if ('IntersectionObserver' in window) { }. Check before use. Load polyfill if missing Always check APIs. Provide fallbacks. Load polyfills conditionally. Test in target browsers
Modernizr Feature detection library. 150+ tests. Custom builds. HTML classes. JS API Custom build at modernizr.com. Adds classes to <html>. JS: Modernizr.feature Custom build only (5-10KB). HTML classes for CSS. JS API for conditionals. Complement polyfills
browserslist Define target browsers. Shared config. Used by Babel, Autoprefixer, ESLint .browserslistrc or package.json. Queries: '>0.5%', 'last 2 versions', 'not dead' Single source of truth. Update yearly. Separate prod/dev. Use caniuse-lite data
User Agent Detection Last resort. Browser/version detection. Mobile vs desktop. Known browser bugs navigator.userAgent. Libraries: ua-parser-js, bowser. Server-side better Avoid if possible. Feature detection preferred. Use for specific bug workarounds. Server-side

Example: Feature detection patterns

// CSS @supports for progressive enhancement
/* Fallback for older browsers */
.grid-container {
  display: flex;
  flex-wrap: wrap;
}

/* Enhanced for browsers with Grid support */
@supports (display: grid) {
  .grid-container {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 2rem;
  }
}

/* Container queries with fallback */
.card {
  padding: 1rem;
}

@supports (container-type: inline-size) {
  .container {
    container-type: inline-size;
  }
  
  @container (min-width: 400px) {
    .card {
      padding: 2rem;
    }
  }
}

// JavaScript feature detection
class FeatureDetector {
  static hasIntersectionObserver(): boolean {
    return 'IntersectionObserver' in window;
  }

  static hasServiceWorker(): boolean {
    return 'serviceWorker' in navigator;
  }

  static hasWebGL(): boolean {
    try {
      const canvas = document.createElement('canvas');
      return !!(
        canvas.getContext('webgl') || 
        canvas.getContext('experimental-webgl')
      );
    } catch {
      return false;
    }
  }

  static hasLocalStorage(): boolean {
    try {
      localStorage.setItem('test', 'test');
      localStorage.removeItem('test');
      return true;
    } catch {
      return false;
    }
  }

  static supportsWebP(): Promise<boolean> {
    return new Promise((resolve) => {
      const img = new Image();
      img.onload = () => resolve(img.width === 1);
      img.onerror = () => resolve(false);
      img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA=';
    });
  }

  static getCSSSupport(): Record<string, boolean> {
    return {
      grid: CSS.supports('display', 'grid'),
      flexbox: CSS.supports('display', 'flex'),
      customProperties: CSS.supports('--custom', 'value'),
      aspectRatio: CSS.supports('aspect-ratio', '16/9'),
      containerQueries: CSS.supports('container-type', 'inline-size'),
      backdrop: CSS.supports('backdrop-filter', 'blur(10px)'),
    };
  }
}

// Usage in React
export function useFeatureDetection() {
  const [features, setFeatures] = useState({
    intersectionObserver: false,
    serviceWorker: false,
    webp: false,
    webgl: false,
    cssGrid: false,
  });

  useEffect(() => {
    const detect = async () => {
      setFeatures({
        intersectionObserver: FeatureDetector.hasIntersectionObserver(),
        serviceWorker: FeatureDetector.hasServiceWorker(),
        webp: await FeatureDetector.supportsWebP(),
        webgl: FeatureDetector.hasWebGL(),
        cssGrid: CSS.supports('display', 'grid'),
      });
    };
    detect();
  }, []);

  return features;
}

// Conditional rendering based on features
function ImageGallery() {
  const { webp } = useFeatureDetection();
  const imageExt = webp ? 'webp' : 'jpg';
  
  return <img src={`/images/photo.${imageExt}`} alt="Photo" />;
}

4. Progressive Enhancement Feature Support

Strategy Description Implementation Benefits
Progressive Enhancement Start with basic HTML/CSS. Layer JavaScript. Feature detection. Graceful degradation Core content in HTML. CSS for presentation. JS for enhancement. Works without JS Accessible by default. SEO-friendly. Resilient. Fast baseline. Works everywhere
Content First HTML semantic markup. No JS required for content. Forms work without JS. Links navigate Use <form action="/submit">. <a href="/page">. Server-side rendering. Semantic HTML5 Accessibility. SEO. No-JS users. Slow connections. Screen readers. Search crawlers
CSS Enhancement Basic layout without modern features. @supports for enhancement. Fallback styles first Flexbox fallback for Grid. Basic colors before gradients. Simple animations Works on all browsers. Better with modern features. Graceful degradation. No breaking
JavaScript Enhancement Check feature before use. Load polyfills conditionally. Provide no-JS alternative <noscript> fallback. Progressive loading. Feature detection. Error boundaries Works without JS. Better with JS. Faster for modern browsers. Resilient to failures
Mobile First Design for mobile baseline. Enhance for desktop. Touch-first interactions. Responsive Min-width media queries. Mobile layout default. Touch events. Responsive images Better mobile UX. Faster on mobile. Progressive enhancement. Desktop gets extras
Network Resilience Work offline. Queue requests. Cache content. Show stale data. Background sync Service workers. IndexedDB. Offline indicators. Retry logic. Optimistic UI Works on slow/offline. Better UX. Resilient. Mobile-friendly. Emerging markets

Example: Progressive enhancement patterns

// HTML - Works without JavaScript
<!-- Form with server-side fallback -->
<form action="/api/submit" method="POST" class="contact-form">
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" required>
  
  <label for="message">Message:</label>
  <textarea id="message" name="message" required></textarea>
  
  <button type="submit">Send Message</button>
</form>

<!-- No-JS fallback -->
<noscript>
  <p>This site works best with JavaScript enabled, but core functionality is available without it.</p>
</noscript>

// CSS - Progressive enhancement with @supports
/* Basic layout (works everywhere) */
.gallery {
  margin: 0 auto;
  max-width: 1200px;
}

.gallery-item {
  margin-bottom: 20px;
}

/* Enhanced with Flexbox */
@supports (display: flex) {
  .gallery {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
  }
  
  .gallery-item {
    flex: 0 0 calc(33.333% - 20px);
    margin-bottom: 0;
  }
}

/* Further enhanced with Grid */
@supports (display: grid) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
  }
  
  .gallery-item {
    flex: unset;
  }
}

// JavaScript - Progressive enhancement
class ContactForm {
  constructor(formElement: HTMLFormElement) {
    this.form = formElement;
    
    // Only enhance if fetch is available
    if ('fetch' in window) {
      this.enhanceForm();
    }
    // Otherwise, form submits normally to server
  }

  private enhanceForm() {
    this.form.addEventListener('submit', async (e) => {
      e.preventDefault(); // Only prevent if JS works
      
      const formData = new FormData(this.form);
      
      try {
        const response = await fetch(this.form.action, {
          method: 'POST',
          body: formData,
        });

        if (response.ok) {
          this.showSuccess();
        } else {
          // Fallback to normal form submit on error
          this.form.submit();
        }
      } catch (error) {
        // Network error - fallback to normal submit
        this.form.submit();
      }
    });
  }

  private showSuccess() {
    // Enhanced UI feedback (only with JS)
    this.form.innerHTML = '<p>Thank you! Message sent.</p>;
  }
}

// Initialize only if DOM is ready and JS enabled
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initForms);
} else {
  initForms();
}

function initForms() {
  const forms = document.querySelectorAll<HTMLFormElement>('.contact-form');
  forms.forEach(form => new ContactForm(form));
}

// React - Progressive enhancement with hydration
// Server-side render first, then hydrate
import { hydrateRoot } from 'react-dom/client';

function App() {
  return (
    <form action="/api/submit" method="POST">
      {/* Server renders working form */}
      {/* Client enhances with React */}
    </form>
  );
}

// Hydrate only if browser supports modules
if ('noModule' in HTMLScriptElement.prototype) {
  const root = document.getElementById('root')!;
  hydrateRoot(root, <App />);
}
// Otherwise, server-rendered HTML works as-is

5. Cross-browser Testing BrowserStack

Tool/Service Features Use Cases Best Practices
BrowserStack NEW 3000+ browsers, Real devices, Live testing, Automated tests, Screenshots, Network throttling Manual testing, Selenium/Playwright integration, Visual regression, Mobile device testing Test critical flows. Automate common paths. Test on real devices. Check latest browsers monthly
Sauce Labs Browser cloud, Mobile testing, CI/CD integration, Parallel tests, Analytics, Video recording Automated E2E tests, Cross-browser CI, Performance testing, Mobile app testing Integrate with CI. Run tests in parallel. Video for failures. Test matrix optimization
LambdaTest Live testing, Screenshot testing, Automated testing, Local tunnel, Responsive testing Visual testing, Responsive layouts, Browser compatibility, Geolocation testing Bulk screenshots. Responsive testing. Local tunnel for dev. Compare screenshots
Playwright Chromium, Firefox, WebKit. Headless testing. Auto-wait. Network interception. Tracing Cross-browser E2E tests. Component testing. Visual regression. CI automation Test in 3 engines. Parallel execution. Trace for debugging. Screenshots on failure. Retry flaky
Testing Strategy Test pyramid. Critical flows manual. Smoke tests automated. Visual regression. Performance Login, checkout, forms. Core user journeys. Different screen sizes. Network conditions Automate repetitive. Manual for UX. Test on real devices. Check accessibility. Performance budget
Browser DevTools Chrome DevTools, Firefox DevTools, Safari Web Inspector. Device emulation. Network throttling Debug issues. Performance profiling. Mobile emulation. Console logging. Network analysis Use native DevTools first. Device emulation for quick checks. Network throttling for slow connections

Example: Playwright cross-browser tests

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: [
    ['html'],
    ['junit', { outputFile: 'results.xml' }],
    ['json', { outputFile: 'results.json' }]
  ],
  
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },

  projects: [
    // Desktop browsers
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
    
    // Mobile browsers
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 13'] },
    },
    
    // Tablet
    {
      name: 'iPad',
      use: { ...devices['iPad Pro'] },
    },
  ],

  webServer: {
    command: 'npm run dev',
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
});

// e2e/critical-flow.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Critical User Flow', () => {
  test('should complete checkout process', async ({ page }) => {
    await page.goto('/');
    
    // Add to cart
    await page.click('[data-testid="add-to-cart"]');
    await expect(page.locator('.cart-badge')).toHaveText('1');
    
    // Go to checkout
    await page.click('[data-testid="cart-icon"]');
    await page.click('[data-testid="checkout-button"]');
    
    // Fill form
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="card"]', '4242424242424242');
    
    // Submit
    await page.click('[data-testid="submit-payment"]');
    
    // Verify success
    await expect(page.locator('.success-message')).toBeVisible();
  });
  
  test('should work offline', async ({ page, context }) => {
    // Go offline
    await context.setOffline(true);
    await page.goto('/');
    
    // Should show offline indicator
    await expect(page.locator('.offline-indicator')).toBeVisible();
    
    // Content should still be accessible
    await expect(page.locator('h1')).toBeVisible();
  });
});

// BrowserStack integration
// wdio.conf.js for BrowserStack
exports.config = {
  user: process.env.BROWSERSTACK_USERNAME,
  key: process.env.BROWSERSTACK_ACCESS_KEY,
  
  capabilities: [
    {
      browserName: 'Chrome',
      browserVersion: 'latest',
      'bstack:options': {
        os: 'Windows',
        osVersion: '11',
      }
    },
    {
      browserName: 'Safari',
      browserVersion: 'latest',
      'bstack:options': {
        os: 'OS X',
        osVersion: 'Ventura',
      }
    },
    {
      browserName: 'Chrome',
      'bstack:options': {
        deviceName: 'Samsung Galaxy S23',
        realMobile: true,
      }
    }
  ],
  
  services: ['browserstack']
};

6. ES Module Legacy Bundle Differential Loading

Strategy Description Implementation Benefits
ES Modules (ESM) Native browser modules. type="module". Dynamic imports. Tree-shaking. Modern browsers <script type="module" src="app.js">. import/export. 95%+ browser support Smaller bundles. No transpilation. Faster. Native tree-shaking. Parallel loading
Legacy Bundle ES5 transpiled. Polyfills included. nomodule attribute. IE11 and old browsers <script nomodule src="app-legacy.js">. Babel transpilation. Full polyfills Support old browsers. Fallback for no-ESM. Separate bundle. Larger size acceptable
Differential Loading Serve modern bundle to modern browsers. Legacy to old browsers. Automatic selection Both module and nomodule scripts. Browser chooses. Vite/Angular CLI built-in 20-40% smaller for modern. No polyfills for 95%. Better performance. Same codebase
Module/Nomodule Pattern Modern browsers ignore nomodule. Old browsers ignore type="module". Automatic fallback Both scripts in HTML. Modern loads module. Old loads nomodule. No JS detection needed Zero config. Automatic. No feature detection. Works everywhere. Easy implementation
Build Configuration Two build outputs. Modern target ES2020. Legacy target ES5. Vite, Angular, custom webpack Vite: build.target = 'esnext'. Legacy plugin. Webpack: dual configs. Different browserslist Automate builds. Single source. Optimize each. Monitor both sizes. Update targets yearly
Bundle Size Comparison Modern: 100KB (no polyfills). Legacy: 150KB (polyfills). 33% savings for modern Analyze both bundles. Modern no core-js. Legacy full polyfills. Separate chunks Modern users get faster. Legacy users still work. Cost-effective. Better metrics

Example: Differential loading setup

// HTML - Module/nomodule pattern
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Differential Loading</title>
  
  <!-- Preload modern bundle for modern browsers -->
  <link rel="modulepreload" href="/assets/app.js">
  
  <!-- Preload legacy bundle for old browsers -->
  <link rel="preload" href="/assets/app-legacy.js" as="script" crossorigin>
</head>
<body>
  <div id="root"></div>
  
  <!-- Modern browsers: ES2020+, no polyfills, smaller -->
  <script type="module" src="/assets/app.js"></script>
  
  <!-- Legacy browsers: ES5, full polyfills, larger -->
  <script nomodule src="/assets/app-legacy.js"></script>
  
  <!-- Safari 10.1 fix (downloads both) -->
  <script>
    !function(){var e=document,t=e.createElement("script");
    if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;
    e.addEventListener("beforeload",function(e){
      if(e.target===t)n=!0;
      else if(!e.target.hasAttribute("nomodule")||!n)return;
      e.preventDefault()},!0),
    t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();
  </script>
</body>
</html>

// vite.config.ts - Differential loading with Vite
import { defineConfig } from 'vite';
import legacy from '@vitejs/plugin-legacy';

export default defineConfig({
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
      modernPolyfills: true,
      renderLegacyChunks: true,
      
      // Additional legacy targets
      additionalLegacyPolyfills: ['regenerator-runtime/runtime']
    })
  ],
  
  build: {
    // Modern target
    target: 'esnext',
    
    // Ensure code splitting
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        }
      }
    }
  }
});

// Webpack - Manual differential loading
// webpack.modern.config.js
module.exports = {
  output: {
    filename: '[name].js',
    environment: {
      // Modern browser features
      arrowFunction: true,
      const: true,
      destructuring: true,
      forOf: true,
      dynamicImport: true,
      module: true,
    }
  },
  
  target: ['web', 'es2020'],
  
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: { esmodules: true },
                bugfixes: true,
                modules: false,
              }]
            ]
          }
        }
      }
    ]
  }
};

// webpack.legacy.config.js
module.exports = {
  output: {
    filename: '[name]-legacy.js',
  },
  
  target: ['web', 'es5'],
  
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: { ie: 11 },
                useBuiltIns: 'usage',
                corejs: 3,
              }]
            ]
          }
        }
      }
    ]
  }
};

// Build script to generate both bundles
// package.json
{
  "scripts": {
    "build": "npm run build:modern && npm run build:legacy",
    "build:modern": "webpack --config webpack.modern.config.js",
    "build:legacy": "webpack --config webpack.legacy.config.js"
  }
}
Browser Compatibility Strategy:
  • Target Browsers: Last 2 versions, >0.5% usage, not dead. Update yearly. Mobile first
  • Polyfills: Use preset-env with useBuiltIns: 'usage'. Conditional loading. 20-50KB for legacy
  • CSS: Autoprefixer with browserslist. @supports for enhancement. Fallbacks first
  • Testing: Playwright for 3 engines. BrowserStack for real devices. Visual regression
  • Differential Loading: Modern ES2020 bundle. Legacy ES5 bundle. 30% size savings

Browser Compatibility Summary

  • Babel + core-js: Transpile to ES5. Polyfill APIs. useBuiltIns: 'usage'. 20KB modern, 50KB legacy
  • PostCSS + Autoprefixer: Auto vendor prefixes. Based on browserslist. Grid support. CSS nesting
  • Feature Detection: caniuse.com for checking. @supports in CSS. JavaScript detection. Modernizr
  • Progressive Enhancement: HTML first. CSS enhancement. JS layer. Works without JS. Mobile first
  • Cross-browser Testing: BrowserStack/Sauce Labs. Playwright 3 engines. Real devices. Automate CI
  • Differential Loading: type="module" for modern. nomodule for legacy. 30% smaller. Auto selection
Compatibility Checklist: Define browserslist targets, enable Babel preset-env, configure Autoprefixer, implement feature detection, use progressive enhancement, test in 3+ browsers, and ship differential bundles. Support 95%+ users efficiently.