Legacy Browser Support Strategies

1. Internet Explorer 11 Polyfill Requirements

Feature Category Missing in IE11 Polyfill Package Size Impact
ES6 Core Promise, Symbol, WeakMap, Set core-js/es ~40 KB
Array Methods find, findIndex, includes, from core-js/features/array ~5 KB
Object Methods assign, entries, values, keys core-js/features/object ~3 KB
String Methods includes, startsWith, endsWith core-js/features/string ~2 KB
Web APIs fetch, IntersectionObserver, CustomEvent whatwg-fetch, polyfill packages ~15 KB
DOM APIs classList, dataset, matches Custom polyfills ~3 KB
CSS Features CSS variables, Grid, Flexbox bugs css-vars-ponyfill, Autoprefixer Variable

Example: Complete IE11 polyfill bundle

// polyfills/ie11.js - Comprehensive IE11 support
// 1. Core ES6 features
import 'core-js/es/symbol';
import 'core-js/es/promise';
import 'core-js/es/map';
import 'core-js/es/set';
import 'core-js/es/weak-map';
import 'core-js/es/weak-set';

// 2. Array methods
import 'core-js/es/array/find';
import 'core-js/es/array/find-index';
import 'core-js/es/array/includes';
import 'core-js/es/array/from';
import 'core-js/es/array/of';
import 'core-js/es/array/fill';
import 'core-js/es/array/iterator';

// 3. Object methods
import 'core-js/es/object/assign';
import 'core-js/es/object/keys';
import 'core-js/es/object/values';
import 'core-js/es/object/entries';
import 'core-js/es/object/get-own-property-descriptors';

// 4. String methods
import 'core-js/es/string/includes';
import 'core-js/es/string/starts-with';
import 'core-js/es/string/ends-with';
import 'core-js/es/string/repeat';
import 'core-js/es/string/pad-start';
import 'core-js/es/string/pad-end';

// 5. Number methods
import 'core-js/es/number/is-nan';
import 'core-js/es/number/is-finite';
import 'core-js/es/number/is-integer';

// 6. Web APIs
import 'whatwg-fetch';
import 'abortcontroller-polyfill';
import 'url-search-params-polyfill';

// 7. DOM APIs
// CustomEvent
if (typeof window.CustomEvent !== 'function') {
    window.CustomEvent = function(event, params) {
        params = params || { bubbles: false, cancelable: false, detail: null };
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
        return evt;
    };
}

// Element.matches
if (!Element.prototype.matches) {
    Element.prototype.matches = 
        Element.prototype.msMatchesSelector ||
        Element.prototype.webkitMatchesSelector;
}

// Element.closest
if (!Element.prototype.closest) {
    Element.prototype.closest = function(selector) {
        var el = this;
        while (el) {
            if (el.matches(selector)) {
                return el;
            }
            el = el.parentElement;
        }
        return null;
    };
}

// classList (IE11 has partial support)
if (!('classList' in document.documentElement) || 
    !document.documentElement.classList ||
    !document.documentElement.classList.toggle('test', false)) {
    // Full classList polyfill for IE9-10
    // (IE11 has it but toggle() is buggy)
}

// NodeList.forEach
if (window.NodeList && !NodeList.prototype.forEach) {
    NodeList.prototype.forEach = Array.prototype.forEach;
}

// 8. requestAnimationFrame
(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||
                                      window[vendors[x] + 'CancelRequestAnimationFrame'];
    }
    
    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() {
                callback(currTime + timeToCall);
            }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
    }
    
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }
})();

// 9. Console polyfill (IE9-10)
if (!window.console) {
    window.console = {
        log: function() {},
        warn: function() {},
        error: function() {},
        info: function() {},
        debug: function() {}
    };
}

// 10. Object.setPrototypeOf
if (!Object.setPrototypeOf) {
    Object.setPrototypeOf = function(obj, proto) {
        obj.__proto__ = proto;
        return obj;
    };
}

console.log('IE11 polyfills loaded');

Example: IE11-specific Babel configuration

// babel.config.js for IE11
module.exports = {
    presets: [
        ['@babel/preset-env', {
            targets: {
                ie: '11'
            },
            useBuiltIns: 'usage',
            corejs: {
                version: 3,
                proposals: false
            },
            // Important: Don't use loose mode for IE11
            loose: false,
            
            // Force all transforms
            forceAllTransforms: false,
            
            // Exclude modern features IE11 will never support
            exclude: [
                'transform-async-to-generator',
                'transform-regenerator'
            ]
        }]
    ],
    
    plugins: [
        // Transform async/await to Promise chains
        ['@babel/plugin-transform-runtime', {
            corejs: 3,
            helpers: true,
            regenerator: true
        }],
        
        // Transform for..of loops
        '@babel/plugin-transform-for-of'
    ]
};

