Back to blog
InspectionJanuary 1, 2024· 14 min read

Web Component and Shadow DOM Analysis: Modern Web Architecture

A practical Fusebox guide to web component and shadow dom analysis.

Web Component and Shadow DOM Analysis: Modern Web Architecture

Web Components represent a suite of technologies allowing you to create reusable custom elements with encapsulated functionality. This guide explores how to detect, analyze, and understand Web Components and Shadow DOM implementations in modern web applications.

Run This Web Component Analyzer Now

// Copy and paste this into your browser console to analyze Web Components

class WebComponentAnalyzer {
    constructor() {
        this.customElements = [];
        this.shadowRoots = [];
        this.slots = [];
        this.templates = [];
        this.adoptedStylesheets = [];
        this.stats = {
            totalCustomElements: 0,
            totalShadowRoots: 0,
            openShadowRoots: 0,
            closedShadowRoots: 0,
            slotsUsed: 0,
            templatesFound: 0
        };
    }
    
    analyzeWebComponents() {
        console.log('=
 Analyzing Web Components and Shadow DOM...\n');
        
        // Detect custom elements
        this.detectCustomElements();
        
        // Find shadow roots
        this.findShadowRoots();
        
        // Analyze templates
        this.analyzeTemplates();
        
        // Check for adopted stylesheets
        this.checkAdoptedStylesheets();
        
        // Analyze component communication
        this.analyzeComponentCommunication();
        
        // Generate report
        this.generateReport();
    }
    
    detectCustomElements() {
        // Get all registered custom elements
        const registry = window.customElements;
        
        // Check for custom element definitions
        if (registry && registry.get) {
            // Common custom element names to check
            const commonPrefixes = ['app-', 'x-', 'my-', 'custom-', 'wc-'];
            const allElements = document.querySelectorAll('*');
            
            allElements.forEach(element => {
                const tagName = element.tagName.toLowerCase();
                
                // Check if it's a custom element (contains hyphen)
                if (tagName.includes('-')) {
                    const definition = registry.get(tagName);
                    
                    this.customElements.push({
                        tagName,
                        element,
                        isDefined: !!definition,
                        hasSlots: element.querySelector('slot') !== null,
                        hasShadowRoot: !!element.shadowRoot,
                        attributes: Array.from(element.attributes).map(attr => ({
                            name: attr.name,
                            value: attr.value
                        }))
                    });
                    
                    this.stats.totalCustomElements++;
                }
            });
        }
        
        console.log(`Found ${this.stats.totalCustomElements} custom elements`);
    }
    
    findShadowRoots() {
        const walker = document.createTreeWalker(
            document.body,
            NodeFilter.SHOW_ELEMENT,
            {
                acceptNode: () => NodeFilter.FILTER_ACCEPT
            }
        );
        
        let node;
        while (node = walker.nextNode()) {
            if (node.shadowRoot) {
                const shadowInfo = {
                    host: node,
                    tagName: node.tagName.toLowerCase(),
                    mode: node.shadowRoot.mode,
                    childrenCount: node.shadowRoot.children.length,
                    hasStyles: node.shadowRoot.styleSheets.length > 0,
                    slots: Array.from(node.shadowRoot.querySelectorAll('slot')).map(slot => ({
                        name: slot.name || 'default',
                        assignedNodes: slot.assignedNodes().length
                    }))
                };
                
                this.shadowRoots.push(shadowInfo);
                this.stats.totalShadowRoots++;
                
                if (shadowInfo.mode === 'open') {
                    this.stats.openShadowRoots++;
                    
                    // Analyze shadow DOM content
                    this.analyzeShadowContent(node.shadowRoot);
                } else {
                    this.stats.closedShadowRoots++;
                }
            }
        }
    }
    
    analyzeShadowContent(shadowRoot) {
        // Check for slots
        const slots = shadowRoot.querySelectorAll('slot');
        slots.forEach(slot => {
            this.slots.push({
                name: slot.name || 'default',
                assignedNodes: slot.assignedNodes(),
                assignedElements: slot.assignedElements()
            });
            this.stats.slotsUsed++;
        });
        
        // Check for styles
        const styles = shadowRoot.querySelectorAll('style');
        if (styles.length > 0) {
            console.log(`  Shadow DOM contains ${styles.length} style tags`);
        }
        
        // Check for adopted stylesheets
        if (shadowRoot.adoptedStyleSheets && shadowRoot.adoptedStyleSheets.length > 0) {
            this.adoptedStylesheets.push({
                host: shadowRoot.host,
                count: shadowRoot.adoptedStyleSheets.length
            });
        }
    }
    
    analyzeTemplates() {
        const templates = document.querySelectorAll('template');
        
        templates.forEach(template => {
            const templateInfo = {
                id: template.id,
                hasContent: template.content.children.length > 0,
                childrenCount: template.content.children.length,
                usesSlots: template.content.querySelector('slot') !== null,
                parent: template.parentElement?.tagName.toLowerCase()
            };
            
            this.templates.push(templateInfo);
            this.stats.templatesFound++;
        });
    }
    
    checkAdoptedStylesheets() {
        // Check if Constructable Stylesheets are supported
        if ('adoptedStyleSheets' in document) {
            console.log(' Constructable Stylesheets supported');
            
            // Check document-level adopted stylesheets
            if (document.adoptedStyleSheets.length > 0) {
                console.log(`=� Document has ${document.adoptedStyleSheets.length} adopted stylesheets`);
            }
        }
    }
    
    analyzeComponentCommunication() {
        console.log('\n= Component Communication Analysis:');
        
        // Check for custom events
        const customEventPatterns = [
            'custom-',
            'component-',
            'app-',
            'change',
            'update'
        ];
        
        // Monitor for custom events (sampling approach)
        const eventListener = (e) => {
            if (e.type.includes('-') || customEventPatterns.some(p => e.type.includes(p))) {
                console.log(`  Custom event detected: ${e.type} from ${e.target.tagName}`);
            }
        };
        
        // Add temporary listener
        document.addEventListener('click', eventListener, true);
        setTimeout(() => {
            document.removeEventListener('click', eventListener, true);
        }, 100);
        
        // Check for properties/attributes that suggest communication
        this.customElements.forEach(comp => {
            const commProps = comp.attributes.filter(attr => 
                attr.name.startsWith('on') || 
                attr.name.includes('callback') ||
                attr.name.includes('handler')
            );
            
            if (commProps.length > 0) {
                console.log(`  ${comp.tagName} has event handlers:`, commProps.map(p => p.name));
            }
        });
    }
    
    generateReport() {
        console.log('\n=� Web Components Analysis Report\n');
        
        // Summary statistics
        console.log('=� Summary:');
        console.log(`  Custom Elements: ${this.stats.totalCustomElements}`);
        console.log(`  Shadow Roots: ${this.stats.totalShadowRoots} (${this.stats.openShadowRoots} open, ${this.stats.closedShadowRoots} closed)`);
        console.log(`  Slots Used: ${this.stats.slotsUsed}`);
        console.log(`  Templates: ${this.stats.templatesFound}`);
        
        // Custom elements details
        if (this.customElements.length > 0) {
            console.log('\n<� Custom Elements:');
            
            // Group by prefix
            const grouped = this.groupByPrefix(this.customElements);
            Object.entries(grouped).forEach(([prefix, elements]) => {
                console.log(`\n  ${prefix}* (${elements.length} elements)`);
                elements.slice(0, 3).forEach(el => {
                    const features = [];
                    if (el.hasShadowRoot) features.push('Shadow DOM');
                    if (el.hasSlots) features.push('Slots');
                    if (!el.isDefined) features.push('� Not defined');
                    
                    console.log(`    - ${el.tagName} ${features.length > 0 ? `[${features.join(', ')}]` : ''}`);
                });
            });
        }
        
        // Shadow DOM details
        if (this.shadowRoots.length > 0) {
            console.log('\n=e Shadow DOM Usage:');
            this.shadowRoots.slice(0, 5).forEach(shadow => {
                console.log(`  ${shadow.tagName}:`);
                console.log(`    Mode: ${shadow.mode}`);
                console.log(`    Children: ${shadow.childrenCount}`);
                if (shadow.slots.length > 0) {
                    console.log(`    Slots: ${shadow.slots.map(s => s.name).join(', ')}`);
                }
            });
        }
        
        // Templates
        if (this.templates.length > 0) {
            console.log('\n=� Templates:');
            this.templates.forEach(template => {
                console.log(`  ${template.id || 'unnamed'}: ${template.childrenCount} children`);
            });
        }
        
        // Generate best practices
        this.generateBestPractices();
    }
    
    groupByPrefix(elements) {
        const grouped = {};
        
        elements.forEach(el => {
            const prefix = el.tagName.split('-')[0];
            if (!grouped[prefix]) {
                grouped[prefix] = [];
            }
            grouped[prefix].push(el);
        });
        
        return grouped;
    }
    
    generateBestPractices() {
        console.log('\n=� Recommendations:');
        
        if (this.stats.closedShadowRoots > 0) {
            console.log('  � Closed shadow roots detected - consider using open mode for better tooling support');
        }
        
        if (this.customElements.some(el => !el.isDefined)) {
            console.log('  � Undefined custom elements found - ensure all custom elements are registered');
        }
        
        if (this.stats.totalCustomElements > 0 && this.adoptedStylesheets.length === 0) {
            console.log('  =� Consider using Constructable Stylesheets for better performance');
        }
        
        if (this.slots.some(slot => slot.assignedNodes.length === 0)) {
            console.log('  9 Empty slots detected - provide default content for better UX');
        }
    }
}

// Run the analyzer
const analyzer = new WebComponentAnalyzer();
analyzer.analyzeWebComponents();

Understanding Web Components

Web Components consist of three main technologies:

