RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/Cursor rules/Shopify/horizon

Cursor rule

.cursor/rules/combobox-accessibility.mdc

Combobox component accessibility compliance pattern

Cursor rules

Quality

40/100

Scores the file, not the repository.

Length

927 words

1 headings · 1 code blocks

Repository

428

— · pushed 17 days ago

Last changed

3 days ago

First indexed 3 days ago.
Shopify/horizon/.cursor/rules/combobox-accessibility.mdcRawGitHub
1---
2description: Combobox component accessibility compliance pattern
3globs: *.vue, *.jsx, *.tsx, *.html, *.php, *.js, *.ts, *.liquid
4alwaysApply: false
5---
6# Combobox Component Accessibility Standards
7 
8Ensures combobox components follow WCAG compliance and WAI-ARIA Combobox Pattern specifications.
9 
10<rule>
11name: combobox_accessibility_standards
12description: Enforce combobox component accessibility standards and WAI-ARIA Combobox Pattern compliance
13filters:
14 - type: file_extension
15 pattern: "\\.(vue|jsx|tsx|html|liquid|php|js|ts)$"
16 
17actions:
18 - type: enforce
19 conditions:
20 # Combobox role requirement
21 - pattern: "(?i)<(div|section)[^>]*(?:combobox|autocomplete)[^>]*>"
22 pattern_negate: "role=\"combobox\""
23 message: "Combobox containers must have role='combobox' attribute."
24 
25 # aria-expanded requirement
26 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
27 pattern_negate: "aria-expanded=\"(true|false)\""
28 message: "Combobox elements must have aria-expanded attribute set to 'true' or 'false'."
29 
30 # aria-haspopup requirement
31 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
32 pattern_negate: "aria-haspopup=\"listbox\""
33 message: "Combobox elements must have aria-haspopup='listbox' attribute."
34 
35 # aria-controls requirement
36 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
37 pattern_negate: "aria-controls=\"[^\"]+\""
38 message: "Combobox elements must have aria-controls attribute referencing the ID of the associated listbox."
39 
40 # aria-autocomplete requirement
41 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
42 pattern_negate: "aria-autocomplete=\"(list|both|inline|none)\""
43 message: "Combobox elements must have aria-autocomplete attribute set to 'list', 'both', 'inline', or 'none'."
44 
45 # aria-activedescendant requirement when expanded
46 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*aria-expanded=\"true\"[^>]*>"
47 pattern_negate: "aria-activedescendant=\"[^\"]+\""
48 message: "Expanded combobox elements must have aria-activedescendant attribute referencing the ID of the active option."
49 
50 # Listbox role requirement
51 - pattern: "(?i)<(div|ul)[^>]*(?:listbox|dropdown|popup)[^>]*>"
52 pattern_negate: "role=\"listbox\""
53 message: "Listbox containers must have role='listbox' attribute."
54 
55 # Option role requirement
56 - pattern: "(?i)<(div|li)[^>]*(?:option|item)[^>]*>"
57 pattern_negate: "role=\"option\""
58 message: "Listbox options must have role='option' attribute."
59 
60 # Option ID requirement for aria-activedescendant
61 - pattern: "(?i)<[^>]*role=\"option\"[^>]*>"
62 pattern_negate: "id=\"[^\"]+\""
63 message: "Listbox options must have unique id attributes for aria-activedescendant to reference them."
64 
65 # aria-selected requirement for options
66 - pattern: "(?i)<[^>]*role=\"option\"[^>]*>"
67 pattern_negate: "aria-selected=\"(true|false)\""
68 message: "Listbox options must have aria-selected attribute set to 'true' or 'false'."
69 
70 # Missing keyboard event handlers
71 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
72 pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown)"
73 message: "Combobox elements should handle keyboard events (Arrow keys, Enter, Escape, etc.)."
74 
75 # Missing status region
76 - pattern: "(?i)<[^>]*role=\"combobox\"[^>]*>"
77 pattern_negate: "aria-controls=\"[^\"]+\".*?<[^>]*role=\"status\""
78 message: "Combobox should have a status region to announce available options."
79 
80 - type: suggest
81 message: |
82 **Combobox Component Accessibility Best Practices:**
83 
84 **Required ARIA Attributes:**
85 - **role='combobox':** Set on the input container element
86 - **aria-expanded:** 'true' if listbox is visible, 'false' if hidden
87 - **aria-haspopup='listbox':** Indicates the combobox has a listbox popup
88 - **aria-controls:** Reference to the ID of the associated listbox
89 - **aria-autocomplete:** 'list', 'both', 'inline', or 'none' based on behavior
90 - **aria-activedescendant:** Reference to the ID of the currently active option (remove when listbox is hidden)
91 - **role='listbox':** Set on the popup container element (preferably on a `ul` element)
92 - **role='option':** Set on each selectable item in the listbox (preferably on an `li` element)
93 - **id:** Unique ID on each option element for `aria-activedescendant` to reference
94 - **aria-selected:** 'true' or 'false' on each option
95 - **role='status':** Set on a visually hidden element to announce available options
96 
97 **Keyboard Interaction Requirements:**
98 - **Down Arrow:** Open listbox and move focus to first option
99 - **Up Arrow:** Open listbox and move focus to last option
100 - **Enter/Space:** Select focused option and close listbox
101 - **Escape:** Close listbox without selection
102 - **Tab:** Move focus to next focusable element
103 - **Shift+Tab:** Move focus to previous focusable element
104 - **Home/End:** Move focus to first/last option
105 - **Character Keys:** Filter options based on input
106 
107 **Focus Management:**
108 - Focus should remain on the input while navigating options
109 - Use aria-activedescendant to indicate the currently focused option
110 - Return focus to input after selection or closing
111 - Ensure focus is trapped within the combobox while open
112 
113 **Status Region Requirements:**
114 - Must announce number of available options when listbox opens
115 - Must announce when no options are available
116 - Must use proper pluralization ("1 item available" vs "2 items available")
117 - Must be visually hidden but available to screen readers
118 - Should update dynamically as options are filtered
119 
120 **Semantic HTML Structure:**
121 - Use `ul` element for the listbox container
122 - Use `li` elements for individual options
123 - This provides better semantic structure and is more appropriate for lists
124 
125 **Implementation Example:**
126```html
127 <div class="combobox-container">
128 <label for="combobox-input">Select an option:</label>
129 <input type="text"
130 id="combobox-input"
131 role="combobox"
132 aria-expanded="false"
133 aria-haspopup="listbox"
134 aria-controls="listbox-popup"
135 aria-autocomplete="list">
136 <ul id="listbox-popup"
137 role="listbox"
138 hidden>
139 <li role="option"
140 id="option-1"
141 aria-selected="false">
142 Option 1
143 </li>
144 <li role="option"
145 id="option-2"
146 aria-selected="false">
147 Option 2
148 </li>
149 </ul>
150 <div id="listbox-status"
151 role="status"
152 class="visually-hidden">
153 <!-- Status messages will be dynamically updated -->
154 </div>
155 </div>
156 
157 <style>
158 .visually-hidden {
159 position: absolute;
160 width: 1px;
161 height: 1px;
162 padding: 0;
163 margin: -1px;
164 overflow: hidden;
165 clip: rect(0, 0, 0, 0);
166 white-space: nowrap;
167 border: 0;
168 }
169 
170 #listbox-popup {
171 list-style: none;
172 padding: 0;
173 margin: 0;
174 }
175 </style>
176 
177 <script>
178 const input = document.getElementById('combobox-input');
179 const listbox = document.getElementById('listbox-popup');
180 const statusElement = document.getElementById('listbox-status');
181 
182 // Status message handling
183 function updateStatusMessage(count) {
184 if (count === 0) {
185 statusElement.textContent = 'No items available';
186 } else {
187 statusElement.textContent = `${count} ${count === 1 ? 'item' : 'items'} available`;
188 }
189 }
190 
191 // Show listbox
192 function showListbox() {
193 listbox.hidden = false;
194 input.setAttribute('aria-expanded', 'true');
195 const options = listbox.querySelectorAll('[role="option"]');
196 updateStatusMessage(options.length);
197 }
198 
199 // Hide listbox
200 function hideListbox() {
201 listbox.hidden = true;
202 input.setAttribute('aria-expanded', 'false');
203 input.removeAttribute('aria-activedescendant'); // Important: remove when hiding
204 statusElement.textContent = '';
205 }
206 
207 // Set active option
208 function setActiveOption(optionId) {
209 input.setAttribute('aria-activedescendant', optionId);
210 }
211 
212 // Example usage:
213 // When opening listbox with options:
214 showListbox();
215 // When setting active option:
216 setActiveOption('option-1');
217 // When closing listbox:
218 hideListbox();
219 </script>
220```
221 
222 **JavaScript Considerations:**
223 - Implement proper event listeners for all keyboard interactions
224 - Update ARIA attributes dynamically based on state
225 - **Remove `aria-activedescendant` when hiding the listbox** to avoid referencing hidden elements
226 - Handle focus management and trapping
227 - Implement proper filtering and selection logic
228 - Update status messages for all state changes
229 - Ensure proper pluralization in status messages
230 - Handle edge cases (no matches, empty input, etc.)
231 
232 **Accessibility Notes:**
233 - Status region helps screen readers understand available options
234 - Proper pluralization improves user experience
235 - Clear status messages help users understand the current state
236 - Visual feedback should match announced status
237 - Test with screen readers to ensure proper announcement
238 
239metadata:
240 priority: high
241 version: 1.0
242</rule>
243 