// webpack.config.js for IE11
module.exports = {
    target: ['web', 'es5'],
    
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules\/(?!(problematic-package)\/).*/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true
                    }
                }
            }
        ]
    },
    
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                terserOptions: {
                    ecma: 5,
                    ie8: false,
                    safari10: false
                }
            })
        ]
    }
};
Warning: IE11 requires ~80-100 KB of polyfills for full ES6 support. Consider dropping IE11 support or providing a minimal experience.

2. Safari and WebKit Specific Polyfills

Safari Version Missing Features Polyfill Notes
Safari 10-11 IntersectionObserver, ResizeObserver intersection-observer, resize-observer-polyfill Common mobile Safari
Safari 12 Intl.RelativeTimeFormat, BigInt @formatjs/intl-relativetimeformat Partial Intl support
Safari 13 Promise.allSettled, String.matchAll core-js/features Some ES2020 features
Safari 14 Logical assignment operators Babel transform ??=, ||=, &&=
iOS Safari Date parsing inconsistencies Custom date parsing YYYY-MM-DD format

Example: Safari-specific polyfills

// polyfills/safari.js
// 1. Feature detection for Safari
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var safariVersion = (function() {
    var match = navigator.userAgent.match(/Version\/(\d+)/);
    return match ? parseInt(match[1]) : null;
})();

console.log('Safari detected:', isSafari, 'Version:', safariVersion);

// 2. IntersectionObserver (Safari < 12.1)
if (!('IntersectionObserver' in window)) {
    import('intersection-observer').then(() => {
        console.log('IntersectionObserver polyfilled');
    });
}

// 3. ResizeObserver (Safari < 13.1)
if (!('ResizeObserver' in window)) {
    import('resize-observer-polyfill').then((module) => {
        window.ResizeObserver = module.default;
        console.log('ResizeObserver polyfilled');
    });
}

// 4. Promise.allSettled (Safari < 13)
if (!Promise.allSettled) {
    Promise.allSettled = function(promises) {
        return Promise.all(
            promises.map(function(promise) {
                return Promise.resolve(promise).then(
                    function(value) {
                        return { status: 'fulfilled', value: value };
                    },
                    function(reason) {
                        return { status: 'rejected', reason: reason };
                    }
                );
            })
        );
    };
}

// 5. Safari Date parsing fix
var OriginalDate = Date;
var SafeDate = function() {
    if (arguments.length === 1 && typeof arguments[0] === 'string') {
        var dateString = arguments[0];
        
        // Fix YYYY-MM-DD format (Safari treats as UTC, others as local)
        if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
            dateString = dateString + 'T00:00:00';
        }
        
        return new OriginalDate(dateString);
    }
    
    return new (Function.prototype.bind.apply(
        OriginalDate,
        [null].concat(Array.prototype.slice.call(arguments))
    ));
};

SafeDate.prototype = OriginalDate.prototype;
SafeDate.now = OriginalDate.now;
SafeDate.UTC = OriginalDate.UTC;
SafeDate.parse = OriginalDate.parse;

if (isSafari) {
    window.Date = SafeDate;
}

// 6. Intl.RelativeTimeFormat (Safari < 14)
if (!Intl.RelativeTimeFormat) {
    import('@formatjs/intl-relativetimeformat').then(() => {
        import('@formatjs/intl-relativetimeformat/locale-data/en').then(() => {
            console.log('Intl.RelativeTimeFormat polyfilled');
        });
    });
}

// 7. scrollIntoView smooth behavior (Safari < 15.4)
if (!('scrollBehavior' in document.documentElement.style)) {
    import('smoothscroll-polyfill').then((smoothscroll) => {
        smoothscroll.polyfill();
        console.log('Smooth scroll polyfilled');
    });
}

// 8. Web Animations API (Safari < 13.1)
if (!Element.prototype.animate) {
    import('web-animations-js').then(() => {
        console.log('Web Animations API polyfilled');
    });
}

// 9. Safari-specific CSS fixes
if (isSafari) {
    // Add Safari-specific class for CSS targeting
    document.documentElement.classList.add('safari');
    if (safariVersion) {
        document.documentElement.classList.add('safari-' + safariVersion);
    }
}
Note: Safari often lags 1-2 years behind Chrome in feature support. Focus on polyfilling Observers and modern Promise methods for iOS Safari.

