Cursor rule
.cursor/rules/flip-card-accessibility.mdcFlip Card component accessibility compliance pattern
Cursor rules
Quality
36/100
Scores the file, not the repository.Length
1,397 words
1 headings · 3 code blocksRepository
428
— · pushed 17 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Flip Card Component Accessibility Standards78Ensures flip card components follow WCAG compliance and provide proper state management for screen reader users.910<rule>11name: flip_card_accessibility_standards12description: Enforce flip card component accessibility standards and proper state management13filters:14 - type: file_extension15 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts)$"1617actions:18 - type: enforce19 conditions:20 # Flip button requirement21 - pattern: "(?i)<(div|section)[^>]*(?:card|flip)[^>]*>"22 pattern_negate: "<button[^>]*aria-pressed"23 message: "Flip cards must contain a button with aria-pressed attribute to control card state."2425 # aria-pressed attribute requirement26 - pattern: "(?i)<button[^>]*(?:flip|card)[^>]*>"27 pattern_negate: "aria-pressed=\"(true|false)\""28 message: "Flip card buttons must have aria-pressed attribute set to 'true' or 'false'."2930 # Card front/back structure requirement31 - pattern: "(?i)<(div|section)[^>]*(?:card|flip)[^>]*>"32 pattern_negate: "(card--front|card--back|front|back)"33 message: "Flip cards must have both front and back content sections for proper structure."3435 # Unique accessible name requirement36 - pattern: "(?i)<button[^>]*aria-pressed[^>]*>"37 pattern_negate: "(aria-label|aria-labelledby|>.*[A-Za-z]{10,})"38 message: "Flip card buttons must have unique, descriptive accessible names that reference visible card content."3940 # Keyboard focus indicator requirement41 - pattern: "(?i)<(div|section)[^>]*(?:card|flip)[^>]*>"42 pattern_negate: "(focus|:focus|focus-visible|:focus-visible)"43 message: "Flip card containers should have visible keyboard focus indicators when the flip button is focused."4445 # Content visibility management46 - pattern: "(?i)aria-pressed=\"(true|false)\""47 pattern_negate: "(visibility.*hidden|display.*none|hidden)"48 message: "Use aria-pressed state to control content visibility - false shows front, true shows back. Prefer visibility: hidden/visible for smooth animations."4950 # Missing flip button type51 - pattern: "(?i)<button[^>]*(?:flip|card)[^>]*>"52 pattern_negate: "type=\"button\""53 message: "Flip card buttons should have type='button' to prevent form submission behavior."5455 # Incomplete card structure56 - pattern: "(?i)<div[^>]*class=\"card[^>]*>"57 pattern_negate: "(card--front.*card--back|card--back.*card--front)"58 message: "Flip cards must contain both front and back content sections for proper functionality."5960 - type: suggest61 message: |62 **Flip Card Component Accessibility Best Practices:**6364 **Required ARIA Attributes:**65 - **aria-pressed:** 'false' shows front content, 'true' shows back content66 - **type="button":** Prevents form submission behavior67 - **Unique accessible name:** Should reference visible card content6869 **DOM Structure Requirements:**70 - Card container with front and back content sections71 - Flip button positioned between or adjacent to content sections72 - Use CSS display: none to hide non-visible content73 - Maintain logical reading order in the DOM7475 **Content Visibility Management:**76 - **aria-pressed="false":** Show front content, hide back content77 - **aria-pressed="true":** Show back content, hide front content78 - Use CSS display property for smooth transitions79 - Ensure only one side is visible at a time8081 **Keyboard and Focus Requirements:**82 - **Enter:** Toggle card state83 - **Space:** Toggle card state84 - **Tab:** Move focus to next focusable element85 - **Shift+Tab:** Move focus to previous focusable element86 - **Focus indicator:** Should wrap the card content container87 - **Hover state:** Blue border matching focus indicator for visual consistency8889 **Implementation Example:**90```html91 <!-- ✅ Correct: Proper flip card structure -->92 <div class="card">93 <div class="card--front">94 <h3>Card Title</h3>95 <img src="front-image.jpg" alt="Front view of the product">96 </div>9798 <button type="button"99 class="flip-button"100 aria-pressed="false"101 aria-label="More about Card Title">102 </button>103104 <div class="card--back">105 <p class="card--tagline">Inspiring content</p>106 <img src="back-image-1.jpg" alt="Product detail view 1">107 <img src="back-image-2.jpg" alt="Product detail view 2">108 <img src="back-image-3.jpg" alt="Product detail view 3">109 <p>More detailed content about the product</p>110 <a href="/product-details">111 <img src="link-icon.svg" alt="View full product details">112 </a>113 </div>114 </div>115```116117 **CSS Implementation:**118```css119 .card {120 position: relative;121 perspective: 1000px;122 /* Focus indicator for keyboard navigation */123 outline: 2px solid transparent;124 outline-offset: 2px;125 /* Visual affordance for clickable card */126 cursor: pointer;127 }128129 .card:focus-within {130 outline-color: #0056b3;131 outline-width: 3px;132 }133134 .card:hover {135 outline-color: #0056b3;136 outline-width: 3px;137 }138139 .card--front,140 .card--back {141 transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0.3s ease-in-out;142 }143144 /* Show front by default */145 .card--front {146 visibility: visible;147 opacity: 1;148 transform: rotateY(0deg);149 }150151 .card--back {152 visibility: hidden;153 opacity: 0;154 transform: rotateY(180deg);155 }156157 /* Show back when aria-pressed="true" */158 .card[data-pressed="true"] .card--front {159 visibility: hidden;160 opacity: 0;161 transform: rotateY(-180deg);162 }163164 .card[data-pressed="true"] .card--back {165 visibility: visible;166 opacity: 1;167 transform: rotateY(0deg);168 }169170 .flip-button {171 position: absolute;172 top: 1rem;173 right: 1rem;174 background: #0056b3;175 color: #ffffff;176 border: none;177 width: 40px;178 height: 40px;179 border-radius: 50%;180 cursor: pointer;181 transition: all 0.2s ease;182 z-index: 10;183 display: flex;184 align-items: center;185 justify-content: center;186 font-size: 18px;187 line-height: 1;188 }189190 .flip-button:hover {191 background: #004085;192 transform: scale(1.1);193 box-shadow: 0 4px 12px rgba(0, 86, 179, 0.3);194 }195196 .flip-button:focus {197 outline: 3px solid #ffd700;198 outline-offset: 2px;199 background: #004085;200 }201202 .flip-button:active {203 transform: scale(0.95);204 }205206 /* Button icon states */207 .flip-button::before {208 content: "↻"; /* Circular arrow icon for front state */209 transition: all 0.3s ease;210 }211212 /* Show X icon when back is visible */213 .card[data-pressed="true"] .flip-button::before {214 content: "×"; /* X icon for back state */215 font-size: 24px;216 font-weight: bold;217 }218219 /* Reduced motion support */220 @media (prefers-reduced-motion: reduce) {221 .card--front,222 .card--back {223 transition: none;224 }225226 .flip-button {227 transition: none;228 }229230 .flip-button:hover {231 transform: none;232 box-shadow: none;233 }234235 .flip-button:active {236 transform: none;237 }238239 .card {240 transition: none;241 }242 }243```244245 **JavaScript State Management:**246```javascript247 const flipCards = document.querySelectorAll('.card');248249 flipCards.forEach(card => {250 const button = card.querySelector('.flip-button');251 const front = card.querySelector('.card--front');252 const back = card.querySelector('.card--back');253254 function toggleCard() {255 const isPressed = button.getAttribute('aria-pressed') === 'true';256 const newState = !isPressed;257258 // Update ARIA state259 button.setAttribute('aria-pressed', newState);260261 // Update card data attribute for CSS262 card.setAttribute('data-pressed', newState);263264 // Announce state change to screen readers265 const announcement = newState ? 'Showing back of card' : 'Showing front of card';266 announceToScreenReader(announcement);267 }268269 // Button click handler270 button.addEventListener('click', (event) => {271 event.stopPropagation(); // Prevent card click when button is clicked272 toggleCard();273 });274275 // Card container click handler for mouse/touch users276 card.addEventListener('click', toggleCard);277278 // Keyboard handler279 button.addEventListener('keydown', (event) => {280 if (event.key === 'Enter' || event.key === ' ') {281 event.preventDefault();282 toggleCard();283 }284 });285 });286287 // Screen reader announcement helper288 function announceToScreenReader(message) {289 const announcement = document.createElement('div');290 announcement.setAttribute('aria-live', 'polite');291 announcement.setAttribute('aria-atomic', 'true');292 announcement.className = 'sr-only';293 announcement.textContent = message;294295 document.body.appendChild(announcement);296297 setTimeout(() => {298 document.body.removeChild(announcement);299 }, 1000);300 }301```302303 **Accessibility Guidelines:**304305 **Button Requirements:**306 - Must have type="button" to prevent form submission307 - aria-pressed attribute must be present and toggle between "true" and "false"308 - Accessible name should reference visible card content via aria-label309 - Should handle both click and keyboard events310 - Position in top-right corner for intuitive placement311 - Use dynamic icons: ↻ (arrow) for front state, × (X) for back state312313 **Card Container Interaction:**314 - **Mouse/Touch Support:** Allow clicking anywhere on card to flip315 - **Visual Affordance:** Use cursor: pointer to indicate clickable area316 - **Event Handling:** Prevent conflicts between card and button clicks317 - **Accessibility Maintained:** All keyboard and screen reader functionality preserved318319 **Content Structure:**320 - Front and back content must be present321 - Only one side visible at a time322 - Use semantic HTML for content (headings, paragraphs, images)323 - Maintain logical reading order324 - Use `visibility: hidden/visible` for smooth animations while maintaining accessibility325326 **Focus Management:**327 - Focus indicator should wrap the entire card when button is focused328 - Use :focus-within CSS pseudo-class for container focus329 - Ensure focus indicator has sufficient contrast (3:1 minimum)330 - Maintain focus order during state changes331332 **Screen Reader Support:**333 - aria-pressed state announces current card side334 - Consider adding aria-live region for state changes335 - Ensure content is properly labeled and described336 - Test with screen readers to verify announcements337338 **Animation Considerations:**339 - Use CSS transitions for smooth flipping effects340 - Ensure animations don't interfere with accessibility341 - **Always implement prefers-reduced-motion media query**342 - Remove all animations, transforms, and transitions when reduced motion is preferred343 - Maintain content visibility during transitions344 - Respect user's motion sensitivity preferences345346 **Button Design Best Practices:**347 - **Positioning**: Place in top-right corner for intuitive access348 - **Shape**: Use circular design for modern, clean appearance349 - **Icons**: Implement dynamic icons that change with card state350 - Front state: ↻ (circular arrow) indicating "click to flip"351 - Back state: × (X) indicating "click to return"352 - **Visual Feedback**: Include hover, focus, and active states353 - **Accessibility**: Maintain aria-label for screen reader context354 - **Responsive**: Scale appropriately for different card sizes355356 **Testing Checklist:**357 - Verify aria-pressed toggles correctly358 - Test keyboard navigation (Enter, Space, Tab)359 - Check focus indicator visibility and contrast360 - Validate screen reader announcements361 - Test content visibility changes362 - Verify accessible names are descriptive via aria-label363 - Check that only one side is visible at a time364 - Verify button positioning in top-right corner365 - Test dynamic icon changes (↻ to ×) with state changes366 - Ensure button remains accessible in all card states367 - Validate responsive button sizing for different card layouts368 - Test card container click functionality (anywhere on card)369 - Verify hover states show blue border matching focus indicator370 - Check cursor pointer appears on card hover371 - Ensure no conflicts between card and button click events372 - Test reduced motion preference support (prefers-reduced-motion: reduce)373 - Verify all animations are disabled when reduced motion is preferred374375metadata:376 priority: high377 version: 1.0378</rule>379
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/animation-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/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/animation-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/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