  1. Custom Elements: Define new HTML elements
  2. Shadow DOM: Encapsulate styles and markup
  3. HTML Templates: Define markup templates

Advanced Shadow DOM Inspector

// Enhanced Shadow DOM exploration tool

class ShadowDOMInspector {
    constructor() {
        this.shadowHosts = new Map();
        this.styleIsolation = [];
        this.eventRetargeting = [];
    }
    
    inspectShadowDOM() {
        console.log('=& Deep Shadow DOM Inspection...\n');
        
        // Find all shadow hosts
        this.findAllShadowHosts();
        
        // Analyze style isolation
        this.analyzeStyleIsolation();
        
        // Check event retargeting
        this.checkEventRetargeting();
        
        // Analyze slot distribution
        this.analyzeSlotDistribution();
        
        // Check for CSS custom properties
        this.analyzeCSSCustomProperties();
        
        // Generate detailed report
        this.generateDetailedReport();
    }
    
    findAllShadowHosts(root = document.body, depth = 0) {
        const walker = document.createTreeWalker(
            root,
            NodeFilter.SHOW_ELEMENT
        );
        
        let node;
        while (node = walker.nextNode()) {
            if (node.shadowRoot) {
                const shadowData = {
                    host: node,
                    depth,
                    mode: node.shadowRoot.mode,
                    styles: this.extractStyles(node.shadowRoot),
                    customProperties: this.extractCustomProperties(node.shadowRoot),
                    eventListeners: this.detectEventListeners(node)
                };
                
                this.shadowHosts.set(node, shadowData);
                
                // Recursively search within shadow roots
                if (node.shadowRoot.mode === 'open') {
                    this.findAllShadowHosts(node.shadowRoot, depth + 1);
                }
            }
        }
    }
    
