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

213 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Html5ElementsChecker = void 0; /** * Enhanced HTML5 Elements Checker * Utilizes new axe-core v4.10 rules for modern HTML5 element testing */ class Html5ElementsChecker { /** * Analyze modern HTML5 elements on a page * Uses Chrome 135 + axe-core v4.10 enhanced detection */ async analyzeHtml5Elements(page) { try { const analysis = await page.evaluate(() => { const results = { detailsElements: 0, summaryElements: 0, summaryWithoutName: 0, dialogElements: 0, dialogAccessibilityIssues: [], mainElements: 0, semanticStructureScore: 0, modernHtml5Usage: false, recommendations: [], elementBreakdown: { sectioning: 0, interactive: 0, semantic: 0, form: 0 } }; // Enhanced HTML5 Elements Detection // 1. Details/Summary Elements (new axe-core v4.10 rule target) const detailsElements = document.querySelectorAll('details'); const summaryElements = document.querySelectorAll('summary'); results.detailsElements = detailsElements.length; results.summaryElements = summaryElements.length; // Check for summary elements without accessible names (new axe-core rule) summaryElements.forEach(summary => { const hasAccessibleName = summary.textContent?.trim() || summary.getAttribute('aria-label') || summary.getAttribute('aria-labelledby') || summary.querySelector('img[alt]'); if (!hasAccessibleName) { results.summaryWithoutName++; } }); // 2. Dialog Elements (Chrome 135 enhanced support) const dialogElements = document.querySelectorAll('dialog'); results.dialogElements = dialogElements.length; dialogElements.forEach((dialog, index) => { // Check for proper dialog accessibility const hasRole = dialog.getAttribute('role'); const hasAriaLabel = dialog.getAttribute('aria-label') || dialog.getAttribute('aria-labelledby'); const hasAriaModal = dialog.getAttribute('aria-modal'); if (!hasAriaLabel) { results.dialogAccessibilityIssues.push(`Dialog ${index + 1}: Missing accessible name`); } if (!hasAriaModal && dialog.hasAttribute('open')) { results.dialogAccessibilityIssues.push(`Dialog ${index + 1}: Missing aria-modal attribute`); } }); // 3. Main Elements (landmark analysis) results.mainElements = document.querySelectorAll('main').length; // 4. Semantic Structure Analysis const sectioningElements = document.querySelectorAll('article, aside, nav, section, header, footer'); const interactiveElements = document.querySelectorAll('details, dialog, button, input, select, textarea'); const semanticElements = document.querySelectorAll('main, figure, figcaption, time, mark, progress, meter'); const formElements = document.querySelectorAll('fieldset, legend, datalist, output, optgroup'); results.elementBreakdown.sectioning = sectioningElements.length; results.elementBreakdown.interactive = interactiveElements.length; results.elementBreakdown.semantic = semanticElements.length; results.elementBreakdown.form = formElements.length; // 5. Modern HTML5 Usage Detection const totalModernElements = results.detailsElements + results.dialogElements + results.elementBreakdown.semantic + results.elementBreakdown.form; results.modernHtml5Usage = totalModernElements > 0; // 6. Semantic Structure Score (0-100) let score = 0; // Main element bonus (foundational) if (results.mainElements === 1) score += 20; else if (results.mainElements > 1) score += 10; // Multiple mains can be problematic // Sectioning elements bonus if (results.elementBreakdown.sectioning > 0) score += 20; // Interactive elements bonus if (results.elementBreakdown.interactive > 0) score += 15; // Modern semantic elements bonus if (results.elementBreakdown.semantic > 0) score += 15; // Form structure bonus if (results.elementBreakdown.form > 0) score += 10; // Details/Summary proper usage bonus if (results.detailsElements > 0 && results.summaryWithoutName === 0) score += 15; // Dialog proper usage bonus if (results.dialogElements > 0 && results.dialogAccessibilityIssues.length === 0) score += 5; results.semanticStructureScore = Math.min(100, score); return results; }); // Generate recommendations based on analysis analysis.recommendations = this.generateHtml5Recommendations(analysis); return analysis; } catch (error) { console.warn('HTML5 elements analysis failed:', error); return this.getDefaultAnalysis(); } } /** * Generate actionable recommendations for HTML5 elements */ generateHtml5Recommendations(analysis) { const recommendations = []; // Main element recommendations if (analysis.mainElements === 0) { recommendations.push('Add a <main> landmark to identify the primary content area'); } else if (analysis.mainElements > 1) { recommendations.push('Use only one <main> element per page for better screen reader navigation'); } // Details/Summary recommendations (new axe-core v4.10 focus) if (analysis.summaryWithoutName > 0) { recommendations.push(`Fix ${analysis.summaryWithoutName} <summary> elements lacking accessible names`); } if (analysis.detailsElements > 0 && analysis.summaryElements === 0) { recommendations.push('Each <details> element should contain a <summary> for better accessibility'); } // Dialog recommendations if (analysis.dialogAccessibilityIssues.length > 0) { recommendations.push('Fix dialog accessibility: ensure aria-label/labelledby and aria-modal attributes'); } // Semantic structure recommendations if (analysis.semanticStructureScore < 50) { recommendations.push('Improve semantic HTML structure with sectioning elements (article, aside, nav, section)'); } if (analysis.elementBreakdown.sectioning === 0) { recommendations.push('Use HTML5 sectioning elements for better document outline and screen reader navigation'); } // Modern HTML5 usage encouragement if (!analysis.modernHtml5Usage) { recommendations.push('Consider using modern HTML5 elements like <details>, <dialog>, <figure> for enhanced semantics'); } // Form structure recommendations if (analysis.elementBreakdown.form === 0 && (document.querySelector('form') || document.querySelector('input'))) { recommendations.push('Use <fieldset> and <legend> elements to group related form fields'); } return recommendations; } /** * Get default analysis in case of errors */ getDefaultAnalysis() { return { detailsElements: 0, summaryElements: 0, summaryWithoutName: 0, dialogElements: 0, dialogAccessibilityIssues: [], mainElements: 0, semanticStructureScore: 0, modernHtml5Usage: false, recommendations: ['HTML5 elements analysis unavailable - ensure page loads completely'], elementBreakdown: { sectioning: 0, interactive: 0, semantic: 0, form: 0 } }; } /** * Check if a page uses modern HTML5 patterns effectively */ async hasModernHtml5Patterns(page) { try { return await page.evaluate(() => { // Check for modern HTML5 usage patterns const modernElements = [ 'details', 'summary', 'dialog', 'main', 'article', 'section', 'aside', 'nav', 'header', 'footer', 'figure', 'figcaption', 'time', 'mark', 'progress', 'meter', 'datalist', 'output' ]; return modernElements.some(tag => document.querySelector(tag)); }); } catch (error) { return false; } } /** * Get HTML5 semantic complexity score for a page */ getSemanticComplexityLevel(analysis) { const totalElements = Object.values(analysis.elementBreakdown).reduce((sum, count) => sum + count, 0); if (totalElements === 0) return 'basic'; if (totalElements < 5) return 'basic'; if (totalElements < 15) return 'intermediate'; return 'advanced'; } } exports.Html5ElementsChecker = Html5ElementsChecker; //# sourceMappingURL=html5-elements-checker.js.map