Cursor rule
.cursor/rules/animation-accessibility.mdcEnforce animation accessibility standards per WCAG 2.2.2 Pause Stop Hide, 2.3.1 Three Flashes or Below Threshold, and 2.3.3 Animation from Interactions requirements
Cursor rules
Quality
40/100
Scores the file, not the repository.Length
1,143 words
1 headings · 10 code blocksRepository
428
— · pushed 17 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Animation Accessibility Standards89Ensures animations follow WCAG compliance and provide inclusive motion design for users with different accessibility needs including photosensitivity, motion sickness, and cognitive impairments.1011<rule>12name: animation_accessibility_standards13description: Enforce animation accessibility standards per WCAG 2.2.2 Pause Stop Hide, 2.3.1 Three Flashes or Below Threshold, and 2.3.3 Animation from Interactions requirements14filters:15 - type: file_extension16 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts|css|scss|sass|less)$"1718actions:19 - type: enforce20 conditions:21 # Missing prefers-reduced-motion media query for animations22 - pattern: "(animation|transition|transform|@keyframes)"23 pattern_negate: "@media\\s*\\(prefers-reduced-motion:\\s*reduce\\)"24 message: "Animations should include prefers-reduced-motion: reduce media query to provide safer alternatives for motion-sensitive users."2526 # Flashing animations exceeding 3Hz frequency27 - pattern: "animation.*(?:pulse|flash|blink|flicker)"28 pattern_negate: "animation-duration:\\s*[0-9]*\\.?[0-9]+s|animation-duration:\\s*[0-9]*\\.?[0-9]+ms"29 message: "Flashing animations must have duration ensuring frequency is below 3Hz (0.33s) to prevent seizures per WCAG 2.3.1."3031 # Rapid color transitions that may trigger photosensitivity32 - pattern: "transition.*color.*[0-9]*\\.?[0-9]+s|transition.*background.*[0-9]*\\.?[0-9]+s"33 pattern_negate: "transition.*color.*[0-9]*\\.?[0-9]+s.*[0-9]*\\.?[0-9]+s|transition.*background.*[0-9]*\\.?[0-9]+s.*[0-9]*\\.?[0-9]+s"34 message: "Color transitions should be slow and smooth to avoid triggering photosensitivity. Use longer durations and easing functions."3536 # Large spatial movements without reduced motion alternatives37 - pattern: "transform.*translate\\([^)]*[0-9]{2,}[^)]*\\)|transform.*translate\\([^)]*-[0-9]{2,}[^)]*\\)"38 pattern_negate: "@media\\s*\\(prefers-reduced-motion:\\s*reduce\\)"39 message: "Large spatial movements should have reduced motion alternatives using prefers-reduced-motion media query."4041 # Parallax effects without user control42 - pattern: "(parallax|scroll.*jack|scroll.*hijack)"43 pattern_negate: "(prefers-reduced-motion|user.*control|pause.*animation)"44 message: "Parallax and scroll jacking effects should respect user motion preferences and provide controls to pause/disable."4546 # Auto-playing animations without pause controls47 - pattern: "animation.*infinite|animation.*loop"48 pattern_negate: "(pause.*control|user.*control|prefers-reduced-motion)"49 message: "Looping animations must provide pause controls and respect prefers-reduced-motion preferences."5051 # Unexpected system-triggered animations52 - pattern: "animation.*(?:appear|fade.*in|slide.*in)"53 pattern_negate: "(user.*interaction|click|hover|focus|prefers-reduced-motion)"54 message: "System-triggered animations should be subtle and respect user motion preferences."5556 # Missing animation alternatives for essential UI changes57 - pattern: "(?:loading|spinner|progress|status)"58 pattern_negate: "(animation|transition|@keyframes)"59 message: "Essential UI elements like loading indicators should have appropriate animations to communicate state changes."6061 # Excessive animation duration that may cause motion sickness62 - pattern: "animation-duration:\\s*[5-9]\\.[0-9]+s|animation-duration:\\s*[0-9]{2,}s"63 message: "Long animation durations may cause motion sickness. Consider shorter durations and provide reduced motion alternatives."6465 # Missing focus indicators for animated interactive elements66 - pattern: "(?:button|a|input|select|textarea).*\\{[^}]*animation"67 pattern_negate: "(focus|focus-visible|outline|box-shadow)"68 message: "Animated interactive elements must have visible focus indicators for keyboard navigation accessibility."6970 # Animation without meaningful purpose or context71 - pattern: "animation.*(?:bounce|wiggle|shake|rotate)"72 pattern_negate: "(loading|status|feedback|interaction)"73 message: "Animations should serve a meaningful purpose. Avoid decorative animations that may distract or confuse users."7475 # Missing animation state management76 - pattern: "animation.*(?:play|pause|stop)"77 pattern_negate: "(prefers-reduced-motion|user.*control|aria.*live)"78 message: "Animation state changes should be communicated to assistive technology and respect user preferences."7980 - type: suggest81 message: |82 **Animation Accessibility Best Practices:**8384 **1. Respect Motion Preferences (WCAG 2.3.3):**85```css86 /* Default animation */87 .fade-in {88 animation: fadeIn 0.3s ease-in-out;89 }9091 /* Reduced motion alternative */92 @media (prefers-reduced-motion: reduce) {93 .fade-in {94 animation: none;95 opacity: 1;96 }97 }98```99100 **2. Seizure Prevention (WCAG 2.3.1):**101```css102 /* Safe flashing animation - below 3Hz threshold */103 .pulse {104 animation: pulse 0.4s ease-in-out infinite;105 }106107 /* Reduced motion alternative */108 @media (prefers-reduced-motion: reduce) {109 .pulse {110 animation: none;111 opacity: 0.8;112 }113 }114```115116 **3. Motion Sickness Prevention:**117```css118 /* Gentle, predictable animations */119 .slide-in {120 transform: translateX(20px);121 transition: transform 0.2s ease-out;122 }123124 /* Reduced motion alternative */125 @media (prefers-reduced-motion: reduce) {126 .slide-in {127 transform: none;128 transition: none;129 opacity: 1;130 }131 }132```133134 **4. Essential UI Animations:**135```css136 /* Loading spinner - essential for user understanding */137 .loading-spinner {138 animation: spin 1s linear infinite;139 }140141 /* Keep essential animations even with reduced motion */142 @media (prefers-reduced-motion: reduce) {143 .loading-spinner {144 animation: spin 2s linear infinite; /* Slower but still visible */145 }146 }147```148149 **5. User Control Implementation:**150```javascript151 // Animation pause control152 class AnimationController {153 constructor() {154 this.isPaused = false;155 this.animations = document.querySelectorAll('[data-animation]');156 this.setupControls();157 }158159 setupControls() {160 const pauseButton = document.getElementById('pause-animations');161 if (pauseButton) {162 pauseButton.addEventListener('click', () => this.togglePause());163 }164 }165166 togglePause() {167 this.isPaused = !this.isPaused;168 this.animations.forEach(animation => {169 if (this.isPaused) {170 animation.style.animationPlayState = 'paused';171 } else {172 animation.style.animationPlayState = 'running';173 }174 });175 }176 }177```178179 **6. CSS Animation Best Practices:**180```css181 /* Safe animation defaults */182 .safe-animation {183 /* Keep travel distances short */184 transform: translateY(10px);185186 /* Use gentle easing */187 transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);188189 /* Respect motion preferences */190 @media (prefers-reduced-motion: reduce) {191 transform: none;192 transition: none;193 }194 }195196 /* Fade as safe default */197 .fade-transition {198 opacity: 0;199 transition: opacity 0.3s ease-in-out;200 }201202 .fade-transition.visible {203 opacity: 1;204 }205206 /* Reduced motion alternative */207 @media (prefers-reduced-motion: reduce) {208 .fade-transition {209 transition: none;210 opacity: 1;211 }212 }213```214215 **7. JavaScript Animation Control:**216```javascript217 // Check motion preferences218 function shouldReduceMotion() {219 return window.matchMedia('(prefers-reduced-motion: reduce)').matches;220 }221222 // Safe animation function223 function safeAnimate(element, animation, options = {}) {224 if (shouldReduceMotion()) {225 // Provide alternative experience226 element.style.opacity = '1';227 element.style.transform = 'none';228 return;229 }230231 // Apply animation232 element.style.animation = animation;233234 // Add pause control235 if (options.looping) {236 element.addEventListener('click', () => {237 element.style.animationPlayState =238 element.style.animationPlayState === 'paused' ? 'running' : 'paused';239 });240 }241 }242```243244 **8. HTML Structure for Animation Control:**245```html246 <!-- Animation control panel -->247 <div class="animation-controls" role="region" aria-label="Animation controls">248 <button id="pause-animations" aria-pressed="false">249 Pause All Animations250 </button>251252 <button id="reduce-motion" aria-pressed="false">253 Reduce Motion254 </button>255 </div>256257 <!-- Animated element with controls -->258 <div class="animated-element"259 data-animation="fade-in"260 aria-live="polite">261 Content that animates262 </div>263```264265 **9. Animation State Management:**266```css267 /* Animation states */268 .animated-element {269 opacity: 0;270 transform: translateY(20px);271 transition: all 0.3s ease-out;272 }273274 .animated-element.animate {275 opacity: 1;276 transform: translateY(0);277 }278279 .animated-element.paused {280 animation-play-state: paused;281 }282283 /* Reduced motion states */284 @media (prefers-reduced-motion: reduce) {285 .animated-element {286 opacity: 1;287 transform: none;288 transition: none;289 }290 }291```292293 **10. Testing and Validation:**294```javascript295 // Test animation accessibility296 function testAnimationAccessibility() {297 const issues = [];298299 // Check for flashing animations300 const flashingElements = document.querySelectorAll('[class*="flash"], [class*="pulse"]');301 flashingElements.forEach(element => {302 const style = window.getComputedStyle(element);303 const duration = parseFloat(style.animationDuration);304 if (duration < 0.33) { // Below 3Hz threshold305 issues.push(`Flashing animation too fast: ${element.className}`);306 }307 });308309 // Check for motion preference support310 const hasReducedMotion = document.querySelector('@media (prefers-reduced-motion: reduce)');311 if (!hasReducedMotion) {312 issues.push('Missing reduced motion alternatives');313 }314315 return issues;316 }317```318319 **Animation Guidelines:**320321 **Keep These Animations:**322 - **Fading:** Safe default for most transitions323 - **Loading indicators:** Essential for user understanding324 - **Subtle scaling:** Gentle size changes for feedback325 - **Color transitions:** Slow, smooth color changes326327 **Subdue These Animations:**328 - **System-triggered:** Make unexpected animations subtle329 - **Large movements:** Reduce distance and speed330 - **Color changes:** Use gentler transitions331332 **Remove These Animations:**333 - **Decorative effects:** Remove purely visual animations334 - **Spatial movement:** Replace with gentle fades335 - **Auto-playing loops:** Remove or provide pause controls336337 **Testing Checklist:**338 - Test with prefers-reduced-motion: reduce339 - Verify animations don't exceed 3Hz frequency340 - Check that essential UI changes are clear without animation341 - Ensure pause controls work for looping animations342 - Test with screen readers and assistive technology343 - Validate motion doesn't cause disorientation344345 **Common Mistakes to Avoid:**346 - Missing prefers-reduced-motion media queries347 - Flashing animations above 3Hz threshold348 - Large spatial movements without alternatives349 - Auto-playing animations without pause controls350 - Parallax effects that ignore user preferences351 - Animations that patch design shortcomings352 - Missing focus indicators on animated elements353 - Excessive animation duration causing motion sickness354355metadata:356 priority: high357 version: 1.0358</rule>359description:360globs:361alwaysApply: false362---363
Also in Shopify/horizon
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Shopify/horizon.cursor/rules/accordion-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/blocks.mdc · 428 | Cursor rules | stylearchtypesdatabase+2 | 62/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/breadcrumb-accessibility.mdc · 428 | Cursor rules | ui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/carousel-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/cart-drawer-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/chat-window-accessibility.mdc · 428 | Cursor rules | styleui | 24/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/color-contrast-accessibility.mdc · 428 | Cursor rules | lint-formatui | 44/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/color-swatch-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/commit-messages.mdc · 428 | Cursor rules | setuplint-formattypesgit | 62/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/css-standards.mdc · 428 | Cursor rules | stylearchuiperformance+3 | 49/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/disclosure-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/dropdown-navigation-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/flip-card-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/form-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/javascript-standards.mdc · 428 | Cursor rules | stylearchtypesui+2 | 54/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/landmark-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/liquid.mdc · 428 | Cursor rules | buildstyletypesdatabase+2 | 77/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/locales.mdc · 428 | Cursor rules | arch | 49/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/localization.mdc · 428 | Cursor rules | style | 54/100 | 3 days ago | |
| Shopify/horizon.cursor/rules/mobile-accessibility-standards.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago |
Diff against .cursor/rules/accordion-accessibility.mdc Diff against .cursor/rules/blocks.mdc Diff against .cursor/rules/breadcrumb-accessibility.mdc Diff against .cursor/rules/carousel-accessibility.mdc Diff against .cursor/rules/cart-drawer-accessibility.mdc Diff against .cursor/rules/chat-window-accessibility.mdc Diff against .cursor/rules/color-contrast-accessibility.mdc Diff against .cursor/rules/color-swatch-accessibility.mdc Diff against .cursor/rules/commit-messages.mdc Diff against .cursor/rules/css-standards.mdc Diff against .cursor/rules/disclosure-accessibility.mdc Diff against .cursor/rules/dropdown-navigation-accessibility.mdc Diff against .cursor/rules/flip-card-accessibility.mdc Diff against .cursor/rules/form-accessibility.mdc Diff against .cursor/rules/javascript-standards.mdc Diff against .cursor/rules/landmark-accessibility.mdc Diff against .cursor/rules/liquid.mdc Diff against .cursor/rules/locales.mdc Diff against .cursor/rules/localization.mdc Diff against .cursor/rules/mobile-accessibility-standards.mdc