    extractStyles(shadowRoot) {
        const styles = {
            inline: [],
            adopted: [],
            external: []
        };
        
        // Inline styles
        shadowRoot.querySelectorAll('style').forEach(style => {
            styles.inline.push({
                content: style.textContent.substring(0, 100) + '...',
                rules: style.sheet ? style.sheet.cssRules.length : 0
            });
        });
        
        // Adopted stylesheets
        if (shadowRoot.adoptedStyleSheets) {
            shadowRoot.adoptedStyleSheets.forEach(sheet => {
                styles.adopted.push({
                    rules: sheet.cssRules.length
                });
            });
        }
        
        // External stylesheets
        shadowRoot.querySelectorAll('link[rel="stylesheet"]').forEach(link => {
            styles.external.push({
                href: link.href
            });
        });
        
        return styles;
    }
    
    extractCustomProperties(shadowRoot) {
        const customProps = new Set();
        
        // Check inline styles for custom properties
        shadowRoot.querySelectorAll('style').forEach(style => {
            if (style.sheet) {
                Array.from(style.sheet.cssRules).forEach(rule => {
                    if (rule.style) {
                        const cssText = rule.style.cssText;
                        const matches = cssText.match(/--[\w-]+/g);
                        if (matches) {
                            matches.forEach(prop => customProps.add(prop));
                        }
                    }
                });
            }
        });
        
        return Array.from(customProps);
    }
    
    detectEventListeners(element) {
        // Get event listeners (Chrome DevTools API if available)
        if (window.getEventListeners) {
            return Object.keys(window.getEventListeners(element));
        }
        
        // Fallback: check for common event attributes
        const eventAttrs = Array.from(element.attributes)
            .filter(attr => attr.name.startsWith('on'))
            .map(attr => attr.name.substring(2));
        
        return eventAttrs;
    }
    
