- Add 26 documentation files including test reports, deployment guides, and troubleshooting documentation - Include 3 CSV data files for trainer imports and user registration tracking - Add 43 JavaScript test files covering mobile optimization, Safari compatibility, and E2E testing - Include 18 PHP utility files for debugging, geocoding, and data analysis - Add 12 shell scripts for deployment verification, user management, and database operations - Update .gitignore with whitelist patterns for development files, documentation, and CSV data 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			301 lines
		
	
	
		
			No EOL
		
	
	
		
			9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			No EOL
		
	
	
		
			9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| #!/usr/bin/env node
 | |
| 
 | |
| /**
 | |
|  * HVAC Community Events CSS Focus Management Fixes
 | |
|  * 
 | |
|  * This script adds proper focus management styles to ensure WCAG 2.1 compliance
 | |
|  * and keyboard accessibility across all CSS files.
 | |
|  */
 | |
| 
 | |
| const fs = require('fs');
 | |
| const path = require('path');
 | |
| 
 | |
| const CSS_DIR = './assets/css';
 | |
| const focusRules = [];
 | |
| 
 | |
| // Focus styles to add for different element types
 | |
| const FOCUS_PATTERNS = {
 | |
|     // Interactive elements that need focus indicators
 | |
|     buttons: [
 | |
|         '.hvac-button:focus',
 | |
|         '.hvac-content .button:focus', 
 | |
|         '.hvac-content button:focus',
 | |
|         '.hvac-content input[type="submit"]:focus',
 | |
|         '.hvac-email-submit:focus',
 | |
|         '.hvac-filter-submit:focus',
 | |
|         '.hvac-certificate-actions button:focus',
 | |
|         '.hvac-certificate-actions a:focus'
 | |
|     ],
 | |
|     
 | |
|     // Form inputs
 | |
|     inputs: [
 | |
|         '.hvac-form-input:focus',
 | |
|         '.hvac-content input[type="text"]:focus',
 | |
|         '.hvac-content input[type="email"]:focus', 
 | |
|         '.hvac-content input[type="password"]:focus',
 | |
|         '.hvac-content input[type="url"]:focus',
 | |
|         '.hvac-content textarea:focus',
 | |
|         '.hvac-content select:focus',
 | |
|         '.hvac-email-form-row input:focus',
 | |
|         '.hvac-email-form-row textarea:focus',
 | |
|         '.hvac-filter-group input:focus',
 | |
|         '.hvac-filter-group select:focus'
 | |
|     ],
 | |
|     
 | |
|     // Links and navigation
 | |
|     links: [
 | |
|         '.hvac-content a:focus',
 | |
|         '.hvac-event-link:focus',
 | |
|         '.hvac-certificate-link:focus',
 | |
|         '.hvac-attendee-profile-icon:focus',
 | |
|         '.hvac-dashboard-nav a:focus',
 | |
|         '.hvac-email-navigation a:focus'
 | |
|     ],
 | |
|     
 | |
|     // Interactive elements
 | |
|     interactive: [
 | |
|         '.hvac-attendee-checkbox:focus',
 | |
|         '.hvac-select-all-container input[type="checkbox"]:focus',
 | |
|         '.hvac-modal-close:focus',
 | |
|         '.hvac-certificate-table tr:focus'
 | |
|     ]
 | |
| };
 | |
| 
 | |
| // Focus styles definitions
 | |
| const FOCUS_STYLES = {
 | |
|     buttons: `
 | |
|     outline: 2px solid #005fcc;
 | |
|     outline-offset: 2px;
 | |
|     box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);
 | |
|     border-radius: 4px;`,
 | |
|     
 | |
|     inputs: `
 | |
|     outline: 2px solid #005fcc;
 | |
|     outline-offset: 2px;
 | |
|     border-color: #005fcc;
 | |
|     box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);`,
 | |
|     
 | |
|     links: `
 | |
|     outline: 2px solid #005fcc;
 | |
|     outline-offset: 2px;
 | |
|     text-decoration: underline;
 | |
|     background-color: rgba(0, 95, 204, 0.1);
 | |
|     border-radius: 2px;`,
 | |
|     
 | |
|     interactive: `
 | |
|     outline: 2px solid #005fcc;
 | |
|     outline-offset: 2px;
 | |
|     box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);`
 | |
| };
 | |
| 
 | |
| // Skip focus outline for mouse users (but keep for keyboard)
 | |
