aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
463 lines (460 loc) • 13.5 kB
JavaScript
import { createGlassStyle } from '../core/mixins/glassMixins.js';
// Browser detection
let __cachedBrowserInfo = null;
let __cachedBrowserCapabilities = null;
let __lastBrowserDetectTs = 0;
const BROWSER_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
const probeGL = () => {
if (typeof document === 'undefined') return {
webgl: false,
webgl2: false
};
const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
const attrs = {
alpha: false,
antialias: false,
depth: false,
stencil: false,
preserveDrawingBuffer: false,
desynchronized: true,
failIfMajorPerformanceCaveat: true,
powerPreference: 'low-power'
};
let gl = null;
let gl2 = null;
try {
gl2 = canvas.getContext('webgl2', attrs);
} catch {
gl2 = null;
}
if (!gl2) {
try {
gl = canvas.getContext('webgl', attrs) || canvas.getContext('experimental-webgl', attrs) || null;
} catch {
gl = null;
}
}
const webgl2 = !!gl2;
const webgl = webgl2 || !!gl;
try {
const ctx = gl2 || gl;
const lose = ctx && typeof ctx.getExtension === 'function' && ctx.getExtension('WEBGL_lose_context');
if (lose && typeof lose.loseContext === 'function') lose.loseContext();
} catch {}
try {
canvas.width = canvas.height = 0;
canvas.remove();
} catch {}
return {
webgl,
webgl2
};
};
const detectBrowser = () => {
const now = Date.now();
if (__cachedBrowserInfo && now - __lastBrowserDetectTs < BROWSER_CACHE_TTL_MS) {
return __cachedBrowserInfo;
}
const ua = navigator.userAgent;
let name = 'unknown';
let version = 0;
let engine = 'unknown';
// Detect browser name and version
if (ua.includes('Chrome') && !ua.includes('Edg/')) {
name = 'Chrome';
const match = ua.match(/Chrome\/(\d+)/);
version = match ? parseInt(match[1]) : 0;
engine = 'blink';
} else if (ua.includes('Firefox')) {
name = 'Firefox';
const match = ua.match(/Firefox\/(\d+)/);
version = match ? parseInt(match[1]) : 0;
engine = 'gecko';
} else if (ua.includes('Safari') && !ua.includes('Chrome')) {
name = 'Safari';
const match = ua.match(/Version\/(\d+)/);
version = match ? parseInt(match[1]) : 0;
engine = 'webkit';
} else if (ua.includes('Edg/')) {
name = 'Edge';
const match = ua.match(/Edg\/(\d+)/);
version = match ? parseInt(match[1]) : 0;
engine = 'blink';
} else if (ua.includes('MSIE') || ua.includes('Trident')) {
name = 'Internet Explorer';
const match = ua.match(/(?:MSIE |Trident\/.*; rv:)(\d+)/);
version = match ? parseInt(match[1]) : 0;
engine = 'edge';
}
// Detect mobile
const mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
// Detect touch capability
const touch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// Detect capabilities
const supports = detectCapabilities();
const info = {
name,
version,
engine,
mobile,
touch,
supports
};
__cachedBrowserInfo = info;
__lastBrowserDetectTs = now;
return info;
};
// Capability detection
const detectCapabilities = () => {
const now = Date.now();
if (__cachedBrowserCapabilities && now - __lastBrowserDetectTs < BROWSER_CACHE_TTL_MS) {
return __cachedBrowserCapabilities;
}
const testElement = document.createElement('div');
const canvas = document.createElement('canvas');
const video = document.createElement('video');
const audio = document.createElement('audio');
const {
webgl,
webgl2
} = probeGL();
const caps = {
backdropFilter: 'backdropFilter' in testElement.style || 'webkitBackdropFilter' in testElement.style,
cssGrid: 'grid' in testElement.style,
flexbox: 'flex' in testElement.style,
cssVariables: 'CSS' in window && 'supports' in window.CSS && window.CSS.supports('--test', 'value'),
es6: (() => {
try {
new Function('const a = 1; return a;')();
return true;
} catch {
return false;
}
})(),
webgl,
webgl2,
webAnimations: 'animate' in testElement,
intersectionObserver: 'IntersectionObserver' in window,
resizeObserver: 'ResizeObserver' in window,
mutationObserver: 'MutationObserver' in window,
requestIdleCallback: 'requestIdleCallback' in window,
webWorkers: 'Worker' in window,
serviceWorkers: 'serviceWorker' in navigator,
indexedDB: 'indexedDB' in window,
localStorage: (() => {
try {
const test = 'test';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch {
return false;
}
})(),
sessionStorage: (() => {
try {
const test = 'test';
sessionStorage.setItem(test, test);
sessionStorage.removeItem(test);
return true;
} catch {
return false;
}
})(),
webRTC: 'RTCPeerConnection' in window || 'webkitRTCPeerConnection' in window,
webAudio: 'AudioContext' in window || 'webkitAudioContext' in window,
canvas: !!canvas.getContext('2d'),
svg: document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#BasicStructure', '1.1'),
video: !!video.canPlayType,
audio: !!audio.canPlayType
};
__cachedBrowserCapabilities = caps;
__lastBrowserDetectTs = now;
return caps;
};
// Compatibility helpers
const compatibilityHelpers = {
// Get fallback styles for unsupported features
getFallbackStyles: feature => {
const browser = detectBrowser();
switch (feature) {
case 'backdropFilter':
if (!browser.supports.backdropFilter) {
return {
background: '/* Use createGlassStyle({ intent: "neutral", elevation: "level2" }) */',
border: '1px solid rgba(0, 0, 0, 0.1)'
};
}
break;
case 'cssGrid':
if (!browser.supports.cssGrid) {
return {
display: 'flex',
flexWrap: 'wrap'
};
}
break;
case 'flexbox':
if (!browser.supports.flexbox) {
return {
display: 'block'
};
}
break;
case 'webAnimations':
if (!browser.supports.webAnimations) {
return {
transition: 'all 0.3s ease'
};
}
break;
}
return {};
},
// Check if browser supports glassmorphism
supportsGlassmorphism: () => {
const browser = detectBrowser();
return browser.supports.backdropFilter && browser.supports.cssVariables;
},
// Get appropriate animation library
getAnimationLibrary: () => {
const browser = detectBrowser();
if (browser.supports.webAnimations) {
return 'web-animations';
} else if (browser.version >= 10) {
return 'css-transitions';
} else {
return 'none';
}
},
// Check WebGL support level
getWebGLSupport: () => {
const browser = detectBrowser();
if (browser.supports.webgl2) {
return 'webgl2';
} else if (browser.supports.webgl) {
return 'webgl';
} else {
return 'none';
}
},
// Get appropriate storage mechanism
getStorageMechanism: () => {
const browser = detectBrowser();
if (browser.supports.indexedDB) {
return 'indexeddb';
} else if (browser.supports.localStorage) {
return 'localstorage';
} else {
return 'memory';
}
}
};
// Polyfill management
class PolyfillManager {
static async loadPolyfill(name, url) {
if (this.loadedPolyfills.has(name)) {
return;
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.onload = () => {
this.loadedPolyfills.add(name);
resolve();
};
script.onerror = reject;
document.head.appendChild(script);
});
}
static async loadRequiredPolyfills() {
const browser = detectBrowser();
const polyfills = [{
name: 'intersection-observer',
url: 'https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver',
condition: !browser.supports.intersectionObserver
}, {
name: 'resize-observer',
url: 'https://polyfill.io/v3/polyfill.min.js?features=ResizeObserver',
condition: !browser.supports.resizeObserver
}, {
name: 'request-idle-callback',
url: 'https://polyfill.io/v3/polyfill.min.js?features=requestIdleCallback',
condition: !browser.supports.requestIdleCallback
}, {
name: 'web-animations',
url: 'https://cdnjs.cloudflare.com/ajax/libs/web-animations/2.3.2/web-animations.min.js',
condition: !browser.supports.webAnimations
}];
const promises = polyfills.filter(polyfill => polyfill.condition).map(polyfill => this.loadPolyfill(polyfill.name, polyfill.url));
await Promise.all(promises);
}
static isPolyfillLoaded(name) {
return this.loadedPolyfills.has(name);
}
}
PolyfillManager.loadedPolyfills = new Set();
// Feature detection utilities
const featureDetection = {
// Check CSS feature support
supportsCSS: (property, value) => {
const testElement = document.createElement('div');
if (value) {
testElement.style.setProperty(property, value);
return testElement.style.getPropertyValue(property) === value;
} else {
return property in testElement.style;
}
},
// Check JavaScript feature support
supportsJS: feature => {
try {
switch (feature) {
case 'es6':
new Function('const a = 1; return a;')();
return true;
case 'async-await':
new Function('async function test() { await Promise.resolve(); }')();
return true;
case 'promises':
return 'Promise' in window;
case 'fetch':
return 'fetch' in window;
case 'proxy':
return 'Proxy' in window;
case 'map':
return 'Map' in window;
case 'set':
return 'Set' in window;
case 'weakmap':
return 'WeakMap' in window;
case 'weakset':
return 'WeakSet' in window;
default:
return false;
}
} catch {
return false;
}
},
// Check API support
supportsAPI: api => {
switch (api) {
case 'geolocation':
return 'geolocation' in navigator;
case 'notifications':
return 'Notification' in window;
case 'service-worker':
return 'serviceWorker' in navigator;
case 'web-share':
return 'share' in navigator;
case 'vibration':
return 'vibrate' in navigator;
case 'battery':
return 'getBattery' in navigator;
case 'device-orientation':
return 'DeviceOrientationEvent' in window;
case 'device-motion':
return 'DeviceMotionEvent' in window;
default:
return false;
}
},
// Get device pixel ratio
getDevicePixelRatio: () => {
return window.devicePixelRatio || 1;
},
// Check if running in iframe
isInIframe: () => {
try {
return window.self !== window.top;
} catch {
return true;
}
},
// Check if running in WebView
isInWebView: () => {
const ua = navigator.userAgent;
return /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(ua) || /\bwv\b/.test(ua) || /Android.*Version\/\d+\.\d+/i.test(ua);
},
// Check connection quality
getConnectionQuality: () => {
const connection = navigator.connection;
if (!connection) return 'unknown';
const effectiveType = connection.effectiveType;
if (effectiveType === 'slow-2g' || effectiveType === '2g') {
return 'slow';
} else {
return 'fast';
}
}
};
// Browser-specific optimizations
const browserOptimizations = {
// Safari-specific optimizations
safari: {
// Fix backdrop-filter performance
optimizeBackdropFilter: () => {
const browser = detectBrowser();
if (browser.name === 'Safari' && browser.version < 15) {
return {
willChange: 'transform',
transform: 'translateZ(0)'
};
}
return {};
},
// Fix flexbox bugs
fixFlexbox: () => {
return {
minWidth: 0,
minHeight: 0
};
}
},
// Firefox-specific optimizations
firefox: {
// Fix backdrop-filter support
optimizeBackdropFilter: () => {
const browser = detectBrowser();
if (browser.name === 'Firefox' && browser.version < 70) {
return createGlassStyle({
intent: "neutral",
elevation: "level2"
});
}
return {};
}
},
// Mobile-specific optimizations
mobile: {
// Optimize for touch interactions
optimizeForTouch: () => {
const browser = detectBrowser();
if (browser.mobile) {
return {
WebkitTapHighlightColor: 'transparent',
WebkitTouchCallout: 'none',
WebkitUserSelect: 'none',
touchAction: 'manipulation'
};
}
return {};
},
// Optimize animations for mobile
optimizeAnimations: () => {
const browser = detectBrowser();
if (browser.mobile) {
return {
willChange: 'transform',
transform: 'translateZ(0)'
};
}
return {};
}
}
};
export { PolyfillManager, browserOptimizations, compatibilityHelpers, detectBrowser, detectCapabilities, featureDetection };
//# sourceMappingURL=browserCompatibility.js.map