    analyzeStyleIsolation() {
        console.log('<� Analyzing Style Isolation...\n');
        
        this.shadowHosts.forEach((data, host) => {
            // Check if styles leak out
            const hostStyles = window.getComputedStyle(host);
            
            // Test style isolation
            const testElement = document.createElement('div');
            testElement.className = 'shadow-test-element';
            
            // Add to shadow and light DOM to compare
            if (data.mode === 'open') {
                const shadowTest = testElement.cloneNode();
                host.shadowRoot.appendChild(shadowTest);
                
                const lightTest = testElement.cloneNode();
                document.body.appendChild(lightTest);
                
                // Compare computed styles
                const shadowStyles = window.getComputedStyle(shadowTest);
                const lightStyles = window.getComputedStyle(lightTest);
                
                this.styleIsolation.push({
                    host: host.tagName,
                    isolated: shadowStyles.color !== lightStyles.color
                });
                
                // Cleanup
                shadowTest.remove();
                lightTest.remove();
            }
        });
    }
    
    checkEventRetargeting() {
        console.log('<� Checking Event Retargeting...\n');
        
        // Set up event listener to check retargeting
        const checkRetargeting = (e) => {
            if (e.target !== e.composedPath()[0]) {
                this.eventRetargeting.push({
                    type: e.type,
                    target: e.target.tagName,
                    originalTarget: e.composedPath()[0].tagName,
                    retargeted: true
                });
            }
        };
        
        // Temporary listener
        document.addEventListener('click', checkRetargeting, true);
        
        // Simulate clicks on shadow elements
        this.shadowHosts.forEach((data, host) => {
            if (data.mode === 'open' && host.shadowRoot.firstElementChild) {
                const event = new MouseEvent('click', { bubbles: true });
                host.shadowRoot.firstElementChild.dispatchEvent(event);
            }
        });
        
        document.removeEventListener('click', checkRetargeting, true);
    }
    
    analyzeSlotDistribution() {
        console.log('<� Analyzing Slot Distribution...\n');
        
        const slotAnalysis = [];
        
        this.shadowHosts.forEach((data, host) => {
            if (data.mode === 'open') {
                const slots = host.shadowRoot.querySelectorAll('slot');
                
                slots.forEach(slot => {
                    const slotInfo = {
                        name: slot.name || 'default',
                        assignedNodes: slot.assignedNodes(),
                        assignedElements: slot.assignedElements(),
                        fallback: slot.childNodes.length > 0
                    };
                    
                    slotAnalysis.push({
                        host: host.tagName,
                        slot: slotInfo
                    });
                });
            }
        });
        
        return slotAnalysis;
    }
    
    analyzeCSSCustomProperties() {
        const customPropertyUsage = new Map();
        
        this.shadowHosts.forEach((data, host) => {
            data.customProperties.forEach(prop => {
                if (!customPropertyUsage.has(prop)) {
                    customPropertyUsage.set(prop, []);
                }
                customPropertyUsage.set(prop, [...customPropertyUsage.get(prop), host.tagName]);
            });
        });
        
        return customPropertyUsage;
    }
    
    generateDetailedReport() {
        console.log('\n=� Detailed Shadow DOM Report\n');
        
        // Shadow host hierarchy
        console.log('<3 Shadow Host Hierarchy:');
        const hostsByDepth = new Map();
        
        this.shadowHosts.forEach((data, host) => {
            if (!hostsByDepth.has(data.depth)) {
                hostsByDepth.set(data.depth, []);
            }
            hostsByDepth.get(data.depth).push({
                tag: host.tagName.toLowerCase(),
                mode: data.mode
            });
        });
        
        hostsByDepth.forEach((hosts, depth) => {
            console.log(`  Level ${depth}: ${hosts.map(h => `${h.tag}(${h.mode})`).join(', ')}`);
        });
        
        // Style analysis
        console.log('\n<� Style Encapsulation:');
        let totalStyles = 0;
        this.shadowHosts.forEach((data, host) => {
            const styleCount = data.styles.inline.length + 
                             data.styles.adopted.length + 
                             data.styles.external.length;
            if (styleCount > 0) {
                console.log(`  ${host.tagName}: ${styleCount} stylesheets`);
                totalStyles += styleCount;
            }
        });
        
        // Custom properties
        const customProps = this.analyzeCSSCustomProperties();
        if (customProps.size > 0) {
            console.log('\n<� CSS Custom Properties:');
            customProps.forEach((components, prop) => {
                console.log(`  ${prop}: used by ${components.join(', ')}`);
            });
        }
        
        // Performance considerations
        console.log('\n� Performance Considerations:');
        console.log(`  Total shadow roots: ${this.shadowHosts.size}`);
        console.log(`  Total encapsulated styles: ${totalStyles}`);
        
        if (this.shadowHosts.size > 50) {
            console.log('  � Large number of shadow roots may impact performance');
        }
    }
}

// Run the inspector
const inspector = new ShadowDOMInspector();
inspector.inspectShadowDOM();

Web Component Lifecycle Monitor

// Monitor Web Component lifecycle events

class ComponentLifecycleMonitor {
    constructor() {
        this.lifecycleEvents = [];
        this.performanceMetrics = new Map();
    }
    
