upskill-event-manager/fix-reduced-motion.js
bengizmo 993a820a84 feat: Add comprehensive development artifacts to repository
- 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>
2025-08-11 12:26:11 -03:00

362 lines
No EOL
12 KiB
JavaScript

#!/usr/bin/env node
/**
* HVAC Community Events Reduced Motion Accessibility Fixes
*
* This script implements prefers-reduced-motion support across all CSS files
* to ensure accessibility compliance for users with vestibular disorders.
*/
const fs = require('fs');
const path = require('path');
const CSS_DIR = './assets/css';
const motionRules = [];
// Properties that should respect reduced motion preferences
const MOTION_PROPERTIES = {
animations: [
'animation',
'animation-name',
'animation-duration',
'animation-timing-function',
'animation-delay',
'animation-iteration-count',
'animation-direction',
'animation-fill-mode',
'animation-play-state'
],
transitions: [
'transition',
'transition-property',
'transition-duration',
'transition-timing-function',
'transition-delay'
],
transforms: [
'transform'
]
};
// Selectors that commonly use motion effects
const MOTION_SELECTORS = [
'.hvac-animate-fade-in',
'.hvac-animate-scale-up',
'.hvac-animate-pulse',
'.hvac-animate-slide-in-right',
'.hvac-animate-slide-in-left',
'.hvac-animate-slide-in-bottom',
'.hvac-card:hover',
'.hvac-stat-card:hover',
'.hvac-event-stat-card:hover',
'.hvac-button:hover',
'.hvac-email-submit:hover',
'.hvac-attendee-item:hover',
'.hvac-loading::after',
'@keyframes',
'transform:',
'animation:',
'transition:'
];
function addReducedMotionSupport(filePath) {
try {
let content = fs.readFileSync(filePath, 'utf8');
let modifications = 0;
const fileName = path.basename(filePath);
console.log(`\nProcessing: ${fileName}`);
// Skip files that already have reduced motion support
if (content.includes('/* Reduced Motion Support Added */')) {
console.log(' ✓ Reduced motion support already exists, skipping');
return 0;
}
// Only process files that contain motion effects
const hasMotionEffects = MOTION_SELECTORS.some(selector =>
content.includes(selector.replace(':', ''))
);
if (!hasMotionEffects) {
console.log(' • No motion effects detected, skipping');
return 0;
}
// Comprehensive reduced motion media query
const reducedMotionSupport = `
/* Reduced Motion Support Added - WCAG 2.1 Accessibility */
/* Respects user preference for reduced motion to prevent vestibular disorders */
@media (prefers-reduced-motion: reduce) {
/* Disable all animations and transitions globally */
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-delay: 0s !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
transition-delay: 0s !important;
scroll-behavior: auto !important;
}
/* Remove specific transform animations */
.hvac-animate-fade-in,
.hvac-animate-scale-up,
.hvac-animate-pulse,
.hvac-animate-slide-in-right,
.hvac-animate-slide-in-left,
.hvac-animate-slide-in-bottom {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
/* Disable hover transformations */
.hvac-card:hover,
.hvac-stat-card:hover,
.hvac-event-stat-card:hover,
.hvac-button:hover,
.hvac-email-submit:hover {
transform: none !important;
animation: none !important;
}
/* Keep essential visual feedback but remove motion */
.hvac-card:hover,
.hvac-stat-card:hover,
.hvac-event-stat-card:hover {
border-color: var(--hvac-primary, #0274be) !important;
box-shadow: 0 0 0 2px rgba(2, 116, 190, 0.2) !important;
}
/* Disable loading spinner animation but keep visibility */
.hvac-loading::after {
animation: none !important;
border-radius: 50% !important;
border: 2px solid rgba(0, 0, 0, 0.2) !important;
border-top-color: #333 !important;
}
/* Disable focus pulse animation */
.hvac-button:focus,
.hvac-email-submit:focus,
.hvac-content button[type="submit"]:focus {
animation: none !important;
}
/* Ensure smooth scrolling is disabled */
html {
scroll-behavior: auto !important;
}
/* Disable CSS Grid/Flexbox animations if any */
.hvac-dashboard-stats .hvac-stat-card:nth-child(n),
.hvac-event-summary-stats .hvac-event-stat-card:nth-child(n) {
animation: none !important;
opacity: 1 !important;
}
}
/* Provide alternative visual feedback for reduced motion users */
@media (prefers-reduced-motion: reduce) {
/* Enhanced border feedback instead of transform */
.hvac-content button:hover,
.hvac-content input[type="submit"]:hover,
.hvac-content a:hover {
outline: 2px solid var(--hvac-primary, #0274be) !important;
outline-offset: 2px !important;
}
/* Enhanced color changes for interactive elements */
.hvac-attendee-item:hover {
background-color: var(--hvac-primary-light, #e6f3fb) !important;
border-left: 4px solid var(--hvac-primary, #0274be) !important;
}
/* Static loading indicator */
.hvac-loading {
opacity: 0.7 !important;
}
.hvac-loading::after {
content: "Loading..." !important;
display: inline-block !important;
font-size: 12px !important;
color: #666 !important;
border: none !important;
background: none !important;
border-radius: 0 !important;
width: auto !important;
height: auto !important;
position: static !important;
margin-left: 8px !important;
}
}`;
// Add reduced motion support before any existing @media queries or at the end
if (content.includes('@media')) {
// Insert before the first @media query
const firstMediaIndex = content.indexOf('@media');
content = content.slice(0, firstMediaIndex) +
reducedMotionSupport + '\n\n' +
content.slice(firstMediaIndex);
} else {
// Add at the end
content += reducedMotionSupport;
}
modifications++;
// Add marker at the beginning
content = `/* Reduced Motion Support Added - ${new Date().toISOString().split('T')[0]} */\n${content}`;
// Write the updated content
fs.writeFileSync(filePath, content, 'utf8');
console.log(` ✅ Added comprehensive reduced motion support`);
motionRules.push({
file: fileName,
modifications: modifications
});
return modifications;
} catch (error) {
console.error(`Error processing ${filePath}:`, error.message);
return 0;
}
}
function createMotionPreferenceDetection() {
const jsPath = './assets/js/reduced-motion-detection.js';
const jsContent = `/**
* Reduced Motion Preference Detection
* Adds CSS class to HTML element for reduced motion users
*/
(function() {
'use strict';
// Check for reduced motion preference
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function handleReducedMotionChange(mediaQuery) {
const htmlElement = document.documentElement;
if (mediaQuery.matches) {
htmlElement.classList.add('reduced-motion');
console.log('Reduced motion preference detected - animations disabled');
} else {
htmlElement.classList.remove('reduced-motion');
}
}
// Set initial state
handleReducedMotionChange(prefersReducedMotion);
// Listen for changes
if (prefersReducedMotion.addEventListener) {
prefersReducedMotion.addEventListener('change', handleReducedMotionChange);
} else if (prefersReducedMotion.addListener) {
// Fallback for older browsers
prefersReducedMotion.addListener(handleReducedMotionChange);
}
// Additional safety: Disable animations if user has motion sensitivity
if (navigator.userAgent.includes('MotionSensitive') ||
navigator.userAgent.includes('AccessibilityMode')) {
document.documentElement.classList.add('reduced-motion');
}
})();`;
// Create js directory if it doesn't exist
const jsDir = path.dirname(jsPath);
if (!fs.existsSync(jsDir)) {
fs.mkdirSync(jsDir, { recursive: true });
}
if (!fs.existsSync(jsPath)) {
fs.writeFileSync(jsPath, jsContent, 'utf8');
console.log('✓ Created reduced-motion-detection.js');
}
}
function main() {
console.log('HVAC Community Events - Reduced Motion Accessibility Fixes');
console.log('='.repeat(60));
if (!fs.existsSync(CSS_DIR)) {
console.error(`CSS directory not found: ${CSS_DIR}`);
process.exit(1);
}
// Focus on files that are likely to have animations
const targetFiles = [
'hvac-animations.css',
'hvac-common.css',
'hvac-dashboard.css',
'hvac-registration.css',
'hvac-event-summary.css',
'hvac-email-attendees.css',
'hvac-mobile-nav.css',
'hvac-certificates.css',
'hvac-attendee-profile.css'
].map(file => path.join(CSS_DIR, file));
// Filter to only existing files
const existingFiles = targetFiles.filter(filePath => fs.existsSync(filePath));
let totalModifications = 0;
// Process each CSS file
existingFiles.forEach(filePath => {
totalModifications += addReducedMotionSupport(filePath);
});
// Create JavaScript detection helper
createMotionPreferenceDetection();
console.log('\n' + '='.repeat(60));
console.log('REDUCED MOTION FIX SUMMARY');
console.log('='.repeat(60));
if (motionRules.length > 0) {
motionRules.forEach(rule => {
console.log(`${rule.file}: Reduced motion support added`);
});
}
console.log(`\nTotal files processed: ${existingFiles.length}`);
console.log(`Total files with reduced motion support: ${totalModifications}`);
if (totalModifications > 0) {
console.log('\n✅ Reduced motion accessibility fixes applied successfully!');
console.log('\nKey improvements:');
console.log('• Added @media (prefers-reduced-motion: reduce) queries');
console.log('• Disabled all animations and transitions for sensitive users');
console.log('• Provided alternative visual feedback without motion');
console.log('• Created JavaScript detection helper');
console.log('• Enhanced accessibility for vestibular disorders');
console.log('\n📋 WCAG 2.1 Compliance:');
console.log('• Success Criterion 2.3.3 (Animation from Interactions) - Level AAA');
console.log('• Success Criterion 2.2.2 (Pause, Stop, Hide) - Level A');
console.log('• Supports users with vestibular motion disorders');
console.log('\n💡 Implementation Notes:');
console.log('• Include reduced-motion-detection.js in your HTML pages');
console.log('• Test with browser dev tools: Rendering > Emulate CSS prefers-reduced-motion');
console.log('• Verify all animations are disabled when preference is set');
console.log('• Alternative visual feedback maintains usability');
} else {
console.log('\n✅ All relevant CSS files already have reduced motion support');
}
}
if (require.main === module) {
main();
}
module.exports = { addReducedMotionSupport, MOTION_PROPERTIES };