3. Android WebView and Mobile Browser Support

Platform Common Issues Polyfill Strategy Considerations
Android 4.4 WebView ES6 features, Promise, fetch Full core-js + fetch Still ~10% of Android users
Chrome Android Usually up-to-date Minimal polyfills Auto-updates enabled
Samsung Internet Lags behind Chrome Target Chrome - 2 versions Popular in Asia
UC Browser Various compatibility issues Conservative polyfills Popular in China/India
Mobile Safari iOS Same as Safari desktop Safari polyfills Tied to iOS version

Example: Mobile browser detection and polyfills

// mobile-polyfills.js
var MobilePolyfills = {
    // Detect mobile browser
    detect: function() {
        var ua = navigator.userAgent;
        
        return {
            isAndroid: /Android/i.test(ua),
            isIOS: /iPhone|iPad|iPod/i.test(ua),
            isChromeAndroid: /Chrome/i.test(ua) && /Android/i.test(ua),
            isSamsungInternet: /SamsungBrowser/i.test(ua),
            isUCBrowser: /UCBrowser/i.test(ua),
            isMobileSafari: /Safari/i.test(ua) && /iPhone|iPad|iPod/i.test(ua),
            
            // Get Android version
            androidVersion: (function() {
                var match = ua.match(/Android\s+([\d.]+)/);
                return match ? parseFloat(match[1]) : null;
            })(),
            
            // Get iOS version
            iosVersion: (function() {
                var match = ua.match(/OS\s+([\d_]+)/);
                if (match) {
                    return parseFloat(match[1].replace(/_/g, '.'));
                }
                return null;
            })(),
            
            // Check if WebView
            isWebView: (function() {
                return /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(ua) ||
                       /wv/.test(ua);
            })()
        };
    },
    
    // Load appropriate polyfills
    load: function() {
        var device = this.detect();
        var polyfills = [];
        
        console.log('Mobile device detected:', device);
        
        // Old Android (4.4 and below)
        if (device.isAndroid && device.androidVersion && device.androidVersion < 5) {
            console.log('Old Android detected, loading full polyfills');
            polyfills.push(
                import('core-js/stable'),
                import('regenerator-runtime/runtime'),
                import('whatwg-fetch')
            );
        }
        
        // Android 5-7 (missing some ES6+ features)
        if (device.isAndroid && device.androidVersion && 
            device.androidVersion >= 5 && device.androidVersion < 8) {
            polyfills.push(
                import('core-js/features/promise'),
                import('core-js/features/array/includes'),
                import('core-js/features/object/assign')
            );
        }
        
        // Samsung Internet
        if (device.isSamsungInternet) {
            console.log('Samsung Internet detected');
            // Samsung Internet lags ~6 months behind Chrome
            if (!window.IntersectionObserver) {
                polyfills.push(import('intersection-observer'));
            }
        }
        
        // UC Browser
        if (device.isUCBrowser) {
            console.log('UC Browser detected');
            // UC Browser has various quirks, load conservative polyfills
            polyfills.push(
                import('core-js/features/promise'),
                import('whatwg-fetch')
            );
        }
        
        // iOS Safari
        if (device.isMobileSafari || (device.isIOS && device.isWebView)) {
            console.log('iOS Safari/WebView detected, version:', device.iosVersion);
            
            // iOS 12 and below
            if (device.iosVersion && device.iosVersion < 13) {
                if (!window.IntersectionObserver) {
                    polyfills.push(import('intersection-observer'));
                }
                if (!Promise.allSettled) {
                    polyfills.push(import('core-js/features/promise/all-settled'));
                }
            }
        }
        
        // Viewport units fix for mobile browsers
        if (device.isIOS || device.isAndroid) {
            this.fixViewportUnits();
        }
        
        // Touch event polyfills
        if (!('ontouchstart' in window)) {
            this.polyfillTouchEvents();
        }
        
        return Promise.all(polyfills).then(function() {
            console.log('Mobile polyfills loaded');
        });
    },
    
    // Fix viewport units on mobile
    fixViewportUnits: function() {
        // Fix for iOS Safari vh units including address bar
        var setVh = function() {
            var vh = window.innerHeight * 0.01;
            document.documentElement.style.setProperty('--vh', vh + 'px');
        };
        
        setVh();
        window.addEventListener('resize', setVh);
        window.addEventListener('orientationchange', setVh);
    },
    
    // Polyfill touch events for desktop testing
    polyfillTouchEvents: function() {
        console.log('Polyfilling touch events');
        
        // Map mouse events to touch events
        var touchMap = {
            'mousedown': 'touchstart',
            'mousemove': 'touchmove',
            'mouseup': 'touchend'
        };
        
        Object.keys(touchMap).forEach(function(mouseEvent) {
            document.addEventListener(mouseEvent, function(e) {
                var touch = {
                    identifier: Date.now(),
                    target: e.target,
                    clientX: e.clientX,
                    clientY: e.clientY,
                    pageX: e.pageX,
                    pageY: e.pageY,
                    screenX: e.screenX,
                    screenY: e.screenY
                };
                
                var touchEvent = new CustomEvent(touchMap[mouseEvent], {
                    bubbles: true,
                    cancelable: true,
                    detail: {
                        touches: [touch],
                        targetTouches: [touch],
                        changedTouches: [touch]
                    }
                });
                
                e.target.dispatchEvent(touchEvent);
            });
        });
    }
};

