const fs = require('fs'); const path = require('path'); class ResponsiveAnalyzer { constructor() { this.breakpoints = new Map(); this.responsivePatterns = []; this.issues = []; } analyzeResponsiveDesign() { console.log('šŸ“± RESPONSIVE DESIGN ANALYSIS'); console.log('='.repeat(50)); 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' ]; cssFiles.forEach(file => { if (fs.existsSync(file)) { this.analyzeFile(file); } }); this.generateResponsiveReport(); } analyzeFile(filePath) { const content = fs.readFileSync(filePath, 'utf8'); const fileName = path.basename(filePath); console.log(`\nšŸ” ${fileName}:`); // Extract media queries const mediaQueries = this.extractMediaQueries(content); if (mediaQueries.length > 0) { console.log(` šŸ“± ${mediaQueries.length} media queries found`); mediaQueries.forEach(mq => { console.log(` ${mq.condition} - ${mq.rules} rules`); // Track breakpoint usage const breakpoint = mq.condition; if (this.breakpoints.has(breakpoint)) { this.breakpoints.set(breakpoint, this.breakpoints.get(breakpoint) + 1); } else { this.breakpoints.set(breakpoint, 1); } }); } else { console.log(` āš ļø No media queries`); this.issues.push({ file: fileName, issue: 'No responsive design', severity: 'medium' }); } // Check for mobile-first vs desktop-first approach const mobileFirst = content.includes('min-width'); const desktopFirst = content.includes('max-width'); if (mobileFirst && desktopFirst) { console.log(` šŸ“± Mixed approach (both min-width and max-width)`); } else if (mobileFirst) { console.log(` šŸ“± Mobile-first approach (min-width)`); } else if (desktopFirst) { console.log(` šŸ–„ļø Desktop-first approach (max-width)`); } // Check for flexible units const flexibleUnits = { 'rem': (content.match(/\d+(\.\d+)?rem/g) || []).length, 'em': (content.match(/\d+(\.\d+)?em/g) || []).length, '%': (content.match(/\d+(\.\d+)?%/g) || []).length, 'vw': (content.match(/\d+(\.\d+)?vw/g) || []).length, 'vh': (content.match(/\d+(\.\d+)?vh/g) || []).length, 'px': (content.match(/\d+px/g) || []).length }; console.log(` šŸ“ Units usage:`, flexibleUnits); // Check for responsive patterns this.checkResponsivePatterns(content, fileName); } extractMediaQueries(content) { const mediaRegex = /@media[^{]*\{([^{}]*\{[^}]*\}[^{}]*)*\}/g; const queries = []; let match; while ((match = mediaRegex.exec(content)) !== null) { const fullQuery = match[0]; const condition = fullQuery.match(/@media[^{]*/)[0]; const body = fullQuery.slice(condition.length); const rules = (body.match(/[^{}]+\{[^}]*\}/g) || []).length; queries.push({ condition: condition.trim(), body: body, rules: rules }); } return queries; } checkResponsivePatterns(content, fileName) { const patterns = { 'Flexible Grid': /display:\s*(grid|flex)/g, 'Responsive Images': /max-width:\s*100%/g, 'Fluid Typography': /font-size:\s*calc\(/g, 'Container Queries': /@container/g, 'Responsive Spacing': /margin|padding.*clamp|margin|padding.*min|margin|padding.*max/g, 'Responsive Tables': /overflow-x:\s*auto/g }; Object.entries(patterns).forEach(([pattern, regex]) => { const matches = content.match(regex); if (matches) { console.log(` āœ… ${pattern}: ${matches.length} instances`); this.responsivePatterns.push({ file: fileName, pattern: pattern, count: matches.length }); } }); } generateResponsiveReport() { console.log('\n' + '='.repeat(80)); console.log('šŸ“‹ RESPONSIVE DESIGN REPORT'); console.log('='.repeat(80)); // Breakpoint analysis console.log('\nšŸ“± BREAKPOINT USAGE:'); const sortedBreakpoints = Array.from(this.breakpoints.entries()) .sort(([,a], [,b]) => b - a); sortedBreakpoints.forEach(([breakpoint, count]) => { console.log(` ${breakpoint} - Used ${count} times`); }); // Common breakpoints check const standardBreakpoints = [ '(max-width: 768px)', '(max-width: 480px)', '(max-width: 1024px)', '(min-width: 768px)', '(min-width: 1024px)' ]; console.log('\nšŸ“ BREAKPOINT CONSISTENCY:'); standardBreakpoints.forEach(bp => { if (this.breakpoints.has(bp)) { console.log(` āœ… ${bp} - Standard breakpoint used`); } else { console.log(` āš ļø ${bp} - Standard breakpoint not used`); } }); // Responsive patterns summary console.log('\nšŸŽØ RESPONSIVE PATTERNS:'); const patternSummary = new Map(); this.responsivePatterns.forEach(p => { if (patternSummary.has(p.pattern)) { patternSummary.set(p.pattern, patternSummary.get(p.pattern) + p.count); } else { patternSummary.set(p.pattern, p.count); } }); Array.from(patternSummary.entries()).forEach(([pattern, count]) => { console.log(` āœ… ${pattern}: ${count} total uses across files`); }); // Issues summary if (this.issues.length > 0) { console.log('\nāš ļø RESPONSIVE ISSUES:'); this.issues.forEach((issue, i) => { console.log(` ${i + 1}. ${issue.file}: ${issue.issue}`); }); } // Recommendations console.log('\nšŸ’” RESPONSIVE RECOMMENDATIONS:'); if (!this.breakpoints.has('(max-width: 320px)')) { console.log(' • Consider adding extra-small device support (320px)'); } if (!patternSummary.has('Fluid Typography')) { console.log(' • Consider implementing fluid typography with clamp()'); } if (!patternSummary.has('Container Queries')) { console.log(' • Consider using container queries for component-based responsive design'); } console.log(' • Test on actual devices, not just browser resize'); console.log(' • Use relative units (rem, em, %) for better scalability'); console.log(' • Implement progressive enhancement approach'); } } const analyzer = new ResponsiveAnalyzer(); analyzer.analyzeResponsiveDesign();