Cursor rule
.cursor/rules/product-card-accessibility.mdcProduct card accessibility compliance pattern
Cursor rules
Quality
40/100
Scores the file, not the repository.Length
1,087 words
1 headings · 6 code blocksRepository
428
— · pushed 17 days agoLast changed
3 days ago
First indexed 3 days ago.123456# Product Card Component Accessibility Standards78Ensures product card components follow WCAG compliance and implement proper single tab-stop navigation for keyboard and screen reader users.910<rule>11name: product_card_accessibility_standards12description: Enforce product card component accessibility standards and single tab-stop navigation compliance13filters:14 - type: file_extension15 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts)$"1617actions:18 - type: enforce19 conditions:20 # Product card missing article wrapper21 - pattern: "(?i)<(div|section)[^>]*(?:product.*card|card.*product)[^>]*>"22 pattern_negate: "<article"23 message: "Product cards should be wrapped with the article element for semantic structure."2425 # Product card missing proper link structure26 - pattern: "(?i)<article[^>]*(?:product.*card|card.*product)[^>]*>"27 pattern_negate: "<a[^>]*>.*?</a>"28 message: "Product cards must contain a link element for keyboard navigation and screen reader accessibility."2930 # Product title not wrapped in link31 - pattern: "(?i)<(h1|h2|h3|h4|h5|h6)[^>]*(?:product.*title|title.*product)[^>]*>"32 pattern_negate: "<a[^>]*>.*?</a>"33 message: "Product titles should be wrapped in link elements to provide proper navigation context."3435 # Heading not wrapping link element36 - pattern: "(?i)<(h1|h2|h3|h4|h5|h6)[^>]*(?:product.*title|title.*product)[^>]*>"37 pattern_negate: "<a[^>]*>"38 message: "Product card headings should wrap link elements to maintain proper heading semantics."3940 # Missing product image alt text41 - pattern: "(?i)<img[^>]*(?:product|card)[^>]*>"42 pattern_negate: "alt=\"[^\"]+\""43 message: "Product card images must have descriptive alt text for screen reader users."4445 # Empty alt text on product images46 - pattern: "(?i)<img[^>]*(?:product|card)[^>]*alt=\"\"[^>]*>"47 message: "Product card images should not have empty alt text; provide descriptive text or use alt=\"\" only for decorative images."4849 # Product price missing proper semantic structure50 - pattern: "(?i)<(div|span)[^>]*(?:price|cost)[^>]*>"51 pattern_negate: "(<span[^>]*aria-label|aria-label=\"[^\"]*price[^\"]*\")"52 message: "Product prices should have proper semantic labeling for screen readers."5354 # Product description not in paragraph element55 - pattern: "(?i)<(div|span)[^>]*(?:product.*description|description.*product)[^>]*>"56 pattern_negate: "<p"57 message: "Product descriptions should be wrapped in paragraph elements for proper semantic structure."5859 # Missing focus indicators60 - pattern: "(?i)<a[^>]*(?:product.*card|card.*product)[^>]*>"61 pattern_negate: "(focus|hover|active)"62 message: "Product card links should have visible focus indicators for keyboard navigation."6364 # Product card without proper positioning context65 - pattern: "(?i)<article[^>]*(?:product.*card|card.*product)[^>]*>"66 pattern_negate: "(position.*relative|position: relative)"67 message: "Product card containers should have position: relative for proper link overlay positioning."6869 # Product card missing aria-labelledby70 - pattern: "(?i)<article[^>]*(?:product.*card|card.*product)[^>]*>"71 pattern_negate: "aria-labelledby=\"[^\"]+\""72 message: "Product card articles should have aria-labelledby referencing the heading ID for better screen reader context."7374 # Product card heading missing ID75 - pattern: "(?i)<(h1|h2|h3|h4|h5|h6)[^>]*(?:product.*title|title.*product)[^>]*>"76 pattern_negate: "id=\"[^\"]+\""77 message: "Product card headings should have unique ID attributes for aria-labelledby reference."7879 # Mouse-only link missing tabindex="-1"80 - pattern: "(?i)<a[^>]*class=\"[^\"]*product-link-mouse[^\"]*\"[^>]*>"81 pattern_negate: "tabindex=\"-1\""82 message: "Mouse-only product links should have tabindex='-1' to remove them from tab order."8384 - type: suggest85 message: |86 **Product Card Component Accessibility Best Practices:**8788 **Required Structure:**89 - **article element:** Wrap each product card with the article element for semantic meaning90 - **Single tab-stop:** Each product card should contain only one keyboard tab-stop91 - **Link overlay:** Use absolutely positioned link that covers the entire card area92 - **Heading wraps link:** The heading should wrap the link element to maintain proper heading semantics9394 **ARIA and Semantic Requirements:**95 - **article role:** Implicit with article element, provides semantic structure96 - **aria-labelledby:** Reference to the heading ID for article labeling97 - **Heading ID:** Unique ID attribute for aria-labelledby reference98 - **Link text:** Product title should be descriptive and unique99 - **Image alt text:** Provide descriptive alt text for product images100 - **Price text:** Use visible text for pricing information101 - **Description text:** Use paragraph elements for product descriptions102103 **Keyboard Navigation Requirements:**104 - **Single tab-stop:** Each product card should be navigable with one tab key press105 - **Enter key:** Activate the link to navigate to product detail page106 - **Focus indicators:** Provide clear visual focus indicators for keyboard users107 - **Logical order:** Ensure tab order follows visual layout108 - **Mouse-only links:** Use tabindex="-1" to remove mouse-only links from tab order109110 **CSS Positioning Requirements:**111 - **position: relative:** Set on card container for absolute positioning context112 - **position: absolute:** Set on link element to cover card area113 - **z-index:** Ensure link appears above other card content114 - **pointer-events:** May need to manage pointer events for interactive elements115116 **Implementation Patterns:**117118 **Basic Product Card Structure:**119```html120 <article class="product-card" aria-labelledby="product-123-title">121 <a href="/product/123" class="product-link-mouse" aria-hidden="true" tabindex="-1"></a>122 <h2 id="product-123-title" class="product-title">123 <a href="/product/123" class="product-link">Product Name</a>124 </h2>125 <img src="product-image.jpg" alt="Product description" class="product-image">126 <div class="product-price">$29.99</div>127 <p class="product-description">Product description text...</p>128 </article>129```130131 **CSS for Visual Layout:**132```css133 .product-card {134 position: relative;135 display: flex;136 flex-direction: column;137 }138139 .product-title {140 order: 2; /* Move heading after image visually */141 margin: 0;142 padding: 16px;143 }144145 .product-image {146 order: 1; /* Move image before heading visually */147 width: 100%;148 height: 200px;149 object-fit: cover;150 }151```152153 **CSS for Link Overlay:**154```css155 .product-card {156 position: relative;157 /* Other card styling */158 }159160 .product-link {161 position: absolute;162 top: 0;163 left: 0;164 right: 0;165 bottom: 0;166 z-index: 1;167 text-decoration: none;168 /* Ensure link text is visible */169 color: inherit;170 }171172 .product-title {173 /* Style the title within the link */174 margin: 0;175 padding: 1rem;176 }177```178179 **Advanced Product Card with Interactive Elements:**180```html181 <article class="product-card" aria-labelledby="product-123-title">182 <a href="/product/123" class="product-link-mouse" aria-hidden="true" tabindex="-1"></a>183 <h2 id="product-123-title" class="product-title">184 <a href="/product/123" class="product-link">Product Name</a>185 </h2>186 <img src="product-image.jpg" alt="Product description" class="product-image">187 <div class="product-price">$29.99</div>188 <button class="add-to-cart">Add to Cart</button>189 </article>190```191192 **CSS for Advanced Layout:**193```css194 .product-card {195 position: relative;196 display: flex;197 flex-direction: column;198 }199200 .product-title {201 order: 2; /* Heading appears after image visually */202 margin: 0;203 padding: 16px;204 }205206 .product-image {207 order: 1; /* Image appears before heading visually */208 width: 100%;209 height: 200px;210 object-fit: cover;211 }212213 .product-content {214 order: 3; /* Content appears after heading */215 padding: 0 16px 16px;216 position: relative;217 }218```219220 **CSS for Interactive Elements:**221```css222 .product-card {223 position: relative;224 }225226 .product-link {227 position: absolute;228 top: 0;229 left: 0;230 right: 0;231 bottom: 0;232 z-index: 1;233 /* Allow clicks to pass through to buttons */234 pointer-events: none;235 }236237 .product-title {238 pointer-events: auto;239 }240241 .add-to-cart {242 position: relative;243 z-index: 2;244 /* Ensure button is above link overlay */245 }246```247248 **JavaScript Considerations:**249 - Ensure proper event handling for interactive elements within cards250 - Manage pointer events appropriately for overlay links251 - Test keyboard navigation flow with screen readers252 - Verify that screen readers can access all card content253 - Consider implementing skip links for large product grids254255 **Accessibility Notes:**256 - Screen readers will announce the link text (product title) when focusing257 - Other card content remains discoverable through normal screen reader navigation258 - The article element provides semantic structure for screen readers259 - Ensure sufficient color contrast for all text elements260 - Test with various screen reader and browser combinations261 - Consider implementing skip links for large product grids (10+ items)262263 **Testing Requirements:**264 - Navigate using Tab key only - each card should be one tab-stop265 - Use screen reader to verify all content is accessible266 - Test with keyboard-only navigation267 - Verify focus indicators are visible and clear268 - Test with high contrast mode enabled269270metadata:271 priority: high272 version: 1.0273</rule>274description:275globs:276alwaysApply: false277---278
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/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 |
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/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