// Initialize mobile polyfills
MobilePolyfills.load().then(function() {
    console.log('Mobile app ready');
    initApp();
});
Warning: Android 4.4 WebView (still ~8% of users in developing markets) requires extensive polyfilling. Consider separate build or basic fallback.

4. Progressive Enhancement with Polyfills

Layer Implementation Fallback User Experience
Core Functionality Works without JS/polyfills Server-side rendering Basic but functional
Enhanced UI Modern JS features Polyfills load on-demand Rich interactions
Advanced Features Cutting-edge APIs Feature detection, graceful skip Optional enhancements
Performance Modern browser optimizations Fallback to simpler code Fast for all

Example: Progressive enhancement pattern

// progressive-enhancement.js
var ProgressiveEnhancement = {
    // Feature tiers
    tiers: {
        core: {
            name: 'Core Experience',
            required: [],
            features: ['basicDOM', 'events']
        },
        
        enhanced: {
            name: 'Enhanced Experience',
            required: ['Promise', 'fetch', 'Object.assign'],
            features: ['asyncData', 'modernUI']
        },
        
        advanced: {
            name: 'Advanced Experience',
            required: [
                'IntersectionObserver',
                'ResizeObserver',
                'CustomElements'
            ],
            features: ['lazyLoading', 'animations', 'webComponents']
        }
    },
    
    // Detect available tier
    detectTier: function() {
        var hasEnhanced = this.tiers.enhanced.required.every(function(feature) {
            return this.hasFeature(feature);
        }.bind(this));
        
        var hasAdvanced = this.tiers.advanced.required.every(function(feature) {
            return this.hasFeature(feature);
        }.bind(this));
        
        if (hasAdvanced) {
            return 'advanced';
        } else if (hasEnhanced) {
            return 'enhanced';
        } else {
            return 'core';
        }
    },
    
    // Check feature availability
    hasFeature: function(featureName) {
        var checks = {
            'Promise': function() {
                return typeof Promise !== 'undefined';
            },
            'fetch': function() {
                return typeof fetch !== 'undefined';
            },
            'Object.assign': function() {
                return typeof Object.assign === 'function';
            },
            'IntersectionObserver': function() {
                return typeof IntersectionObserver !== 'undefined';
            },
            'ResizeObserver': function() {
                return typeof ResizeObserver !== 'undefined';
            },
            'CustomElements': function() {
                return typeof customElements !== 'undefined';
            }
        };
        
        return checks[featureName] ? checks[featureName]() : false;
    },
    
    // Load polyfills to upgrade tier
    upgradeTier: function(targetTier) {
        var currentTier = this.detectTier();
        
        console.log('Current tier:', currentTier);
        console.log('Target tier:', targetTier);
        
        if (currentTier === targetTier) {
            return Promise.resolve(currentTier);
        }
        
        var polyfills = [];
        var required = this.tiers[targetTier].required;
        
        required.forEach(function(feature) {
            if (!this.hasFeature(feature)) {
                polyfills.push(this.loadPolyfillFor(feature));
            }
        }.bind(this));
        
        return Promise.all(polyfills).then(function() {
            console.log('Upgraded to:', targetTier);
            return targetTier;
        });
    },
    
    // Load polyfill for specific feature
    loadPolyfillFor: function(feature) {
        var polyfillMap = {
            'Promise': function() {
                return import('es6-promise/auto');
            },
            'fetch': function() {
                return import('whatwg-fetch');
            },
            'Object.assign': function() {
                return import('core-js/features/object/assign');
            },
            'IntersectionObserver': function() {
                return import('intersection-observer');
            },
            'ResizeObserver': function() {
                return import('resize-observer-polyfill')
                    .then(function(module) {
                        window.ResizeObserver = module.default;
                    });
            }
        };
        
        console.log('Loading polyfill for:', feature);
        return polyfillMap[feature] ? polyfillMap[feature]() : Promise.resolve();
    },
    
    // Initialize with progressive enhancement
    init: function(preferredTier) {
        var tier = preferredTier || 'enhanced';
        
        return this.upgradeTier(tier).then(function(achievedTier) {
            // Add tier class to document
            document.documentElement.classList.add('tier-' + achievedTier);
            document.documentElement.setAttribute('data-tier', achievedTier);
            
            // Load tier-specific features
            this.loadFeaturesForTier(achievedTier);
            
            return achievedTier;
        }.bind(this));
    },
    
    // Load features for tier
    loadFeaturesForTier: function(tier) {
        var features = this.tiers[tier].features;
        
        console.log('Loading features:', features);
        
        if (tier === 'advanced') {
            // Enable all advanced features
            this.enableLazyLoading();
            this.enableAnimations();
            this.enableWebComponents();
        } else if (tier === 'enhanced') {
            // Enable enhanced features
            this.enableAsyncData();
            this.enableModernUI();
        } else {
            // Core features only
            this.enableBasicUI();
        }
    },
    
    enableLazyLoading: function() {
        console.log('Lazy loading enabled');
        // Use IntersectionObserver for lazy loading
    },
    
    enableAnimations: function() {
        console.log('Animations enabled');
        // Use Web Animations API
    },
    
    enableWebComponents: function() {
        console.log('Web Components enabled');
        // Load custom elements
    },
    
    enableAsyncData: function() {
        console.log('Async data enabled');
        // Use fetch for data loading
    },
    
    enableModernUI: function() {
        console.log('Modern UI enabled');
        // Enable modern UI features
    },
    
    enableBasicUI: function() {
        console.log('Basic UI enabled');
        // Minimal UI enhancements
    }
};