    monitorComponent(ComponentClass) {
        const monitor = this;
        
        return class extends ComponentClass {
            constructor() {
                const start = performance.now();
                super();
                
                monitor.recordEvent(this.tagName, 'constructor', performance.now() - start);
            }
            
            connectedCallback() {
                const start = performance.now();
                
                if (super.connectedCallback) {
                    super.connectedCallback();
                }
                
                monitor.recordEvent(this.tagName, 'connected', performance.now() - start);
                monitor.analyzeRenderPerformance(this);
            }
            
            disconnectedCallback() {
                const start = performance.now();
                
                if (super.disconnectedCallback) {
                    super.disconnectedCallback();
                }
                
                monitor.recordEvent(this.tagName, 'disconnected', performance.now() - start);
            }
            
            attributeChangedCallback(name, oldValue, newValue) {
                const start = performance.now();
                
                if (super.attributeChangedCallback) {
                    super.attributeChangedCallback(name, oldValue, newValue);
                }
                
                monitor.recordEvent(this.tagName, 'attributeChanged', performance.now() - start, {
                    attribute: name,
                    oldValue,
                    newValue
                });
            }
            
            adoptedCallback() {
                const start = performance.now();
                
                if (super.adoptedCallback) {
                    super.adoptedCallback();
                }
                
                monitor.recordEvent(this.tagName, 'adopted', performance.now() - start);
            }
        };
    }
    
    recordEvent(tagName, event, duration, details = {}) {
        this.lifecycleEvents.push({
            tagName,
            event,
            duration,
            timestamp: performance.now(),
            details
        });
        
        // Update performance metrics
        if (!this.performanceMetrics.has(tagName)) {
            this.performanceMetrics.set(tagName, {
                totalTime: 0,
                eventCount: 0,
                events: {}
            });
        }
        
        const metrics = this.performanceMetrics.get(tagName);
        metrics.totalTime += duration;
        metrics.eventCount++;
        
        if (!metrics.events[event]) {
            metrics.events[event] = {
                count: 0,
                totalTime: 0,
                avgTime: 0
            };
        }
        
        metrics.events[event].count++;
        metrics.events[event].totalTime += duration;
        metrics.events[event].avgTime = metrics.events[event].totalTime / metrics.events[event].count;
    }
    
    analyzeRenderPerformance(element) {
        // Measure time to first paint after connection
        requestAnimationFrame(() => {
            const renderTime = performance.now();
            
            // Check if element has shadow DOM
            if (element.shadowRoot) {
                const shadowElements = element.shadowRoot.querySelectorAll('*').length;
                this.recordEvent(element.tagName, 'render', 0, {
                    shadowElements,
                    hasStyles: element.shadowRoot.styleSheets.length > 0
                });
            }
        });
    }
    
    generateReport() {
        console.log('\n� Component Lifecycle Performance Report\n');
        
        this.performanceMetrics.forEach((metrics, tagName) => {
            console.log(`=� ${tagName}:`);
            console.log(`  Total time: ${metrics.totalTime.toFixed(2)}ms`);
            console.log(`  Events fired: ${metrics.eventCount}`);
            
            Object.entries(metrics.events).forEach(([event, data]) => {
                console.log(`  ${event}: ${data.count}x (avg ${data.avgTime.toFixed(2)}ms)`);
            });
            
            console.log('');
        });
        
        // Find slow components
        const slowComponents = Array.from(this.performanceMetrics.entries())
            .filter(([_, metrics]) => metrics.totalTime > 50)
            .sort((a, b) => b[1].totalTime - a[1].totalTime);
        
        if (slowComponents.length > 0) {
            console.log('� Slow Components:');
            slowComponents.forEach(([tagName, metrics]) => {
                console.log(`  ${tagName}: ${metrics.totalTime.toFixed(2)}ms total`);
            });
        }
    }
}

// Example usage:
// const monitor = new ComponentLifecycleMonitor();
// customElements.define('my-element', monitor.monitorComponent(MyElement));

Component Communication Analyzer

// Analyze how Web Components communicate

class ComponentCommunicationAnalyzer {
    constructor() {
        this.communications = [];
        this.propertyBindings = new Map();
        this.eventFlows = [];
        this.setupListeners();
    }
    