| const MOUSE_FOCUS_RESET = `
 | |
| /* Reset focus for mouse users while preserving keyboard accessibility */
 | |
| .js-focus-visible :focus:not(.focus-visible) {
 | |
|     outline: none;
 | |
|     box-shadow: none;
 | |
| }
 | |
| 
 | |
| /* Ensure focus is visible for keyboard users */
 | |
| .js-focus-visible .focus-visible {
 | |
|     outline: 2px solid #005fcc;
 | |
|     outline-offset: 2px;
 | |
| }`;
 | |
| 
 | |
| function addFocusStylesToFile(filePath) {
 | |
|     try {
 | |
|         let content = fs.readFileSync(filePath, 'utf8');
 | |
|         let modifications = 0;
 | |
|         
 | |
|         console.log(`\nProcessing: ${path.basename(filePath)}`);
 | |
|         
 | |
|         // Check if focus styles already exist
 | |
|         if (content.includes('/* Focus Management Styles */')) {
 | |
|             console.log('  ✓ Focus styles already exist, skipping');
 | |
|             return 0;
 | |
|         }
 | |
|         
 | |
|         // Add focus styles section
 | |
|         const focusSection = `
 | |
| 
 | |
| /* Focus Management Styles - WCAG 2.1 Compliance */
 | |
| /* Added for keyboard accessibility and screen reader support */
 | |
| 
 | |
| /* Button Focus Styles */
 | |
| ${FOCUS_PATTERNS.buttons.join(',\n')} {${FOCUS_STYLES.buttons}
 | |
| }
 | |
| 
 | |
| /* Input Focus Styles */
 | |
| ${FOCUS_PATTERNS.inputs.join(',\n')} {${FOCUS_STYLES.inputs}
 | |
| }
 | |
| 
 | |
| /* Link Focus Styles */
 | |
| ${FOCUS_PATTERNS.links.join(',\n')} {${FOCUS_STYLES.links}
 | |
| }
 | |
| 
 | |
| /* Interactive Element Focus Styles */
 | |
| ${FOCUS_PATTERNS.interactive.join(',\n')} {${FOCUS_STYLES.interactive}
 | |
| }
 | |
| 
 | |
| /* High Contrast Mode Support */
 | |
| @media (prefers-contrast: high) {
 | |
|     .hvac-content *:focus {
 | |
|         outline: 3px solid #000000;
 | |
|         outline-offset: 2px;
 | |
|         background-color: #ffff00;
 | |
|         color: #000000;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Focus-visible polyfill support */
 | |
| ${MOUSE_FOCUS_RESET}
 | |
| `;
 | |
| 
 | |
|         // Add the focus section before the last closing brace or at the end
 | |
|         if (content.includes('@media print')) {
 | |
|             // Insert before print styles
 | |
|             content = content.replace('@media print', focusSection + '\n@media print');
 | |
|         } else {
 | |
|             // Add at the end
 | |
|             content += focusSection;
 | |
|         }
 | |
|         
 | |
|         modifications++;
 | |
|         
 | |
|         // Write the updated content
 | |
|         fs.writeFileSync(filePath, content, 'utf8');
 | |
|         
 | |
|         console.log(`  ✓ Added ${modifications} focus management sections`);
 | |
|         focusRules.push({
 | |
|             file: path.basename(filePath),
 | |
|             modifications: modifications
 | |
|         });
 | |
|         
 | |
|         return modifications;
 | |
|         
 | |
|     } catch (error) {
 | |
|         console.error(`Error processing ${filePath}:`, error.message);
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function addFocusIndicatorsToAnimations() {
 | |
|     const animationsPath = path.join(CSS_DIR, 'hvac-animations.css');
 | |
|     
 | |
|     try {
 | |
|         let content = fs.readFileSync(animationsPath, 'utf8');
 | |
|         
 | |
|         // Check if focus-specific animations already exist
 | |
|         if (content.includes('/* Focus Animation Styles */')) {
 | |
|             console.log('  ✓ Animations file already has focus styles');
 | |
|             return 0;
 | |
|         }
 | |
|         
 | |
|         const focusAnimations = `
 | |
| 
 | |
| /* Focus Animation Styles */
 | |
| /* Smooth transitions for focus indicators */
 | |
| 
 | |
| .hvac-content *:focus {
 | |
|     transition: outline 0.2s ease-out, 
 | |
|                 box-shadow 0.2s ease-out,
 | |
|                 background-color 0.2s ease-out;
 | |
| }
 | |
| 
 | |
| /* Focus indicator animation for better visibility */
 | |
| @keyframes focus-pulse {
 | |
|     0% { box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.3); }
 | |
|     50% { box-shadow: 0 0 0 5px rgba(0, 95, 204, 0.2); }
 | |
|     100% { box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.3); }
 | |
| }
 | |
| 
 | |
| /* Apply focus pulse to critical interactive elements */
 | |
| .hvac-button:focus,
 | |
| .hvac-email-submit:focus,
 | |
| .hvac-content button[type="submit"]:focus {
 | |
|     animation: focus-pulse 2s ease-in-out infinite;
 | |
| }
 | |
| 
 | |
| /* Disable focus animations for reduced motion users */
 | |
| @media (prefers-reduced-motion: reduce) {
 | |
|     .hvac-content *:focus {
 | |
|         transition: none;
 | |
|         animation: none;
 | |
|     }
 | |
| }`;
 | |
| 
 | |
|         // Add before the reduced motion section
 | |
|         content = content.replace(
 | |
|             '/* Disable animations for users who prefer reduced motion */',
 | |
|             focusAnimations + '\n\n/* Disable animations for users who prefer reduced motion */'
 | |
|         );
 | |
|         
 | |
|         fs.writeFileSync(animationsPath, content, 'utf8');
 | |
|         console.log('  ✓ Added focus animations to hvac-animations.css');
 | |
|         
 | |
|         return 1;
 | |
|     } catch (error) {
 | |
|         console.error(`Error updating animations file:`, error.message);
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function main() {
 | |
|     console.log('HVAC Community Events - CSS Focus Management Fixes');
 | |
|     console.log('='.repeat(60));
 | |
|     
 | |
|     if (!fs.existsSync(CSS_DIR)) {
 | |
|         console.error(`CSS directory not found: ${CSS_DIR}`);
 | |
|         process.exit(1);
 | |
|     }
 | |
|     
 | |
|     const cssFiles = fs.readdirSync(CSS_DIR)
 | |
|         .filter(file => file.endsWith('.css'))
 | |
|         .map(file => path.join(CSS_DIR, file));
 | |
|     
 | |
|     let totalModifications = 0;
 | |
|     
 | |
|     // Process each CSS file
 | |
|     cssFiles.forEach(filePath => {
 | |
|         totalModifications += addFocusStylesToFile(filePath);
 | |
|     });
 | |
|     
 | |
|     // Add focus-specific animations
 | |
|     totalModifications += addFocusIndicatorsToAnimations();
 | |
|     
 | |
|     console.log('\n' + '='.repeat(60));
 | |
|     console.log('FOCUS MANAGEMENT FIX SUMMARY');
 | |
|     console.log('='.repeat(60));
 | |
|     
 | |
|     focusRules.forEach(rule => {
 | |
|         console.log(`${rule.file}: ${rule.modifications} focus sections added`);
 | |
|     });
 | |
|     
 | |
|     console.log(`\nTotal files processed: ${cssFiles.length}`);
 | |
|     console.log(`Total focus modifications: ${totalModifications}`);
 | |
|     
 | |
|     if (totalModifications > 0) {
 | |
|         console.log('\n✅ Focus management fixes applied successfully!');
 | |
|         console.log('\nKey improvements:');
 | |
|         console.log('• Added WCAG 2.1 compliant focus indicators');
 | |
|         console.log('• Implemented keyboard navigation support');
 | |
|         console.log('• Added high contrast mode compatibility');
 | |
|         console.log('• Included focus-visible polyfill support');
 | |
|         console.log('• Added smooth focus transitions');
 | |
|         console.log('• Respect user motion preferences');
 | |
|         
 | |
|         console.log('\n📋 Next Steps:');
 | |
|         console.log('• Test keyboard navigation across all pages');
 | |
|         console.log('• Verify screen reader compatibility');
 | |
|         console.log('• Check high contrast mode display');
 | |
|         console.log('• Add focus-visible.js polyfill to pages');
 | |
|     } else {
 | |
|         console.log('\n✅ All CSS files already have focus management styles');
 | |
|     }
 | |
| }
 | |
| 
 | |
| if (require.main === module) {
 | |
|     main();
 | |
| }
 | |
| 
 | |
| module.exports = { addFocusStylesToFile, FOCUS_PATTERNS, FOCUS_STYLES }; |