UNPKG

gbu-accessibility-package

Version:

Comprehensive accessibility fixes and project optimization for HTML files. Smart context-aware alt text generation, form labels, button names, link names, landmarks, heading analysis, WCAG-compliant role attributes, unused files detection, dead code analy

163 lines (143 loc) 5 kB
/** * Accessibility Enhancer * JavaScript enhancements for better accessibility */ class AccessibilityEnhancer { constructor(config = {}) { this.config = { language: config.language || 'ja', skipLinkText: config.skipLinkText || 'メインコンテンツにスキップ', ...config }; } // Generate JavaScript code for accessibility enhancements generateEnhancementScript() { return ` /** * Accessibility Enhancements * Auto-generated by Accessibility Toolkit */ (function () { 'use strict'; // Add accessibility labels to slider pagination buttons function addSliderAccessibilityLabels() { setTimeout(function () { // Find all pagination containers const paginationContainers = document.querySelectorAll('.splide-custom-pagination, .pagination'); paginationContainers.forEach(function (container) { const buttons = container.querySelectorAll('button'); buttons.forEach(function (button, index) { const slideNumber = index + 1; if (!button.getAttribute('aria-label')) { button.setAttribute('aria-label', 'スライド' + slideNumber + 'を表示'); } }); }); // Handle pagination buttons with data-index const paginationButtons = document.querySelectorAll('[data-index]'); paginationButtons.forEach(function (button) { const index = button.getAttribute('data-index'); const slideNumber = parseInt(index) + 1; if (!button.getAttribute('aria-label')) { button.setAttribute('aria-label', 'スライド' + slideNumber + 'を表示'); } }); // Add role="img" and aria-label to picture elements in slides const sliderPictures = document.querySelectorAll('.splide__slide picture, .slide picture'); sliderPictures.forEach(function (picture) { if (!picture.getAttribute('role')) { picture.setAttribute('role', 'img'); } if (!picture.getAttribute('aria-label')) { const slide = picture.closest('.splide__slide, .slide'); const heading = slide ? slide.querySelector('h4, h3, h2, h1') : null; const altText = picture.querySelector('img') ? picture.querySelector('img').getAttribute('alt') : ''; let ariaLabel = ''; if (heading && heading.textContent) { ariaLabel = heading.textContent + 'の画像'; } else if (altText) { ariaLabel = altText + 'の画像'; } else { ariaLabel = 'スライド画像'; } picture.setAttribute('aria-label', ariaLabel); } }); }, 1000); } // Add skip link for keyboard navigation function addSkipLink() { const skipLink = document.createElement('a'); skipLink.href = '#main-content'; skipLink.textContent = '${this.config.skipLinkText}'; skipLink.className = 'skip-link sr-only'; skipLink.style.cssText = \` position: absolute; top: -40px; left: 6px; background: #000; color: #fff; padding: 8px; text-decoration: none; z-index: 1000; border-radius: 4px; font-size: 14px; \`; skipLink.addEventListener('focus', function () { this.style.top = '6px'; }); skipLink.addEventListener('blur', function () { this.style.top = '-40px'; }); document.body.insertBefore(skipLink, document.body.firstChild); } // Add main content ID for skip link target function addMainContentId() { const mainElement = document.querySelector('main'); if (mainElement && !mainElement.id) { mainElement.id = 'main-content'; } } // Fix missing form labels function fixFormLabels() { const inputs = document.querySelectorAll('input[type="text"], input[type="email"], input[type="tel"], textarea'); inputs.forEach(function(input) { if (!input.getAttribute('aria-label') && !input.getAttribute('aria-labelledby')) { const placeholder = input.getAttribute('placeholder'); if (placeholder) { input.setAttribute('aria-label', placeholder); } } }); } // Initialize all enhancements function init() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { addSkipLink(); addMainContentId(); addSliderAccessibilityLabels(); fixFormLabels(); }); } else { addSkipLink(); addMainContentId(); addSliderAccessibilityLabels(); fixFormLabels(); } // Run again after delay for dynamic content setTimeout(addSliderAccessibilityLabels, 2000); } init(); })(); `; } // Save enhancement script to file async saveEnhancementScript(outputPath) { const fs = require('fs').promises; const script = this.generateEnhancementScript(); await fs.writeFile(outputPath, script); return outputPath; } } module.exports = AccessibilityEnhancer;