    setupListeners() {
        // Override dispatchEvent to track custom events
        const originalDispatch = EventTarget.prototype.dispatchEvent;
        
        EventTarget.prototype.dispatchEvent = function(event) {
            if (event.type.includes('-') || event.constructor.name !== 'Event') {
                this.communications.push({
                    type: 'event',
                    eventType: event.type,
                    source: this.tagName || this.constructor.name,
                    timestamp: performance.now(),
                    detail: event.detail,
                    bubbles: event.bubbles,
                    composed: event.composed
                });
            }
            
            return originalDispatch.call(this, event);
        }.bind(this);
    }
    
    analyzePropertyBindings() {
        const components = document.querySelectorAll('*');
        
        components.forEach(component => {
            if (component.tagName.includes('-')) {
                // Check for property setters
                const proto = Object.getPrototypeOf(component);
                const descriptors = Object.getOwnPropertyDescriptors(proto);
                
                Object.entries(descriptors).forEach(([prop, descriptor]) => {
                    if (descriptor.set && !prop.startsWith('_')) {
                        this.propertyBindings.set(`${component.tagName}.${prop}`, {
                            hasGetter: !!descriptor.get,
                            hasSetter: !!descriptor.set,
                            configurable: descriptor.configurable
                        });
                    }
                });
            }
        });
    }
    
    traceEventFlow(eventType, duration = 1000) {
        const trace = [];
        
        const captureHandler = (e) => {
            trace.push({
                phase: 'capture',
                target: e.currentTarget.tagName,
                timestamp: performance.now()
            });
        };
        
        const bubbleHandler = (e) => {
            trace.push({
                phase: 'bubble',
                target: e.currentTarget.tagName,
                timestamp: performance.now()
            });
        };
        
        // Add listeners to all elements
        document.querySelectorAll('*').forEach(el => {
            el.addEventListener(eventType, captureHandler, true);
            el.addEventListener(eventType, bubbleHandler, false);
        });
        
        // Remove after duration
        setTimeout(() => {
            document.querySelectorAll('*').forEach(el => {
                el.removeEventListener(eventType, captureHandler, true);
                el.removeEventListener(eventType, bubbleHandler, false);
            });
            
            if (trace.length > 0) {
                this.eventFlows.push({
                    eventType,
                    flow: trace
                });
            }
        }, duration);
    }
    
    generateCommunicationMap() {
        console.log('\n=� Component Communication Map\n');
        
        // Event communication
        if (this.communications.length > 0) {
            console.log('=� Event Communications:');
            
            const eventsByType = {};
            this.communications.forEach(comm => {
                if (!eventsByType[comm.eventType]) {
                    eventsByType[comm.eventType] = [];
                }
                eventsByType[comm.eventType].push(comm);
            });
            
            Object.entries(eventsByType).forEach(([type, events]) => {
                console.log(`  ${type}: ${events.length} dispatches`);
                const sources = [...new Set(events.map(e => e.source))];
                console.log(`    Sources: ${sources.join(', ')}`);
            });
        }
        
        // Property bindings
        if (this.propertyBindings.size > 0) {
            console.log('\n= Property Bindings:');
            this.propertyBindings.forEach((binding, prop) => {
                console.log(`  ${prop}:`);
                console.log(`    Getter: ${binding.hasGetter ? '' : 'L'}`);
                console.log(`    Setter: ${binding.hasSetter ? '' : 'L'}`);
            });
        }
    }
}

Security Considerations for Web Components

// Security analyzer for Web Components

class WebComponentSecurityAnalyzer {
    constructor() {
        this.vulnerabilities = [];
        this.trustedTypes = 'trustedTypes' in window;
    }
    
