aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
365 lines (362 loc) âĸ 13.3 kB
JavaScript
import { canUseDOM } from './ssr.js';
class GlassStyleProbes {
constructor() {
this.probeResults = [];
this.observer = null;
this.performanceObserver = null;
this.isMonitoring = false;
}
static getInstance() {
if (!GlassStyleProbes.instance) {
GlassStyleProbes.instance = new GlassStyleProbes();
}
return GlassStyleProbes.instance;
}
// Start monitoring glass elements
startMonitoring() {
// Skip monitoring during SSR
if (!canUseDOM || this.isMonitoring) return;
console.log("đ Starting AuraGlass runtime probes...");
this.isMonitoring = true;
this.setupMutationObserver();
this.setupPerformanceObserver();
this.schedulePeriodicProbes();
// Initial scan of existing glass elements
this.scanExistingElements();
}
// Stop monitoring
stopMonitoring() {
if (!this.isMonitoring) return;
console.log("âšī¸ Stopping AuraGlass runtime probes...");
this.isMonitoring = false;
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
if (this.performanceObserver) {
this.performanceObserver.disconnect();
this.performanceObserver = null;
}
}
setupMutationObserver() {
if (!canUseDOM) return;
this.observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
this.probeElement(node);
}
});
} else if (mutation.type === "attributes") {
if (mutation.attributeName === "class" || mutation.attributeName === "style") {
this.probeElement(mutation.target);
}
}
});
});
this.observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["class", "style"]
});
}
setupPerformanceObserver() {
if (!canUseDOM) return;
if ("PerformanceObserver" in window) {
try {
this.performanceObserver = new PerformanceObserver(list => {
const entries = list.getEntries();
entries.forEach(entry => {
if (entry.name.includes("glass") || entry.name.includes("backdrop")) {
console.log("đââī¸ Glass performance entry:", entry);
}
});
});
this.performanceObserver.observe({
entryTypes: ["measure", "paint", "layout-shift"]
});
} catch (error) {
console.warn("â ī¸ Performance observer setup failed:", error);
}
}
}
schedulePeriodicProbes() {
// Run comprehensive probes every 10 seconds
setInterval(() => {
if (this.isMonitoring) {
this.runComprehensiveProbe();
}
}, 10000);
}
scanExistingElements() {
if (!canUseDOM) return;
const glassElements = document.querySelectorAll('[class*="glass-"]');
glassElements.forEach(element => {
this.probeElement(element);
});
}
probeElement(element) {
if (!this.isGlassElement(element)) return;
const result = this.analyzeElement(element);
this.probeResults.push(result);
// Keep only last 100 results to prevent memory issues
if (this.probeResults.length > 100) {
this.probeResults = this.probeResults.slice(-100);
}
// Report critical issues immediately
if (result.compliance.accessibilityScore < 0.7) {
console.warn("â ī¸ Glass accessibility issue detected:", result);
}
if (!result.performance.backdropSupported) {
console.warn("â ī¸ Backdrop filter not supported, glass effects disabled");
}
}
isGlassElement(element) {
const classes = element.className || "";
return classes.includes("glass-") || element.getAttribute("data-glass") !== null || this.hasGlassStyles(element);
}
hasGlassStyles(element) {
const computedStyle = window.getComputedStyle(element);
return computedStyle.backdropFilter !== "none" || computedStyle.backdropFilter !== "none";
}
analyzeElement(element) {
const startTime = performance.now();
const glassConfig = this.extractGlassConfiguration(element);
const performanceData = this.analyzePerformance(element);
const complianceData = this.analyzeCompliance(element);
const usageData = this.analyzeUsage(element);
const endTime = performance.now();
performanceData.renderTime = endTime - startTime;
return {
timestamp: Date.now(),
elementId: element.id,
glassConfiguration: glassConfig,
performance: performanceData,
compliance: complianceData,
usage: usageData
};
}
extractGlassConfiguration(element) {
const classes = element.className || "";
// Extract intent from class names like "glass-primary-level2"
const intentMatch = classes.match(/glass-(neutral|primary|secondary|success|warning|danger|info)-/);
const elevationMatch = classes.match(/glass-\w+-(level[1-4])/);
const tierMatch = classes.match(/glass-tier-(high|medium|low)/) || classes.match(/tier-(high|medium|low)/);
return {
intent: intentMatch ? intentMatch[1] : "unknown",
elevation: elevationMatch ? elevationMatch[1] : "unknown",
tier: tierMatch ? tierMatch[1] : "high" // default to high
};
}
analyzePerformance(element) {
const computedStyle = window.getComputedStyle(element);
const backdropSupported = this.testBackdropSupport();
const gpuAccelerated = this.testGPUAcceleration(computedStyle);
// Estimate memory usage (rough approximation)
const memoryUsage = this.estimateMemoryUsage(element);
return {
backdropSupported,
gpuAccelerated,
memoryUsage
};
}
analyzeCompliance(element) {
const computedStyle = window.getComputedStyle(element);
const contrastRatio = this.calculateContrastRatio(element);
const minVisibility = this.checkMinimumVisibility(computedStyle);
const accessibilityScore = this.calculateAccessibilityScore(element);
return {
wcagContrast: contrastRatio,
minVisibility,
accessibilityScore
};
}
analyzeUsage(element) {
const classes = element.className || "";
const inlineStyles = element.getAttribute("style") || "";
const warnings = [];
// Determine which API was used
let apiUsed = "unknown";
if (classes.match(/glass-\w+-level[1-4]/)) {
apiUsed = "css-classes"; // Using generated CSS classes
} else if (inlineStyles.includes("backdrop") || classes.includes("createGlassStyle")) {
apiUsed = "createGlassStyle"; // Using unified API
} else if (classes.includes("glassSurface") || classes.includes("glassBorder")) {
apiUsed = "legacy";
warnings.push("Using deprecated glass API - migrate to createGlassStyle()");
}
// Check for deprecated patterns
if (inlineStyles.includes("backdrop-filter")) {
warnings.push("Inline backdrop-filter detected - should use unified token system");
}
if (inlineStyles.match(/rgba\(\s*255,\s*255,\s*255,\s*0\.[0-9]/)) {
warnings.push("Hardcoded glass background - should use token system");
}
return {
apiUsed,
deprecationWarnings: warnings
};
}
testBackdropSupport() {
const testEl = document.createElement("div");
testEl.style.backdropFilter = "blur(1px)";
return testEl.style.backdropFilter !== "";
}
testGPUAcceleration(computedStyle) {
return computedStyle.transform !== "none" || computedStyle.willChange.includes("transform");
}
estimateMemoryUsage(element) {
// Rough estimation based on element complexity
const rect = element.getBoundingClientRect();
const area = rect.width * rect.height;
const complexity = (element.children.length + 1) * (element.className.split(" ").length + 1);
return Math.round(area * complexity / 1000); // KB estimate
}
calculateContrastRatio(element) {
try {
const computedStyle = window.getComputedStyle(element);
const backgroundColor = computedStyle.backgroundColor;
const color = computedStyle.color;
// Simplified contrast calculation
const bgLuminance = this.getColorLuminance(backgroundColor);
const textLuminance = this.getColorLuminance(color);
const lighter = Math.max(bgLuminance, textLuminance);
const darker = Math.min(bgLuminance, textLuminance);
return (lighter + 0.05) / (darker + 0.05);
} catch {
return 1; // Fallback if calculation fails
}
}
getColorLuminance(color) {
// Simplified luminance calculation
const rgb = this.parseColor(color);
if (!rgb) return 1;
const [r, g, b] = rgb.map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
parseColor(color) {
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
return match ? [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])] : null;
}
checkMinimumVisibility(computedStyle) {
const opacity = parseFloat(computedStyle.opacity);
const backgroundAlpha = this.extractAlphaFromBackground(computedStyle.backgroundColor);
return opacity >= 0.05 && backgroundAlpha >= 0.05;
}
extractAlphaFromBackground(backgroundColor) {
const match = backgroundColor.match(/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\)/);
return match ? parseFloat(match[1]) : 1;
}
calculateAccessibilityScore(element) {
let score = 1.0;
const contrastRatio = this.calculateContrastRatio(element);
if (contrastRatio < 4.5) score -= 0.3; // WCAG AA failure
if (contrastRatio < 3.0) score -= 0.3; // Severe contrast issue
const computedStyle = window.getComputedStyle(element);
if (!this.checkMinimumVisibility(computedStyle)) score -= 0.2;
if (!element.getAttribute("aria-label") && !element.textContent) {
score -= 0.1; // Missing accessibility info
}
return Math.max(0, score);
}
runComprehensiveProbe() {
const glassElements = document.querySelectorAll('[class*="glass-"]');
const summary = {
timestamp: Date.now(),
totalElements: glassElements.length,
compliance: {
passed: 0,
warning: 0,
failed: 0
},
performance: {
backdropSupported: this.testBackdropSupport(),
averageRenderTime: 0,
memoryUsage: 0
},
usage: {
unified: 0,
legacy: 0,
unknown: 0
},
deprecationWarnings: []
};
let totalRenderTime = 0;
let totalMemory = 0;
this.probeResults.slice(-glassElements.length).forEach(result => {
// Compliance scoring
if (result.compliance.accessibilityScore >= 0.8) summary.compliance.passed++;else if (result.compliance.accessibilityScore >= 0.6) summary.compliance.warning++;else summary.compliance.failed++;
// Performance aggregation
if (result.performance.renderTime) totalRenderTime += result.performance.renderTime;
if (result.performance.memoryUsage) totalMemory += result.performance.memoryUsage;
// Usage patterns
if (result.usage.apiUsed === "createGlassStyle" || result.usage.apiUsed === "css-classes") {
summary.usage.unified++;
} else if (result.usage.apiUsed === "legacy") {
summary.usage.legacy++;
} else {
summary.usage.unknown++;
}
// Collect warnings
summary.deprecationWarnings.push(...result.usage.deprecationWarnings);
});
summary.performance.averageRenderTime = totalRenderTime / glassElements.length;
summary.performance.memoryUsage = totalMemory;
// Remove duplicate warnings
summary.deprecationWarnings = [...new Set(summary.deprecationWarnings)];
console.log("đ AuraGlass Comprehensive Probe Summary:", summary);
// Store for potential reporting
window.__auraglassProbeData = {
latestSummary: summary,
allResults: this.probeResults
};
}
// Public API for accessing probe data
getProbeResults() {
return [...this.probeResults];
}
getLatestSummary() {
return window.__auraglassProbeData?.latestSummary;
}
// Force a probe of specific element
probeElementById(elementId) {
const element = document.getElementById(elementId);
if (!element) return null;
return this.analyzeElement(element);
}
}
/**
* Utility to manually start probes (call from useEffect or initialization code)
* @param options - Configuration options
*/
function startGlassProbes(options) {
if (!canUseDOM) return;
const probes = GlassStyleProbes.getInstance();
// Start monitoring after DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
probes.startMonitoring();
});
} else {
probes.startMonitoring();
}
// Expose to global scope for debugging if requested
if (options?.exposeGlobally && process.env.NODE_ENV === "development") {
window.__auraglassProbes = probes;
}
return probes;
}
/**
* Utility to stop probes
*/
function stopGlassProbes() {
if (!canUseDOM) return;
GlassStyleProbes.getInstance().stopMonitoring();
}
export { GlassStyleProbes, GlassStyleProbes as default, startGlassProbes, stopGlassProbes };
//# sourceMappingURL=glassStyleProbes.js.map