UNPKG

@casoon/auditmysite

Version:

Professional website analysis suite with robust accessibility testing, Core Web Vitals performance monitoring, SEO analysis, and content optimization insights. Features isolated browser contexts, retry mechanisms, and comprehensive API endpoints for profe

1,326 lines (1,130 loc) 39.5 kB
/** * Comprehensive HTML Template from main branch * Features: Full sticky navigation, Performance/SEO sections, Mobile-Friendliness, AI-friendly copy */ function getComprehensiveHtmlTemplate(data) { const { domain = 'Unknown', testedPages = 0, totalPages = 0, totalErrors = 0, successRate = 0, totalDuration = '0s', groupedAccessibilityIssues = [], performanceResults = [], seoResults = [] } = data; return ` <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Accessibility Test Report - {{domain}}</title> <meta name="description" content="Comprehensive accessibility test report generated by auditmysite"> <!-- Favicon --> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%232563eb'/><text x='50' y='65' text-anchor='middle' fill='white' font-size='40' font-weight='bold'>A</text></svg>"> <!-- CSS --> <style> /* CSS Variables für Theming */ :root { --primary-color: #2563eb; --secondary-color: #64748b; --success-color: #10b981; --warning-color: #f59e0b; --error-color: #ef4444; --background-color: #ffffff; --surface-color: #f8fafc; --text-primary: #1e293b; --text-secondary: #64748b; --border-color: #e2e8f0; --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); } /* Dark Mode */ @media (prefers-color-scheme: dark) { :root { --background-color: #0f172a; --surface-color: #1e293b; --text-primary: #f1f5f9; --text-secondary: #94a3b8; --border-color: #334155; } } /* Reset und Base Styles */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; color: var(--text-primary); background-color: var(--background-color); transition: background-color 0.3s ease; } /* Header - Only Filter Badges */ .report-header { background: #f8fafc; color: #1e293b; padding: 0.75rem 0; box-shadow: var(--shadow-lg); position: sticky; top: 0; z-index: 100; border-bottom: 1px solid #e2e8f0; height: 60px; } /* Filter Section - Now the main content */ .filter-section { background: var(--surface-color); padding: 0; height: 100%; display: flex; align-items: center; } .filter-container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; } .header-content { display: flex; align-items: center; justify-content: center; width: 100%; } .filter-badges { display: flex; list-style: none; gap: 1rem; flex-wrap: wrap; justify-content: center; } .filter-badge { background: white; color: var(--text-primary); border: 2px solid #e2e8f0; padding: 0.5rem 1rem; border-radius: 2rem; cursor: pointer; transition: all 0.2s ease; font-size: 0.875rem; font-weight: 500; user-select: none; box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); } .filter-badge:hover { background: var(--primary-color); color: white; border-color: var(--primary-color); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); } .filter-badge.active { background: var(--primary-color); color: white; border-color: var(--primary-color); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); } .filter-badge.inactive { opacity: 0.6; background: #f8fafc; } /* Main Content */ .main-content { max-width: 1200px; margin: 0 auto; padding: 2rem 1rem; } /* Dashboard */ .dashboard { margin-bottom: 3rem; } .kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; } .kpi-card { background: var(--surface-color); padding: 1.5rem; border-radius: 0.75rem; box-shadow: var(--shadow); border: 1px solid var(--border-color); transition: transform 0.2s ease, box-shadow 0.2s ease; } .kpi-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); } .kpi-card h3 { color: var(--text-secondary); font-size: 0.875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; } .kpi-value { font-size: 2rem; font-weight: bold; margin-bottom: 0.5rem; } .kpi-trend { font-size: 0.875rem; font-weight: 500; } .kpi-trend.positive { color: var(--success-color); } .kpi-trend.negative { color: var(--error-color); } /* Table Styles - Simplified without borders */ .table-container { background: var(--surface-color); border-radius: 0.75rem; margin-bottom: 2rem; overflow: hidden; } .table-header { display: flex; justify-content: space-between; align-items: center; padding: 1.5rem; border-bottom: 1px solid var(--border-color); background: var(--background-color); } .table-header h3 { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); } .copy-btn { background: var(--primary-color); color: white; border: none; padding: 0.5rem 1rem; border-radius: 0.5rem; cursor: pointer; font-size: 0.875rem; font-weight: 500; transition: background-color 0.2s ease; display: flex; align-items: center; gap: 0.5rem; } .copy-btn:hover { background: #1d4ed8; } .copy-btn:active { transform: translateY(1px); } .table-wrapper { overflow-x: auto; } .data-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; } .data-table th { background: var(--background-color); padding: 1rem; text-align: left; font-weight: 600; color: var(--text-primary); border-bottom: 2px solid var(--border-color); position: sticky; top: 0; z-index: 10; } .data-table th:first-child { padding-top: 1rem; } .data-table td { padding: 1rem; border-bottom: 1px solid var(--border-color); color: var(--text-primary); } .data-table tr:hover { background: var(--background-color); } .data-table tr.error { background: rgba(239, 68, 68, 0.05); } .data-table tr.warning { background: rgba(245, 158, 11, 0.05); } .data-table tr.error:hover { background: rgba(239, 68, 68, 0.1); } .data-table tr.warning:hover { background: rgba(245, 158, 11, 0.1); } /* Report Section styling */ .report-section { margin-bottom: 3rem; transition: all 0.3s ease; } .report-section.hidden { display: none; } .section-header { margin-bottom: 2rem; text-align: left; } .section-content { /* Removed background and border to avoid nesting */ border-radius: 0.75rem; overflow: hidden; } .section-title { font-size: 1.5rem; font-weight: 700; color: var(--text-primary); margin-bottom: 0.5rem; display: flex; align-items: center; gap: 0.5rem; } .section-description { color: var(--text-secondary); font-size: 0.95rem; margin-bottom: 1.5rem; font-style: italic; } /* Page info styling for SEO section */ .page-info { line-height: 1.4; } .page-info strong { color: var(--text-primary); font-size: 0.95rem; } .page-title { color: var(--text-secondary); font-size: 0.85rem; margin-top: 0.25rem; } .page-url { color: var(--text-secondary); font-size: 0.75rem; opacity: 0.7; margin-top: 0.125rem; } /* No Data States */ .no-data, .no-issues { text-align: center; padding: 3rem 1rem; color: var(--text-secondary); } .no-data h3, .no-issues h3 { font-size: 1.5rem; margin-bottom: 1rem; color: var(--text-primary); } /* Toast Notification */ .toast { position: fixed; top: 2rem; right: 2rem; background: var(--success-color); color: white; padding: 1rem 1.5rem; border-radius: 0.5rem; box-shadow: var(--shadow-lg); z-index: 1000; transform: translateX(100%); transition: transform 0.3s ease; } .toast.show { transform: translateX(0); } /* Detailed Issues Styles */ .detailed-issues-container { padding: 0; } .issues-summary { background: var(--surface-color); border-radius: 0.75rem; padding: 1.5rem; margin-bottom: 2rem; border: 1px solid var(--border-color); } .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1rem; margin-top: 1rem; } .summary-card { background: white; border-radius: 0.5rem; padding: 1rem; text-align: center; border: 2px solid var(--border-color); transition: all 0.2s ease; } .summary-card.error { border-color: var(--error-color); background: rgba(239, 68, 68, 0.05); } .summary-card.warning { border-color: var(--warning-color); background: rgba(245, 158, 11, 0.05); } .summary-card.notice { border-color: #6b7280; background: rgba(107, 114, 128, 0.05); } .summary-card.total { border-color: var(--primary-color); background: rgba(37, 99, 235, 0.05); } .summary-number { font-size: 1.75rem; font-weight: bold; margin-bottom: 0.25rem; } .summary-label { font-size: 0.875rem; color: var(--text-secondary); font-weight: 500; } .category-section { background: var(--surface-color); border-radius: 0.75rem; margin-bottom: 2rem; border: 1px solid var(--border-color); overflow: hidden; } .category-header { background: var(--background-color); padding: 1.5rem; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center; } .category-title { font-size: 1.25rem; font-weight: 600; color: var(--text-primary); margin: 0; } .issue-count { font-weight: normal; color: var(--text-secondary); font-size: 1rem; } .copy-category-btn { background: var(--primary-color); color: white; border: none; padding: 0.5rem 1rem; border-radius: 0.375rem; cursor: pointer; font-size: 0.875rem; font-weight: 500; transition: background-color 0.2s ease; } .copy-category-btn:hover { background: #1d4ed8; } .page-issues-section { border-bottom: 1px solid var(--border-color); } .page-issues-section:last-child { border-bottom: none; } .page-header { background: rgba(248, 250, 252, 0.5); padding: 1rem 1.5rem; border-bottom: 1px solid var(--border-color); } .page-title { font-size: 1.1rem; font-weight: 600; color: var(--text-primary); margin: 0 0 0.25rem 0; } .page-meta { display: flex; justify-content: space-between; align-items: center; font-size: 0.875rem; } .page-url { color: var(--text-secondary); word-break: break-all; } .page-issue-count { color: var(--text-secondary); background: white; padding: 0.25rem 0.5rem; border-radius: 0.25rem; border: 1px solid var(--border-color); } .issues-list { padding: 0; } .issue-item { padding: 1.5rem; border-bottom: 1px solid var(--border-color); transition: background-color 0.2s ease; } .issue-item:hover { background: rgba(248, 250, 252, 0.8); } .issue-item:last-child { border-bottom: none; } .issue-item.error { border-left: 4px solid var(--error-color); background: rgba(239, 68, 68, 0.02); } .issue-item.warning { border-left: 4px solid var(--warning-color); background: rgba(245, 158, 11, 0.02); } .issue-item.notice { border-left: 4px solid #6b7280; background: rgba(107, 114, 128, 0.02); } .issue-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; } .issue-type-badge { font-size: 0.75rem; font-weight: 600; padding: 0.25rem 0.5rem; border-radius: 0.25rem; text-transform: uppercase; } .issue-type-badge.error { background: var(--error-color); color: white; } .issue-type-badge.warning { background: var(--warning-color); color: white; } .issue-type-badge.notice { background: #6b7280; color: white; } .issue-source { font-size: 0.75rem; color: var(--text-secondary); font-weight: 500; background: var(--background-color); padding: 0.25rem 0.5rem; border-radius: 0.25rem; border: 1px solid var(--border-color); } .copy-issue-btn { background: none; border: 1px solid var(--border-color); padding: 0.25rem 0.5rem; border-radius: 0.25rem; cursor: pointer; color: var(--text-secondary); font-size: 0.875rem; transition: all 0.2s ease; } .copy-issue-btn:hover { background: var(--primary-color); color: white; border-color: var(--primary-color); } .issue-content { line-height: 1.6; } .issue-message { font-size: 1rem; color: var(--text-primary); margin-bottom: 0.75rem; font-weight: 500; } .issue-code, .issue-selector, .issue-context, .issue-help, .issue-help-url { margin-bottom: 0.5rem; font-size: 0.875rem; line-height: 1.5; } .issue-selector code, .issue-context code { background: var(--background-color); border: 1px solid var(--border-color); padding: 0.125rem 0.25rem; border-radius: 0.25rem; font-size: 0.8rem; word-break: break-all; } .issue-help-url a { color: var(--primary-color); text-decoration: none; } .issue-help-url a:hover { text-decoration: underline; } /* Enhanced Performance and SEO Section Styles */ .performance-overview, .seo-overview { margin-bottom: 2rem; } .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin: 1.5rem 0; } .metric-card { background: white; border-radius: 0.5rem; padding: 1.25rem; text-align: center; border: 2px solid var(--border-color); transition: all 0.2s ease; } .metric-card.excellent { border-color: var(--success-color); background: rgba(16, 185, 129, 0.05); } .metric-card.good { border-color: #10b981; background: rgba(16, 185, 129, 0.03); } .metric-card.needs-improvement { border-color: var(--warning-color); background: rgba(245, 158, 11, 0.05); } .metric-card.poor { border-color: var(--error-color); background: rgba(239, 68, 68, 0.05); } .metric-label { font-size: 0.8rem; color: var(--text-secondary); font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.5rem; } .metric-value { font-size: 1.5rem; font-weight: bold; margin-bottom: 0.25rem; } .metric-grade { font-size: 0.875rem; font-weight: 500; padding: 0.25rem 0.5rem; border-radius: 0.25rem; display: inline-block; } .metric-grade.A { background: var(--success-color); color: white; } .metric-grade.B { background: #10b981; color: white; } .metric-grade.C { background: var(--warning-color); color: white; } .metric-grade.D { background: #ef4444; color: white; } .metric-grade.F { background: #991b1b; color: white; } .content-breakdown { margin: 2rem 0; } .breakdown-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; color: var(--text-primary); } .breakdown-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; } .breakdown-item { background: var(--background-color); border: 1px solid var(--border-color); border-radius: 0.5rem; padding: 1rem; text-align: center; } .breakdown-type { font-size: 0.75rem; color: var(--text-secondary); font-weight: 600; text-transform: uppercase; margin-bottom: 0.5rem; } .breakdown-size { font-size: 1.25rem; font-weight: bold; color: var(--text-primary); margin-bottom: 0.25rem; } .breakdown-percentage { font-size: 0.875rem; color: var(--text-secondary); } .vitals-table, .resources-table, .meta-tags-table, .content-analysis-table, .technical-seo-table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.875rem; } .vitals-table th, .resources-table th, .meta-tags-table th, .content-analysis-table th, .technical-seo-table th { background: var(--surface-color); padding: 0.75rem; text-align: left; font-weight: 600; color: var(--text-primary); border-bottom: 2px solid var(--border-color); } .vitals-table td, .resources-table td, .meta-tags-table td, .content-analysis-table td, .technical-seo-table td { padding: 0.75rem; border-bottom: 1px solid var(--border-color); color: var(--text-primary); } .vitals-table tr:hover, .resources-table tr:hover, .meta-tags-table tr:hover, .content-analysis-table tr:hover, .technical-seo-table tr:hover { background: rgba(248, 250, 252, 0.8); } .score-cell { text-align: center; font-weight: 600; } .score-excellent { color: var(--success-color); } .score-good { color: #10b981; } .score-needs-improvement { color: var(--warning-color); } .score-poor { color: var(--error-color); } .grade-badge { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.75rem; font-weight: 600; text-align: center; min-width: 2rem; } .grade-badge.A { background: var(--success-color); color: white; } .grade-badge.B { background: #10b981; color: white; } .grade-badge.C { background: var(--warning-color); color: white; } .grade-badge.D { background: #ef4444; color: white; } .grade-badge.F { background: #991b1b; color: white; } .recommendations-section { margin-top: 2rem; padding: 1.5rem; background: var(--surface-color); border-radius: 0.75rem; border: 1px solid var(--border-color); } .recommendations-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; color: var(--text-primary); } .recommendations-list { list-style: none; padding: 0; } .recommendation-item { margin-bottom: 0.75rem; padding: 0.75rem; background: var(--background-color); border-radius: 0.5rem; border-left: 4px solid var(--primary-color); } .recommendation-item:last-child { margin-bottom: 0; } .recommendation-priority { font-size: 0.75rem; font-weight: 600; text-transform: uppercase; margin-bottom: 0.25rem; } .recommendation-priority.high { color: var(--error-color); } .recommendation-priority.medium { color: var(--warning-color); } .recommendation-priority.low { color: var(--success-color); } .recommendation-text { font-size: 0.875rem; color: var(--text-primary); line-height: 1.5; } .status-indicator { display: inline-block; width: 0.75rem; height: 0.75rem; border-radius: 50%; margin-right: 0.5rem; } .status-pass { background: var(--success-color); } .status-fail { background: var(--error-color); } .status-warning { background: var(--warning-color); } .status-unknown { background: var(--text-secondary); } .analysis-section { margin: 2rem 0; padding: 1.5rem; background: var(--surface-color); border-radius: 0.75rem; border: 1px solid var(--border-color); } .analysis-title { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; color: var(--text-primary); } /* Responsive Design */ @media (max-width: 768px) { .nav-links { display: none; } .table-header { flex-direction: column; gap: 1rem; align-items: flex-start; } .data-table { font-size: 0.75rem; } .data-table th, .data-table td { padding: 0.75rem 0.5rem; } .summary-grid { grid-template-columns: repeat(2, 1fr); } .category-header { flex-direction: column; gap: 1rem; align-items: flex-start; } .page-meta { flex-direction: column; align-items: flex-start; gap: 0.5rem; } .issue-header { flex-wrap: wrap; gap: 0.5rem; } /* Enhanced Performance and SEO Responsive Styles */ .metrics-grid { grid-template-columns: repeat(2, 1fr); gap: 0.75rem; } .breakdown-grid { grid-template-columns: repeat(2, 1fr); gap: 0.75rem; } .metric-card { padding: 1rem; } .metric-value { font-size: 1.25rem; } .breakdown-item { padding: 0.75rem; } .vitals-table, .resources-table, .meta-tags-table, .content-analysis-table, .technical-seo-table { font-size: 0.75rem; } .vitals-table th, .resources-table th, .meta-tags-table th, .content-analysis-table th, .technical-seo-table th { padding: 0.5rem; } .vitals-table td, .resources-table td, .meta-tags-table td, .content-analysis-table td, .technical-seo-table td { padding: 0.5rem; } .recommendations-section { padding: 1rem; } .analysis-section { padding: 1rem; } } </style> </head> <body> <header class="report-header"> <div class="filter-section"> <div class="filter-container"> <div class="header-content"> <!-- Filter badges centered --> <div class="filter-badges"> <span class="filter-badge" data-section="summary">Summary</span> <span class="filter-badge" data-section="accessibility">Accessibility</span> <span class="filter-badge" data-section="performance">Performance</span> <span class="filter-badge" data-section="seo">SEO</span> <span class="filter-badge" data-section="mobile-friendliness">Mobile-Friendliness</span> <span class="filter-badge" data-section="detailed-issues">Detailed Issues</span> </div> </div> </div> </div> </header> <main class="main-content"> <!-- Dashboard Section --> <section id="summary" class="dashboard"> <h2 class="section-title">Test Summary</h2> <div class="kpi-grid"> <div class="kpi-card"> <h3>Success Rate</h3> <div class="kpi-value">{{successRate}}%</div> <div class="kpi-trend positive">Passed</div> </div> <div class="kpi-card"> <h3>Pages Tested</h3> <div class="kpi-value">{{testedPages}}/{{totalPages}}</div> <div class="kpi-trend">Total Pages</div> </div> <div class="kpi-card"> <h3>Total Errors</h3> <div class="kpi-value">{{totalErrors}}</div> <div class="kpi-trend negative">Issues Found</div> </div> <div class="kpi-card"> <h3>Test Duration</h3> <div class="kpi-value">{{totalDuration}}</div> <div class="kpi-trend">Time Taken</div> </div> </div> </section> <!-- Accessibility Section --> <section id="accessibility" class="report-section"> <div class="section-header"> <h2 class="section-title">Accessibility Issues</h2> <p class="section-description">Web accessibility compliance and WCAG violations analysis</p> </div> <div class="section-content"> {{accessibility}} </div> </section> <!-- Performance Section --> <section id="performance" class="report-section"> <div class="section-header"> <h2 class="section-title">Performance Metrics</h2> <p class="section-description">Web page performance metrics and loading times</p> </div> <div class="section-content"> {{performance}} </div> </section> <!-- SEO Section --> <section id="seo" class="report-section"> <div class="section-header"> <h2 class="section-title">SEO Analysis</h2> <p class="section-description">Search engine optimization analysis and content structure</p> </div> <div class="section-content"> {{seo}} </div> </section> <!-- Mobile-Friendliness Section --> <section id="mobile-friendliness" class="report-section"> <div class="section-header"> <h2 class="section-title">Mobile-Friendliness Analysis</h2> <p class="section-description">Mobile usability and responsive design analysis</p> </div> <div class="section-content"> {{mobileFriendliness}} </div> </section> <!-- Detailed Issues Section - LAST --> <section id="detailed-issues" class="report-section"> <div class="section-header"> <h2 class="section-title">Detailed Issues Analysis</h2> <p class="section-description">Comprehensive breakdown of all accessibility issues grouped by category</p> </div> <div class="section-content"> {{detailedIssues}} </div> </section> </main> <!-- JavaScript für Interaktivität --> <script> // Copy to Clipboard Funktion (silent - no toast) function copyToClipboard(tableId) { const table = document.getElementById(tableId); if (!table) return; // Erstelle eine temporäre Textarea für das Kopieren const textarea = document.createElement('textarea'); textarea.value = table.innerText; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); // Silent copy - no notification } catch (err) { console.log('Copy failed'); } document.body.removeChild(textarea); } // Smooth Scrolling für Navigation function initSmoothScrolling() { document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); } // Dark Mode Toggle function initDarkMode() { const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); function updateTheme() { document.documentElement.classList.toggle('dark', prefersDark.matches); } prefersDark.addEventListener('change', updateTheme); updateTheme(); } // Filter Badge System function initFilterSystem() { const badges = document.querySelectorAll('.filter-badge'); const sections = document.querySelectorAll('.report-section, .dashboard'); badges.forEach(badge => { badge.addEventListener('click', function() { const targetSection = this.getAttribute('data-section'); // Toggle badge state this.classList.toggle('active'); // Show/hide corresponding section const section = document.getElementById(targetSection); if (section) { if (this.classList.contains('active')) { section.classList.remove('hidden'); section.style.display = 'block'; } else { section.classList.add('hidden'); section.style.display = 'none'; } } // Update badge appearance based on active state updateBadgeStates(); }); }); // Initialize: Set all badges as active and show all sections by default // (User can hide sections they don't want to see) badges.forEach(function(badge) { badge.classList.add('active'); }); sections.forEach(function(section) { section.classList.remove('hidden'); section.style.display = 'block'; }); } function updateBadgeStates() { const badges = document.querySelectorAll('.filter-badge'); const activeBadges = document.querySelectorAll('.filter-badge.active'); badges.forEach(badge => { if (activeBadges.length === 0) { badge.classList.remove('inactive'); } else { if (badge.classList.contains('active')) { badge.classList.remove('inactive'); } else { badge.classList.add('inactive'); } } }); } // Copy single issue to clipboard (AI-friendly format) function copyIssue(issueId) { const issueElement = document.getElementById(issueId); if (!issueElement) return; const aiFormatElement = issueElement.querySelector('.issue-ai-format'); if (!aiFormatElement) return; const textarea = document.createElement('textarea'); textarea.value = aiFormatElement.textContent; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); // Silent copy - no notification } catch (err) { console.log('Failed to copy issue'); } document.body.removeChild(textarea); } // Copy all issues in a category (AI-friendly format) function copyCategoryIssues(categoryId) { const categoryElement = document.getElementById('category-' + categoryId); if (!categoryElement) return; const issues = categoryElement.querySelectorAll('.issue-ai-format'); let allIssuesText = ''; // Add category header const categoryTitle = categoryElement.querySelector('.category-title'); if (categoryTitle) { allIssuesText += '# ' + categoryTitle.textContent + '\\n\\n'; } // Collect all issues issues.forEach((issueFormat, index) => { allIssuesText += '## Issue ' + (index + 1) + '\\n'; allIssuesText += issueFormat.textContent + '\\n'; }); // Add footer with AI prompt suggestion allIssuesText += '\\n---\\nPlease review these accessibility issues and provide specific code fixes for each one. Include the corrected HTML where applicable.'; const textarea = document.createElement('textarea'); textarea.value = allIssuesText; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); // Silent copy - no notification } catch (err) { console.log('Failed to copy category issues'); } document.body.removeChild(textarea); } // Initialize everything when DOM is loaded document.addEventListener('DOMContentLoaded', function() { initSmoothScrolling(); initDarkMode(); initFilterSystem(); }); </script> </body> </html>`; } module.exports = { getComprehensiveHtmlTemplate };