const fs = require('fs'); const path = require('path'); class CSSAnalyzer { constructor() { this.issues = []; this.warnings = []; this.recommendations = []; this.browserSupport = { customProperties: { ie: false, edge: '16+', chrome: '49+', firefox: '31+', safari: '9.1+' }, gridLayout: { ie: '10+ (partial)', edge: '16+', chrome: '57+', firefox: '52+', safari: '10.1+' }, flexbox: { ie: '11+', edge: '12+', chrome: '29+', firefox: '28+', safari: '9+' }, transforms: { ie: '9+', edge: '12+', chrome: '36+', firefox: '16+', safari: '9+' }, transitions: { ie: '10+', edge: '12+', chrome: '26+', firefox: '16+', safari: '6.1+' }, calc: { ie: '9+', edge: '12+', chrome: '26+', firefox: '16+', safari: '7+' } }; } analyzeFile(filePath) { console.log(`\n๐Ÿ” Analyzing: ${path.basename(filePath)}`); console.log('='.repeat(50)); try { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.split('\n'); this.checkCustomProperties(content, filePath); this.checkResponsiveDesign(content, filePath); this.checkAccessibility(content, filePath); this.checkBrowserCompatibility(content, filePath); this.checkPerformance(content, filePath); this.checkCodeQuality(content, filePath); } catch (error) { console.error(`โŒ Error reading ${filePath}:`, error.message); } } checkCustomProperties(content, filePath) { const customProps = content.match(/--[\w-]+:/g); const useCustomProps = content.match(/var\(--[\w-]+\)/g); if (customProps) { console.log(`โœ… CSS Custom Properties: ${customProps.length} defined`); // Check for fallbacks const withFallbacks = content.match(/var\(--[\w-]+,\s*[^)]+\)/g); if (withFallbacks) { console.log(`โœ… Fallbacks provided: ${withFallbacks.length}`); } else if (useCustomProps) { this.warnings.push({ file: filePath, issue: 'CSS Custom Properties used without fallbacks', impact: 'Will break in IE and older browsers', recommendation: 'Add fallback values: var(--color-primary, #0073aa)' }); console.log(`โš ๏ธ No fallbacks for custom properties (${useCustomProps.length} uses)`); } } } checkResponsiveDesign(content, filePath) { const mediaQueries = content.match(/@media[^{]*{/g); const breakpoints = content.match(/\(max-width:\s*(\d+(?:\.\d+)?)(px|em|rem)\)/g); if (mediaQueries) { console.log(`โœ… Media Queries: ${mediaQueries.length} found`); if (breakpoints) { const uniqueBreakpoints = [...new Set(breakpoints)]; console.log(`๐Ÿ“ฑ Breakpoints: ${uniqueBreakpoints.join(', ')}`); // Check for common breakpoints const commonBreakpoints = ['768px', '480px', '1024px', '767px']; const hasStandardBreakpoints = commonBreakpoints.some(bp => content.includes(bp) ); if (hasStandardBreakpoints) { console.log(`โœ… Uses standard breakpoints`); } else { this.recommendations.push({ file: filePath, recommendation: 'Consider using standard breakpoints (768px, 1024px) for better consistency' }); } } } else { this.warnings.push({ file: filePath, issue: 'No responsive design detected', impact: 'May not work well on mobile devices' }); console.log(`โš ๏ธ No media queries found`); } } checkAccessibility(content, filePath) { const focusStyles = content.match(/:focus[^{]*{/g); const skipLinks = content.includes('skip-link'); const highContrastSupport = content.includes('prefers-color-scheme') || content.includes('high-contrast'); if (focusStyles) { console.log(`โœ… Focus Styles: ${focusStyles.length} found`); } else { this.issues.push({ file: filePath, issue: 'No focus styles defined', impact: 'Poor keyboard navigation accessibility', severity: 'high' }); console.log(`โŒ No focus styles found`); } if (skipLinks) { console.log(`โœ… Skip links implemented`); } if (highContrastSupport) { console.log(`โœ… High contrast support detected`); } // Check for sufficient color contrast (basic check) const colorValues = content.match(/#[0-9a-fA-F]{3,6}/g); if (colorValues) { console.log(`๐ŸŽจ Color values: ${[...new Set(colorValues)].length} unique colors`); } } checkBrowserCompatibility(content, filePath) { const features = { 'grid': /display:\s*grid/g, 'flexbox': /display:\s*flex/g, 'transforms': /transform:/g, 'transitions': /transition:/g, 'calc': /calc\(/g, 'customProperties': /var\(/g }; console.log(`๐ŸŒ Browser Compatibility Check:`); Object.entries(features).forEach(([feature, regex]) => { const matches = content.match(regex); if (matches) { const support = this.browserSupport[feature]; console.log(` ${feature}: ${matches.length} uses - ${JSON.stringify(support)}`); if (feature === 'customProperties' && !content.includes('var(--')) { // This is a false positive, skip return; } if (support.ie === false || support.ie.includes('partial')) { this.warnings.push({ file: filePath, issue: `${feature} has limited IE support`, recommendation: 'Consider progressive enhancement or fallbacks' }); } } }); } checkPerformance(content, filePath) { const selectors = content.match(/[^{}]+{/g); const universalSelectors = content.match(/\*[^{]*/g); const deepSelectors = content.match(/[^{]+>\s*[^{]+>\s*[^{]+{/g); if (selectors) { console.log(`โšก Performance Analysis:`); console.log(` Total selectors: ${selectors.length}`); if (universalSelectors) { console.log(` โš ๏ธ Universal selectors: ${universalSelectors.length}`); this.warnings.push({ file: filePath, issue: 'Universal selectors detected', impact: 'May impact performance', recommendation: 'Use more specific selectors when possible' }); } if (deepSelectors) { console.log(` Deep selectors (3+ levels): ${deepSelectors.length}`); if (deepSelectors.length > 10) { this.recommendations.push({ file: filePath, recommendation: 'Consider reducing selector depth for better performance' }); } } } // Check file size const sizeKB = Math.round(content.length / 1024); console.log(` File size: ${sizeKB}KB`); if (sizeKB > 50) { this.recommendations.push({ file: filePath, recommendation: 'Consider minification and compression for production' }); } } checkCodeQuality(content, filePath) { const duplicateProperties = this.findDuplicateProperties(content); const unusedVariables = this.findUnusedVariables(content); const inconsistentNaming = this.checkNamingConsistency(content); console.log(`๐Ÿงน Code Quality:`); if (duplicateProperties.length > 0) { console.log(` โš ๏ธ Duplicate properties: ${duplicateProperties.length}`); this.issues.push({ file: filePath, issue: 'Duplicate CSS properties found', details: duplicateProperties }); } else { console.log(` โœ… No duplicate properties`); } if (unusedVariables.length > 0) { console.log(` โš ๏ธ Potentially unused variables: ${unusedVariables.length}`); } // Check for vendor prefixes const vendorPrefixes = content.match(/-webkit-|-moz-|-ms-|-o-/g); if (vendorPrefixes) { console.log(` ๐Ÿ”ง Vendor prefixes: ${vendorPrefixes.length}`); } } findDuplicateProperties(content) { // This is a simplified check - in reality, we'd need a CSS parser const rules = content.match(/{[^}]+}/g) || []; const duplicates = []; rules.forEach(rule => { const properties = rule.match(/[\w-]+\s*:/g) || []; const propertyNames = properties.map(p => p.replace(/\s*:$/, '')); const seen = new Set(); propertyNames.forEach(prop => { if (seen.has(prop)) { duplicates.push(prop); } seen.add(prop); }); }); return [...new Set(duplicates)]; } findUnusedVariables(content) { const defined = content.match(/--[\w-]+/g) || []; const used = content.match(/var\(--[\w-]+/g) || []; const definedClean = defined.map(v => v.replace('--', '')); const usedClean = used.map(v => v.replace('var(--', '')); return definedClean.filter(v => !usedClean.includes(v)); } checkNamingConsistency(content) { const variables = content.match(/--[\w-]+/g) || []; const classes = content.match(/\.[\w-]+/g) || []; // Check for consistent naming patterns const kebabCase = /^[a-z][a-z0-9-]*$/; const camelCase = /^[a-z][a-zA-Z0-9]*$/; const inconsistent = []; [...variables, ...classes].forEach(name => { const clean = name.replace(/^(--|\.)/g, ''); if (!kebabCase.test(clean) && !camelCase.test(clean)) { inconsistent.push(name); } }); return inconsistent; } generateReport() { console.log('\n' + '='.repeat(80)); console.log('๐Ÿ“‹ COMPREHENSIVE CSS ANALYSIS REPORT'); console.log('='.repeat(80)); if (this.issues.length > 0) { console.log('\nโŒ CRITICAL ISSUES:'); this.issues.forEach((issue, i) => { console.log(`${i + 1}. ${issue.issue}`); console.log(` File: ${path.basename(issue.file)}`); if (issue.impact) console.log(` Impact: ${issue.impact}`); if (issue.recommendation) console.log(` Fix: ${issue.recommendation}`); console.log(''); }); } if (this.warnings.length > 0) { console.log('\nโš ๏ธ WARNINGS:'); this.warnings.forEach((warning, i) => { console.log(`${i + 1}. ${warning.issue}`); console.log(` File: ${path.basename(warning.file)}`); if (warning.impact) console.log(` Impact: ${warning.impact}`); if (warning.recommendation) console.log(` Recommendation: ${warning.recommendation}`); console.log(''); }); } if (this.recommendations.length > 0) { console.log('\n๐Ÿ’ก RECOMMENDATIONS:'); this.recommendations.forEach((rec, i) => { console.log(`${i + 1}. ${rec.recommendation}`); console.log(` File: ${path.basename(rec.file)}`); console.log(''); }); } console.log('\nโœ… SUMMARY:'); console.log(`Total Issues: ${this.issues.length}`); console.log(`Total Warnings: ${this.warnings.length}`); console.log(`Total Recommendations: ${this.recommendations.length}`); } } // Main execution const analyzer = new CSSAnalyzer(); const cssFiles = [ './assets/css/hvac-common.css', './assets/css/hvac-dashboard.css', './assets/css/hvac-registration.css', './assets/css/hvac-certificates.css', './assets/css/hvac-email-attendees.css', './assets/css/hvac-event-summary.css', './assets/css/hvac-attendee-profile.css', './assets/css/hvac-mobile-nav.css', './assets/css/hvac-animations.css', './assets/css/hvac-print.css' ]; console.log('๐Ÿ”ฌ HVAC COMMUNITY EVENTS - CSS DEEP ANALYSIS'); console.log('Starting comprehensive CSS investigation...\n'); cssFiles.forEach(file => { if (fs.existsSync(file)) { analyzer.analyzeFile(file); } else { console.log(`โš ๏ธ File not found: ${file}`); } }); analyzer.generateReport();