// Usage
ProgressiveEnhancement.init('enhanced').then(function(tier) {
    console.log('App initialized with tier:', tier);
    
    // Start application
    initApp(tier);
});
Note: Progressive enhancement ensures everyone gets a working experience. Load polyfills to upgrade capabilities, never as a hard requirement.

5. Graceful Degradation Implementation Patterns

Pattern Strategy Fallback Example
Feature Detection Check before use Alternative implementation IntersectionObserver → scroll events
Try-Catch Wrapper Catch unsupported features Simpler alternative async/await → Promise chains
Polyfill + Fallback Load polyfill, fallback if fails Native or nothing fetch → XMLHttpRequest
Conditional Loading Skip feature entirely Simplified UX Skip animations on old browsers
Message Display Inform user of limitations Upgrade prompt "Update browser for best experience"

Example: Graceful degradation utilities

// graceful-degradation.js
var GracefulDegradation = {
    // Feature availability cache
    features: {},
    
    // Check and cache feature availability
    supports: function(feature) {
        if (this.features.hasOwnProperty(feature)) {
            return this.features[feature];
        }
        
        var checks = {
            'IntersectionObserver': function() {
                return 'IntersectionObserver' in window;
            },
            'ResizeObserver': function() {
                return 'ResizeObserver' in window;
            },
            'fetch': function() {
                return 'fetch' in window;
            },
            'Promise': function() {
                return 'Promise' in window;
            },
            'async-await': function() {
                try {
                    eval('(async () => {})');
                    return true;
                } catch (e) {
                    return false;
                }
            },
            'webp': function(callback) {
                // Async check for WebP support
                var img = new Image();
                img.onload = function() {
                    callback(img.width > 0 && img.height > 0);
                };
                img.onerror = function() {
                    callback(false);
                };
                img.src = 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==';
            },
            'localStorage': function() {
                try {
                    var test = '__test__';
                    localStorage.setItem(test, test);
                    localStorage.removeItem(test);
                    return true;
                } catch (e) {
                    return false;
                }
            }
        };
        
        var result = checks[feature] ? checks[feature]() : false;
        this.features[feature] = result;
        
        return result;
    },
    
    // Execute with fallback
    withFallback: function(primary, fallback) {
        try {
            return primary();
        } catch (e) {
            console.warn('Primary function failed, using fallback:', e);
            return fallback();
        }
    },
    
    // Lazy loading with fallback
    lazyLoad: function(selector, options) {
        if (this.supports('IntersectionObserver')) {
            // Use IntersectionObserver
            var observer = new IntersectionObserver(function(entries) {
                entries.forEach(function(entry) {
                    if (entry.isIntersecting) {
                        var img = entry.target;
                        img.src = img.dataset.src;
                        observer.unobserve(img);
                    }
                });
            }, options);
            
            document.querySelectorAll(selector).forEach(function(img) {
                observer.observe(img);
            });
        } else {
            // Fallback to immediate loading
            console.log('IntersectionObserver not supported, loading all images');
            document.querySelectorAll(selector).forEach(function(img) {
                img.src = img.dataset.src;
            });
        }
    },
    
    // Fetch with XMLHttpRequest fallback
    fetchWithFallback: function(url, options) {
        if (this.supports('fetch')) {
            return fetch(url, options).then(function(response) {
                return response.json();
            });
        } else {
            // XMLHttpRequest fallback
            return new Promise(function(resolve, reject) {
                var xhr = new XMLHttpRequest();
                xhr.open(options && options.method || 'GET', url);
                
                xhr.onload = function() {
                    if (xhr.status >= 200 && xhr.status < 300) {
                        try {
                            resolve(JSON.parse(xhr.responseText));
                        } catch (e) {
                            reject(e);
                        }
                    } else {
                        reject(new Error('Request failed: ' + xhr.status));
                    }
                };
                
                xhr.onerror = function() {
                    reject(new Error('Network error'));
                };
                
                xhr.send(options && options.body);
            });
        }
    },
    
    // Storage with fallback
    storage: {
        get: function(key) {
            if (GracefulDegradation.supports('localStorage')) {
                return localStorage.getItem(key);
            } else {
                // Cookie fallback
                var nameEQ = key + '=';
                var ca = document.cookie.split(';');
                for (var i = 0; i < ca.length; i++) {
                    var c = ca[i];
                    while (c.charAt(0) === ' ') c = c.substring(1, c.length);
                    if (c.indexOf(nameEQ) === 0) {
                        return c.substring(nameEQ.length, c.length);
                    }
                }
                return null;
            }
        },
        
        set: function(key, value) {
            if (GracefulDegradation.supports('localStorage')) {
                localStorage.setItem(key, value);
            } else {
                // Cookie fallback
                document.cookie = key + '=' + value + '; path=/';
            }
        },
        
        remove: function(key) {
            if (GracefulDegradation.supports('localStorage')) {
                localStorage.removeItem(key);
            } else {
                // Remove cookie
                document.cookie = key + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/';
            }
        }
    },
    
    // Browser upgrade message
    showUpgradeMessage: function() {
        var oldBrowsers = [
            navigator.userAgent.indexOf('MSIE') !== -1,
            navigator.userAgent.indexOf('Trident/') !== -1 && 
                navigator.userAgent.indexOf('rv:11') !== -1
        ];
        
        if (oldBrowsers.some(Boolean)) {
            var message = document.createElement('div');
            message.className = 'browser-upgrade-message';
            message.innerHTML = 
                '<p>You are using an outdated browser. ' +
                'Please <a href="https://browsehappy.com/">upgrade your browser</a> ' +
                'to improve your experience and security.</p>';
            
            document.body.insertBefore(message, document.body.firstChild);
        }
    }
};

// Usage examples
// Lazy loading with fallback
GracefulDegradation.lazyLoad('img[data-src]');

// Fetch with fallback
GracefulDegradation.fetchWithFallback('/api/data')
    .then(function(data) {
        console.log('Data loaded:', data);
    })
    .catch(function(error) {
        console.error('Failed to load data:', error);
    });

// Storage with fallback
GracefulDegradation.storage.set('user', 'John');
console.log('User:', GracefulDegradation.storage.get('user'));

// Show upgrade message for IE
GracefulDegradation.showUpgradeMessage();

Key Takeaways - Legacy Browser Support

  • IE11: Requires ~80-100 KB of polyfills, consider dropping or minimal UX
  • Safari: Often lags 1-2 years, focus on Observer APIs and Promise methods
  • Mobile: Android 4.4 still prevalent in developing markets, needs full polyfills
  • Progressive Enhancement: Build core experience first, enhance with polyfills
  • Graceful Degradation: Always have fallbacks, never break for old browsers