    analyzeSecurityRisks() {
        console.log('= Web Component Security Analysis\n');
        
        // Check for innerHTML usage
        this.checkInnerHTMLUsage();
        
        // Check for eval or Function constructor
        this.checkDynamicCodeExecution();
        
        // Check Shadow DOM boundaries
        this.checkShadowDOMSecurity();
        
        // Check for XSS vulnerabilities
        this.checkXSSVulnerabilities();
        
        // Generate security report
        this.generateSecurityReport();
    }
    
    checkInnerHTMLUsage() {
        const components = document.querySelectorAll('*');
        
        components.forEach(component => {
            if (component.tagName.includes('-')) {
                // Check component definition
                const proto = Object.getPrototypeOf(component);
                const methods = Object.getOwnPropertyNames(proto);
                
                methods.forEach(method => {
                    if (typeof component[method] === 'function') {
                        const fnString = component[method].toString();
                        
                        if (fnString.includes('innerHTML')) {
                            this.vulnerabilities.push({
                                type: 'innerHTML',
                                component: component.tagName,
                                method: method,
                                severity: 'high',
                                recommendation: 'Use textContent or createElement instead'
                            });
                        }
                    }
                });
            }
        });
    }
    
    checkDynamicCodeExecution() {
        const dangerous = ['eval', 'Function', 'setTimeout', 'setInterval'];
        
        document.querySelectorAll('script').forEach(script => {
            const content = script.textContent;
            
            dangerous.forEach(fn => {
                if (content.includes(fn + '(')) {
                    this.vulnerabilities.push({
                        type: 'dynamic-code',
                        function: fn,
                        severity: 'critical',
                        recommendation: 'Avoid dynamic code execution'
                    });
                }
            });
        });
    }
    
    checkShadowDOMSecurity() {
        const shadowHosts = [];
        
        document.querySelectorAll('*').forEach(el => {
            if (el.shadowRoot) {
                shadowHosts.push({
                    element: el,
                    mode: el.shadowRoot.mode
                });
                
                // Check for form elements in shadow DOM
                const forms = el.shadowRoot.querySelectorAll('form, input, textarea');
                if (forms.length > 0) {
                    this.vulnerabilities.push({
                        type: 'shadow-form',
                        component: el.tagName,
                        severity: 'medium',
                        recommendation: 'Ensure proper form validation in Shadow DOM'
                    });
                }
            }
        });
    }
    
    checkXSSVulnerabilities() {
        // Check for attribute reflection
        const components = document.querySelectorAll('*');
        
        components.forEach(component => {
            if (component.tagName.includes('-')) {
                // Check for dangerous attribute names
                const dangerousAttrs = ['href', 'src', 'action', 'formaction'];
                
                dangerousAttrs.forEach(attr => {
                    if (component.hasAttribute(attr)) {
                        const value = component.getAttribute(attr);
                        if (value.startsWith('javascript:')) {
                            this.vulnerabilities.push({
                                type: 'xss-attribute',
                                component: component.tagName,
                                attribute: attr,
                                severity: 'critical',
                                recommendation: 'Sanitize attribute values'
                            });
                        }
                    }
                });
            }
        });
    }
    