Sections

  • Combobox Component Accessibility Standards

What it covers

ui

Glob targeting

  • *.vue
  • *.jsx
  • *.tsx
  • *.html
  • *.php
  • *.js
  • *.ts
  • *.liquid

Format

Cursor rules

The most expressive format here. Many small .mdc files, each with frontmatter declaring when it should load, so a rule about migrations only enters context when a migration is open. Costs the most to maintain and only one editor reads it.

What the corpus says about it

Repository

Owner
Shopify
Language
—
License
—
Archived
no

All configs in this repo

Also in Shopify/horizon

Diff this repo’s formats

One 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?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
Shopify/horizon.cursor/rules/accordion-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/animation-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/blocks.mdc · 428Cursor rulesunclassifiedstylearchtypesdatabase+262/1003 days ago
Shopify/horizon.cursor/rules/breadcrumb-accessibility.mdc · 428Cursor rulesunclassifiedui36/1003 days ago
Shopify/horizon.cursor/rules/carousel-accessibility.mdc · 428Cursor rulesunclassifiedui32/1003 days ago
Shopify/horizon.cursor/rules/cart-drawer-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/chat-window-accessibility.mdc · 428Cursor rulesunclassifiedstyleui24/1003 days ago
Shopify/horizon.cursor/rules/color-contrast-accessibility.mdc · 428Cursor rulesunclassifiedlint-formatui44/1003 days ago
Shopify/horizon.cursor/rules/color-swatch-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/commit-messages.mdc · 428Cursor rulesunclassifiedsetuplint-formattypesgit62/1003 days ago
Shopify/horizon.cursor/rules/css-standards.mdc · 428Cursor rulesunclassifiedstylearchuiperformance+349/1003 days ago
Shopify/horizon.cursor/rules/disclosure-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/dropdown-navigation-accessibility.mdc · 428Cursor rulesunclassifiedstyleui36/1003 days ago
Shopify/horizon.cursor/rules/flip-card-accessibility.mdc · 428Cursor rulesunclassifiedstyleui36/1003 days ago
Shopify/horizon.cursor/rules/form-accessibility.mdc · 428Cursor rulesunclassifiedui32/1003 days ago
Shopify/horizon.cursor/rules/javascript-standards.mdc · 428Cursor rulesunclassifiedstylearchtypesui+254/1003 days ago
Shopify/horizon.cursor/rules/landmark-accessibility.mdc · 428Cursor rulesunclassifiedui40/1003 days ago
Shopify/horizon.cursor/rules/liquid.mdc · 428Cursor rulesunclassifiedbuildstyletypesdatabase+277/1003 days ago
Shopify/horizon.cursor/rules/locales.mdc · 428Cursor rulesunclassifiedarch49/1003 days ago
Shopify/horizon.cursor/rules/localization.mdc · 428Cursor rulesunclassifiedstyle54/1003 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
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack