Cursor rule
.cursor/rules/accordion-accessibility.mdcAccordion component accessibility compliance and WAI-ARIA Accordion Pattern
Cursor rules
Quality
40/100
Scores the file, not the repository.Length
722 words
1 headings · 3 code blocksRepository
428
— · pushed 17 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Accordion Component Accessibility Standards89Ensures accordion components follow WCAG compliance and WAI-ARIA Accordion Pattern specifications.1011<rule>12name: accordion_accessibility_standards13description: Enforce accordion component accessibility standards and WAI-ARIA Accordion Pattern compliance14filters:15 - type: file_extension16 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts)$"1718actions:1920- type: enforce21 conditions:2223 # Accordion header button role requirement2425 - pattern: "(?i)<button[^>]_(?:accordion|expand|collapse)[^>]_>"26 pattern_negate: "role=\"button\""27 message: "Accordion header buttons should have role='button' (or use native button element which has implicit role)."2829 # Accordion header missing aria-expanded3031 - pattern: "(?i)<button[^>]_(?:accordion|expand|collapse)[^>]_>"32 pattern_negate: "aria-expanded=\"(true|false)\""33 message: "Accordion header buttons must have aria-expanded attribute set to 'true' or 'false'."3435 # Accordion header missing aria-controls3637 - pattern: "(?i)<button[^>]_(?:accordion|expand|collapse)[^>]_>"38 pattern_negate: "aria-controls=\"[^\"]+\""39 message: "Accordion header buttons must have aria-controls attribute referencing the ID of the associated panel."4041 # Heading wrapper missing role4243 - pattern: "(?i)<(div|section)[^>]*(?:accordion.*header|header._accordion)[^>]_>"44 pattern_negate: "role=\"heading\""45 message: "Accordion header wrappers should have role='heading' or use native heading elements (h1-h6)."4647 # Heading role missing aria-level4849 - pattern: "(?i)<[^>]_role=\"heading\"[^>]_>"50 pattern_negate: "aria-level=\"[1-6]\""51 message: "Elements with role='heading' must have aria-level attribute set to appropriate level (1-6)."5253 # Panel missing proper identification5455 - pattern: "(?i)<(div|section)[^>]*(?:accordion.*panel|panel._accordion)[^>]_>"56 pattern_negate: "id=\"[^\"]+\""57 message: "Accordion panels must have unique ID attributes for aria-controls reference."5859 # Panel with region role missing aria-labelledby6061 - pattern: "(?i)<[^>]_role=\"region\"[^>]_>"62 pattern_negate: "aria-labelledby=\"[^\"]+\""63 message: "Accordion panels with role='region' must have aria-labelledby referencing the heading element."6465 # Missing keyboard event handlers6667 - pattern: "(?i)<button[^>]_(?:accordion|expand|collapse)[^>]_>"68 pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown)"69 message: "Accordion header buttons should handle keyboard events (Enter, Space, optionally Arrow keys)."7071 # Missing Escape key support for accordion content7273 - pattern: "(?i)<(div|section)[^>]*(?:accordion.*panel|panel._accordion)[^>]_>"74 pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown)"75 message: "Accordion panels should handle Escape key to close panel and return focus to header."7677- type: suggest78 message: |79 **Accordion Component Accessibility Best Practices:**8081 **Required ARIA Attributes:**8283 - **role='button':** Set on accordion header elements (or use native button)84 - **role='heading':** Set on accordion header container with aria-level85 - **aria-expanded:** 'true' if panel is visible, 'false' if collapsed86 - **aria-controls:** Reference to the ID of the associated panel content87 - **aria-level:** Appropriate heading level (1-6) for information architecture88 - **aria-disabled:** 'true' if panel cannot be collapsed (optional)8990 **Optional ARIA Attributes:**9192 - **role='region':** On panel content containers (avoid with >6 panels)93 - **aria-labelledby:** On panels with role='region', referencing the heading element9495 **Keyboard Interaction Requirements:**9697 - **Enter/Space:** Toggle panel expansion/collapse98 - **Tab/Shift+Tab:** Move through all focusable elements in page order99 - **Down/Up Arrow:** (Optional) Navigate between accordion headers100 - **Home/End:** (Optional) Jump to first/last accordion header101 - **Escape:** Close open panel and return focus to header button102103 **Structure Requirements:**104105 - Header button must be the only element inside heading container106 - Each panel must have unique ID for aria-controls reference107 - Use native heading elements (h1-h6) when possible instead of role='heading'108 - Avoid role='region' on panels when many accordions exist (>6 panels)109110 **Implementation Patterns:**111112 **Single Accordion Item:**113114```html115 <div class="accordion-item">116 <h3117 role="heading"118 aria-level="3"119 id="header-1"120 >121 <button122 aria-expanded="false"123 aria-controls="panel-1"124 >125 Section Title126 </button>127 </h3>128 <div129 id="panel-1"130 role="region"131 aria-labelledby="header-1"132 hidden133 >134 <p>Panel content...</p>135 </div>136 </div>137```138139 **JavaScript for Accordion with Escape Support:**140141```javascript142 function toggleAccordion(button) {143 const isExpanded = button.getAttribute('aria-expanded') === 'true';144 const panel = document.getElementById(button.getAttribute('aria-controls'));145146 button.setAttribute('aria-expanded', !isExpanded);147 panel.hidden = isExpanded;148149 if (!isExpanded) {150 // Add escape key listener to panel151 panel.addEventListener('keydown', handleAccordionEscapeKey);152 } else {153 // Remove escape key listener154 panel.removeEventListener('keydown', handleAccordionEscapeKey);155 }156 }157158 function handleAccordionEscapeKey(event) {159 if (event.key === 'Escape') {160 const panel = event.target.closest('[hidden]');161 if (panel) {162 const button = document.querySelector(`[aria-controls="${panel.id}"]`);163 if (button) {164 button.setAttribute('aria-expanded', 'false');165 panel.hidden = true;166 button.focus(); // Return focus to header167 panel.removeEventListener('keydown', handleAccordionEscapeKey);168 }169 }170 }171 }172```173174 **Using Native Heading:**175176```html177 <div class="accordion-item">178 <h3 id="header-2">179 <button180 aria-expanded="false"181 aria-controls="panel-2"182 >183 Section Title184 </button>185 </h3>186 <div187 id="panel-2"188 aria-labelledby="header-2"189 >190 <p>Panel content...</p>191 </div>192 </div>193```194195 **JavaScript Considerations:**196197 - Implement Enter and Space key handlers for expansion/collapse198 - Optionally implement Arrow key navigation between headers199 - Update aria-expanded state when panels toggle200 - Consider implementing single-expand vs multi-expand behavior201 - Use hidden attribute or CSS to show/hide panels (note: CSS visibility property can be animated)202 - Ensure smooth keyboard navigation flow203 - Implement Escape key handler to close open panel and return focus to header204 - Add/remove event listeners when panels open/close to manage Escape key support205206 **Accessibility Notes:**207208 - Role 'region' helps screen readers understand panel structure209 - Avoid role='region' proliferation with many simultaneous panels210 - Button should be direct child of heading element211 - Consider aria-disabled='true' for panels that cannot be collapsed212 - Test with screen readers to ensure proper announcement213214metadata:215priority: high216version: 1.0217</rule>218
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/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 | |
| Shopify/horizon.cursor/rules/mobile-accessibility-standards.mdc · 428 | Cursor rules | styleui | 36/100 | 3 days ago |
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 Diff against .cursor/rules/mobile-accessibility-standards.mdc