    generateSecurityReport() {
        console.log('=� Security Report:\n');
        
        if (this.vulnerabilities.length === 0) {
            console.log(' No major security vulnerabilities detected');
        } else {
            console.log(`� Found ${this.vulnerabilities.length} potential vulnerabilities:\n`);
            
            // Group by severity
            const bySeverity = {
                critical: [],
                high: [],
                medium: [],
                low: []
            };
            
            this.vulnerabilities.forEach(vuln => {
                bySeverity[vuln.severity].push(vuln);
            });
            
            Object.entries(bySeverity).forEach(([severity, vulns]) => {
                if (vulns.length > 0) {
                    console.log(`${severity.toUpperCase()} (${vulns.length}):`);
                    vulns.forEach(vuln => {
                        console.log(`  - ${vuln.type}: ${vuln.component || vuln.function}`);
                        console.log(`    ${vuln.recommendation}`);
                    });
                }
            });
        }
        
        // Best practices
        console.log('\n= Security Best Practices:');
        console.log('  1. Use Trusted Types API when available');
        console.log('  2. Sanitize all user inputs');
        console.log('  3. Avoid innerHTML, use textContent');
        console.log('  4. Use closed Shadow DOM for sensitive components');
        console.log('  5. Implement Content Security Policy');
    }
}

// Run security analyzer
const securityAnalyzer = new WebComponentSecurityAnalyzer();
securityAnalyzer.analyzeSecurityRisks();

Performance Optimization Strategies

// Web Component performance optimizer

class WebComponentOptimizer {
    static optimizationStrategies = {
        lazyDefine() {
            // Lazy define components only when needed
            const lazyDefine = (tagName, componentModule) => {
                const observer = new IntersectionObserver((entries) => {
                    entries.forEach(entry => {
                        if (entry.isIntersecting && !customElements.get(tagName)) {
                            import(componentModule).then(module => {
                                customElements.define(tagName, module.default);
                            });
                            observer.disconnect();
                        }
                    });
                });
                
                document.querySelectorAll(tagName).forEach(el => {
                    observer.observe(el);
                });
            };
            
            return lazyDefine;
        },
        
        shadowDOMCache() {
            // Cache shadow DOM templates
            const templateCache = new Map();
            
            return {
                get(templateId) {
                    if (!templateCache.has(templateId)) {
                        const template = document.getElementById(templateId);
                        if (template) {
                            templateCache.set(templateId, template.content.cloneNode(true));
                        }
                    }
                    return templateCache.get(templateId)?.cloneNode(true);
                }
            };
        },
        
        adoptedStylesheetsOptimization() {
            // Share stylesheets across components
            const sharedStyles = new Map();
            
            return {
                getStylesheet(css) {
                    if (!sharedStyles.has(css)) {
                        const sheet = new CSSStyleSheet();
                        sheet.replaceSync(css);
                        sharedStyles.set(css, sheet);
                    }
                    return sharedStyles.get(css);
                }
            };
        },
        
        propertyChangeDebounce() {
            // Debounce property changes
            return function debounce(fn, delay) {
                let timeoutId;
                return function(...args) {
                    clearTimeout(timeoutId);
                    timeoutId = setTimeout(() => fn.apply(this, args), delay);
                };
            };
        }
    };
}

// Example optimized component
class OptimizedComponent extends HTMLElement {
    static template = WebComponentOptimizer.optimizationStrategies.shadowDOMCache();
    static styles = WebComponentOptimizer.optimizationStrategies.adoptedStylesheetsOptimization();
    
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        
        // Use cached template
        const template = OptimizedComponent.template.get('optimized-template');
        if (template) {
            this.shadowRoot.appendChild(template);
        }
        
        // Use shared stylesheet
        const styles = OptimizedComponent.styles.getStylesheet(`
            :host {
                display: block;
                padding: 1rem;
            }
        `);
        this.shadowRoot.adoptedStyleSheets = [styles];
    }
    
    // Debounced property setter
    set value(val) {
        this._value = val;
        this.debouncedUpdate();
    }
    
    debouncedUpdate = WebComponentOptimizer.optimizationStrategies
        .propertyChangeDebounce()(this.update.bind(this), 100);
    
    update() {
        // Expensive update operation
        this.render();
    }
}

Real-World Examples

1. YouTube (Polymer Elements)

YouTube uses Web Components extensively for video players and UI components, achieving consistent behavior across different pages.

2. GitHub (Custom Elements)

GitHub's interface uses Web Components for complex UI elements like file trees and code editors.

3. Material Design Components

Google's Material Design Web Components provide reusable, accessible components following Material Design guidelines.

Best Practices Summary

// Web Components best practices checklist

const webComponentsBestPractices = {
    naming: [
        'Use lowercase with hyphens (kebab-case)',
        'Include a namespace prefix (e.g., "app-button")',
        'Be descriptive but concise'
    ],
    
    shadowDOM: [
        'Use "open" mode unless security requires "closed"',
        'Provide ::part() for styling flexibility',
        'Use slots for content projection',
        'Include default slot content'
    ],
    
    performance: [
        'Lazy load component definitions',
        'Use adoptedStyleSheets for shared styles',
        'Minimize shadow DOM rebuilds',
        'Debounce property updates'
    ],
    
    accessibility: [
        'Implement proper ARIA attributes',
        'Ensure keyboard navigation',
        'Provide focus management',
        'Test with screen readers'
    ],
    
    security: [
        'Avoid innerHTML with user content',
        'Sanitize attribute values',
        'Use Trusted Types when available',
        'Validate all inputs'
    ]
};

console.log('=� Web Components Best Practices:', webComponentsBestPractices);

Conclusion

Web Components and Shadow DOM provide powerful encapsulation and reusability features for modern web development. By understanding how to detect, analyze, and optimize these technologies, developers can build more maintainable and performant applications. Regular analysis helps ensure components follow best practices for security, performance, and accessibility.