diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..b4b7d81bf Binary files /dev/null and b/.DS_Store differ diff --git a/.cursor/commands/fix-accesibility-issue.md b/.cursor/commands/fix-accesibility-issue.md deleted file mode 100644 index e9d089808..000000000 --- a/.cursor/commands/fix-accesibility-issue.md +++ /dev/null @@ -1,92 +0,0 @@ -# Fix Accessibility Issue - -Analyze and fix accessibility issues in web applications, following the standard GitHub issue workflow with accessibility-specific considerations. - -## Usage - -```bash -Fix accessibility issue in [COMPONENT_NAME] or Fix GitHub accessibility issue #[NUMBER] -``` - -## Accessibility Rules Reference - -**IMPORTANT**: Use the `fetch_rules` tool to access specific accessibility rules for components you're working on: - -- `accordion-accessibility`: Accordion component ARIA patterns -- `breadcrumb-accessibility`: Breadcrumb navigation patterns -- `carousel-accessibility`: Carousel/slider ARIA compliance -- `cart-drawer-accessibility`: Cart drawer accessibility patterns -- `color-swatch-accessibility`: Color swatch component patterns -- `combobox-accessibility`: Combobox/dropdown ARIA patterns -- `disclosure-accessibility`: Disclosure/collapsible content patterns -- `dropdown-navigation-accessibility`: Navigation dropdown patterns -- `modal-accessibility`: Modal/dialog ARIA Dialog Pattern -- `product-card-accessibility`: Product card accessibility patterns -- `product-filter-accessibility`: Product filtering interface patterns -- `sale-price-accessibility`: Sale price display patterns -- `slider-accessibility`: Slider/range input patterns -- `switch-accessibility`: Toggle switch patterns -- `tab-accessibility`: Tab interface patterns -- `tooltip-accessibility`: Tooltip accessibility patterns - -Always fetch and follow the relevant rule(s) for the component you're fixing. - -## Base Workflow - -**Follow the complete workflow from `fix-github-issue` command**, including: - -- Testing requirements (full test suite with `--reporter=line`) -- Commit standards (one-liner messages only) -- Test failure handling and selector updates -- Force push safety with `--force-with-lease` -- Write down learnings as a last step - -## Accessibility-Specific Principles - -### Critical Implementation Rules - -- **Role must be on the element that contains the items** - not the wrapper -- **Screen readers need direct parent-child relationship** between role and items -- **Test with actual screen readers**, not just markup validation -- **Fetch specific component rules** before implementing fixes - -### Testing Requirements - -In addition to standard testing requirements: - -- **Update page object models** when changing roles (e.g., `navigation` → `menubar`) -- **Test with screen readers** to verify actual behavior, not just markup -- **Verify individual items are recognized**, not just containers -- **Test focus states thoroughly** by navigating away and back to components -- **Accessibility tests**: Place in `tests/suites/theme1/accessibility/` folder -- **View parameters**: Only add `?view=` parameter to URLs when specifically needed for test configuration (e.g., when using custom test templates) - -### Focus Management Best Practices - -- **Consistent focus behavior** across all interaction methods (keyboard vs mouse) -- **Focus state bugs are subtle** - may look correct but behave wrong on subsequent interactions -- **Reset focus state properly** when closing dropdowns/menus with ESC vs selection -- **Centralize focus management logic** to avoid duplication and inconsistency - -### Performance Considerations - -- **Complex keyboard navigation can introduce lag** - balance functionality with performance -- **Test on slower devices** to ensure accessibility JavaScript doesn't impact UX -- **Consider simpler solutions first** before implementing custom keyboard handling - -### Implementation Guidelines - -- **Fetch and follow existing rules** for the component type you're working on -- **Search for existing ARIA patterns** in the codebase first -- **Make minimal changes** that improve accessibility -- **Focus on semantic correctness** over visual changes -- **Ensure backward compatibility** -- **Don't over-engineer** - native browser behavior often suffices -- **Use `aria-labelledby`** when referencing existing visible text instead of duplicating with `aria-label` - -### Code Quality Standards - -- **Avoid duplicate logic** between keyboard and mouse interaction handlers -- **Single responsibility principle** - separate ARIA state management from focus management -- **Centralize common patterns** like focus reset and state management -- **Refactor when you find redundancy** in accessibility implementations diff --git a/.cursor/commands/fix-breaking-changes.md b/.cursor/commands/fix-breaking-changes.md deleted file mode 100644 index ab77bc12a..000000000 --- a/.cursor/commands/fix-breaking-changes.md +++ /dev/null @@ -1,246 +0,0 @@ -# Breaking Changes Fix Command - -## Command: Fix Shopify Theme Breaking Changes - -### Prerequisites - -- `breaking-changes.md` file exists in project root with fix instructions -- Modify files in the `templates/` folder and `config/settings_data.json` -- Use scripts when possible, clean up temporary files afterward - -### Step-by-Step Process - -1. **Analyze Breaking Changes Documentation** - - - Read the `breaking-changes.md` file to understand what needs to be fixed - - Identify the specific changes required based on the documentation - - **Look for:** - - - Settings that need to be removed - - Block types that need to be changed - - Property values that need to be updated - - Context changes (e.g., `closest.product` vs explicit settings) - -2. **Create Adaptable Fix Script Template** - Create `scripts/fix-breaking-changes.js` and customize the `applyFixes` function based on breaking-changes.md: - - ```javascript - #!/usr/bin/env node - const fs = require('fs'); - const path = require('path'); - const templatesDir = './templates'; - const configFile = './config/settings_data.json'; - - // ======================================== - // CUSTOMIZE THIS SECTION BASED ON breaking-changes.md - // ======================================== - function applyFixes(obj) { - if (typeof obj !== 'object' || obj === null) return obj; - if (Array.isArray(obj)) return obj.map(applyFixes); - - const result = { ...obj }; - - // ADD YOUR BREAKING CHANGE FIXES HERE - // Examples based on common patterns: - - // 1. Remove specific settings (adapt key/value as needed) - if (result.settings) { - // Example: Remove closest.product/collection references - // if (result.settings.product === "{{ closest.product }}") delete result.settings.product; - // if (result.settings.collection === "{{ closest.collection }}") delete result.settings.collection; - // Example: Remove deprecated settings - // delete result.settings.deprecated_setting; - } - - // 2. Update block types (adapt old/new types as needed) - // Example: Change block types in specific contexts - // if (result.type === 'old-block-type') result.type = 'new-block-type'; - - // 3. Update setting values (adapt property/old/new values as needed) - // if (result.settings && result.settings.some_property === 'old-value') { - // result.settings.some_property = 'new-value'; - // } - - // 4. Rename properties (adapt old/new property names as needed) - // if (result.old_property_name) { - // result.new_property_name = result.old_property_name; - // delete result.old_property_name; - // } - - // Recursively process nested objects - for (const key in result) { - if (typeof result[key] === 'object' && result[key] !== null) { - result[key] = applyFixes(result[key]); - } - } - - return result; - } - // ======================================== - // END CUSTOMIZATION SECTION - // ======================================== - - function processTemplateFile(filePath) { - try { - console.log(`Processing ${filePath}...`); - const content = fs.readFileSync(filePath, 'utf8'); - - // Preserve comment headers - const commentMatch = content.match(/^(\/\*[\s\S]*?\*\/)\s*/); - const comment = commentMatch ? commentMatch[1] : ''; - const jsonContent = commentMatch ? content.slice(commentMatch[0].length) : content; - - // Parse and apply fixes - const data = JSON.parse(jsonContent); - const processedData = applyFixes(data); - - // Write back with preserved formatting - const updatedJsonContent = JSON.stringify(processedData, null, 2); - const updatedContent = comment ? comment + '\n' + updatedJsonContent : updatedJsonContent; - - fs.writeFileSync(filePath, updatedContent); - console.log(`✓ Updated ${filePath}`); - } catch (error) { - console.error(`Error processing ${filePath}:`, error.message); - } - } - - function processConfigFile(filePath) { - try { - console.log(`Processing ${filePath}...`); - const content = fs.readFileSync(filePath, 'utf8'); - const data = JSON.parse(content); - const processedData = applyFixes(data); - - const updatedContent = JSON.stringify(processedData, null, 2); - fs.writeFileSync(filePath, updatedContent); - console.log(`✓ Updated ${filePath}`); - } catch (error) { - console.error(`Error processing ${filePath}:`, error.message); - } - } - - function main() { - console.log('🔧 Fixing breaking changes in template files and config...\n'); - - // Process template files - const files = fs.readdirSync(templatesDir); - const jsonFiles = files.filter((file) => file.endsWith('.json')); - - if (jsonFiles.length > 0) { - console.log(`Found ${jsonFiles.length} template files to process:\n`); - jsonFiles.forEach((file) => { - const filePath = path.join(templatesDir, file); - processTemplateFile(filePath); - }); - } else { - console.log('No JSON template files found.'); - } - - // Process config file - if (fs.existsSync(configFile)) { - console.log(`\nProcessing config file: ${configFile}`); - processConfigFile(configFile); - } else { - console.log(`\nConfig file not found: ${configFile}`); - } - - console.log('\n✅ All template files and config have been processed!'); - console.log('Next: Run theme check to verify fixes'); - } - - if (require.main === module) main(); - ``` - -3. **Customize the Fix Script** - Based on your `breaking-changes.md` analysis, uncomment and modify the relevant fix patterns in the `applyFixes` function. - - **Common Fix Patterns:** - - - **Settings Removal**: `if (result.settings.key === 'value') delete result.settings.key;` - - **Block Type Changes**: `if (result.type === 'old-type') result.type = 'new-type';` - - **Value Updates**: `if (result.settings.prop === 'old') result.settings.prop = 'new';` - - **Property Renaming**: `result.newName = result.oldName; delete result.oldName;` - -4. **Execute Fix Script** - - ```bash - node fix-breaking-changes.js - ``` - -5. **Verify Fixes** - - ```bash - shopify theme check --fail-level error - ``` - -6. **Review and Iterate** - If theme check still shows errors: - - - Analyze remaining issues - - Update the `applyFixes` function - - Re-run the script - - Verify again - -7. **Handle Issues Outside Templates and Config** - If theme check still fails after template and config fixes, some issues may require changes outside the `templates/` folder and `config/settings_data.json`. - - **STOP HERE** and: - - - Run theme check again to capture remaining errors - - Analyze which files outside `templates/` and `config/` need changes - - Identify what specific changes are required - - **Create a summary report:** - - - List which files outside `templates/` and `config/` need changes - - Document what specific changes are required - - Note that these fixes require manual intervention - - Examples of files that might need fixes: - - `sections/*.liquid` - - `blocks/*.liquid` - - `snippets/*.liquid` - - `assets/*.js` or `assets/*.css` - - Schema files in `schemas/` - - **Report format:** - - 🚫 BREAKING CHANGES REQUIRING MANUAL FIXES OUTSIDE TEMPLATES/ AND CONFIG/ - - The following issues cannot be resolved by modifying templates/ and config/ only: - - 1. [File path] - [Description of required change] - 2. [File path] - [Description of required change] - - These require manual review and fixes in non-template and non-config files. - -8. **Clean Up** - - ```bash - rm scripts/fix-breaking-changes.js - # Keep remaining-issues.txt if there are unresolved issues - ``` - -### Expected Outcome - -- All breaking changes in template files and config are fixed -- Theme check passes with no errors -- Template files and config follow new architecture requirements -- Temporary files are cleaned up - -### Framework Benefits - -- **Adaptable**: Easily customizable for any breaking change type -- **Reusable**: Same process works for future releases -- **Safe**: Preserves JSON formatting and comment headers -- **Comprehensive**: Handles nested objects and arrays recursively -- **Targeted**: Modifies both templates and config as required - -### Notes for Future Use - -1. **Always start** by thoroughly reading `breaking-changes.md` -2. **Identify patterns** in the breaking changes (settings removal, type changes, etc.) -3. **Use the template** and customize the `applyFixes` function accordingly -4. **Test incrementally** - run theme check after each fix type -5. **Document your fixes** in the script comments for future reference diff --git a/.cursor/commands/get-breaking-changes.md b/.cursor/commands/get-breaking-changes.md deleted file mode 100644 index 72f88b573..000000000 --- a/.cursor/commands/get-breaking-changes.md +++ /dev/null @@ -1,116 +0,0 @@ -# Get Breaking Changes - -This command helps identify confirmed breaking changes merged since the last breaking version in a repository. - -**Modified Approach**: Instead of searching through commits, this process focuses on pull requests that have been explicitly labeled with "Breaking changes" to identify confirmed breaking changes. - -## Command Steps: - -### 1. Get All SEMVER Tags - -```bash -# Using GitHub CLI to get all SEMVER tags -gh api repos/:owner/:repo/git/refs/tags --jq '.[].ref' | sed 's|refs/tags/||' | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$" | sort -V - -# Alternative using git -git show-ref --tags | grep -E "refs/tags/[0-9]+\.[0-9]+\.[0-9]+$" -``` - -### 2. Identify Last Breaking Version - -- In SEMVER, breaking changes occur when major version increases -- Find the first version in the current major version series (e.g., 1.0.1 for 1.x.x series) -- This represents the last breaking change - -### 3. Get Breaking Version Commit Date - -```bash -# Get commit date for the breaking version tag -git show --format="%ci" --no-patch -``` - -### 4. Find Pull Requests with Breaking Changes Label - -```bash -# Search for merged PRs with "Breaking changes" label merged to main since the breaking version date -gh pr list --label "Breaking changes" --state merged --search "merged:>YYYY-MM-DD base:main" --json number,title,mergedAt,url - -# Example: Find breaking changes PRs merged to main after 2025-05-22 -gh pr list --label "Breaking changes" --state merged --search "merged:>2025-05-22 base:main" --json number,title,mergedAt,url -``` - -### 5. Get PR Details - -Use `fetch_pull_request` tool to get full details of each breaking change PR: - -``` -fetch_pull_request(pullNumberOrCommitHash: "PR_NUMBER") -``` - -### 6. Classify Breaking Changes - -Review each PR and classify by impact type: - -- **API Changes**: Removal or modification of public APIs -- **Configuration Changes**: Removal of settings or configuration options -- **Behavior Changes**: Changes that could break existing functionality -- **Schema Changes**: Changes to data structures or interfaces -- **Removal of Features**: Deprecated or removed functionality - -Save the analysis into the breaking-changes.md file. If the file already contains text, replace it. - -### 7. Analyze Solution Patterns - -Examine the template file changes in each breaking change PR to identify actionable patterns for solving the breaking changes: - -- Look at `/templates` directory changes in the PR diffs -- Identify common patterns in how configurations were updated (settings removal, block type changes, etc.) -- Focus on specific find/replace patterns that can be applied systematically -- Add these patterns to the breaking-changes.md file as shown in the example output format - -## Example Output Format: - -``` -## Breaking Changes Since Last Breaking Version (X.Y.Z) - -### Confirmed Breaking Changes: -1. **PR #XXXX** - "Title" (Merged: DATE) - - **Type**: API Changes/Configuration Changes/Behavior Changes/etc. - - **Impact**: Description of what breaks and how it affects users - -2. **PR #YYYY** - "Title" (Merged: DATE) - - **Type**: Schema Changes - - **Impact**: Description of breaking changes - -### Potential Breaking Changes: -1. **PR #ZZZZ** - "Title" (Merged: DATE) - - **Needs Review**: Description of potential impact that requires further analysis - -## Summary: -X confirmed breaking changes found since version X.Y.Z (DATE) - -**Key Areas of Impact:** -- Brief summary of main breaking change categories -- Notes on merchant/user impact - -## Solving breaking changes - -Based on the template file changes in the breaking change PRs, here are the key patterns to follow when updating themes: - -### Breaking Change Name (PR #XXXX): -- Specific find/replace pattern or configuration change needed -- Another configuration change with before/after example -- Remove/add specific settings or block types - -### Another Breaking Change (PR #YYYY): -- Different set of patterns for this breaking change -- Specific block type replacements needed -- Configuration updates required -``` - -## Usage Notes: - -- Focus on changes that would require users to modify their code/configuration -- Internal refactoring may not be breaking unless it affects public APIs -- Consider both technical breaking changes and UX breaking changes -- Document the rationale for why each change is considered breaking diff --git a/.cursor/commands/prompt-template.md b/.cursor/commands/prompt-template.md deleted file mode 100644 index 06a20f65a..000000000 --- a/.cursor/commands/prompt-template.md +++ /dev/null @@ -1,13 +0,0 @@ -# Title - -## Instructions - -## Steps to resolve issues - -### Step 1: XXXXX - -## What works well - -## What doesn't work well / Pitfalls to Avoid - -## Summary diff --git a/.cursor/commands/update-schema-translations.md b/.cursor/commands/update-schema-translations.md deleted file mode 100644 index 0d4986180..000000000 --- a/.cursor/commands/update-schema-translations.md +++ /dev/null @@ -1,94 +0,0 @@ -# Update Schema Translations - -## Instructions - -This guide helps you translate English strings in theme schema JSON files into multiple languages. - -## Usage - -1. Create the translation data structure -2. Run the translation script with the schema flag - -## Step 1: Create Translation Data Structure - -You must translate English strings in a JSON object into multiple languages. -Use the following structure to create the file `scripts/translation-data.json`: - -```json -{ - "sourceStructure": { - "categories": { - "banners": "Banners", - "decorative": "Decorative", - "storytelling": "Storytelling" - }, - "content": { - "advanced": "Advanced", - "some_key": { - "child_key_1": "Child key 1" - } - } - }, - "wordTranslations": {} -} -``` - -Inside the `wordTranslations` key, match the structure in `sourceStructure`, but add a translation for each language. - -## Getting Language Codes - -You can use the following command to get the list of language codes: - -```bash -ls locales/*.schema.json | grep -v en.default.schema.json | sed 's|locales/||g' | sed 's|\.schema\.json||g' -``` - -## Constraints - -- Match the sourceStructure in the wordTranslations -- Have each leaf key's value be an object that includes translations for each of the languages -- Do not read any other files -- Only add translations for the keys in sourceStructure -- Never add any other keys - -## Step 2: Run the Translation Script - -After creating the `scripts/translation-data.json` file, run: - -```bash -node scripts/update-translations.js --schema -``` - -This will update all schema translation files with the new translations. - -## Example - -For a `sourceStructure` with: - -```json -{ - "categories": { - "banners": "Banners" - } -} -``` - -The `wordTranslations` would look like: - -```json -{ - "categories": { - "banners": { - "fr": "Bannières", - "es": "Banners", - "de": "Banner" - // ... other languages - } - } -} -``` - -## Tips - -- Use CMD+SHIFT+V to paste the JSON structure into cursor as plain text -- The script will automatically delete the `translation-data.json` file after completion diff --git a/.cursor/commands/update-translations.md b/.cursor/commands/update-translations.md deleted file mode 100644 index 7af0f1d80..000000000 --- a/.cursor/commands/update-translations.md +++ /dev/null @@ -1,94 +0,0 @@ -# Update Storefront Translations - -## Instructions - -This guide helps you translate English strings in theme locale JSON files into multiple languages for storefront-facing content. - -## Usage - -1. Create the translation data structure -2. Run the translation script - -## Step 1: Create Translation Data Structure - -You must translate English strings in a JSON object into multiple languages. -Use the following structure to create the file `scripts/translation-data.json`: - -```json -{ - "sourceStructure": { - "actions": { - "add": "Add", - "add_to_cart": "Add to cart" - }, - "blocks": { - "contact_form": { - "name": "Name", - "email": "Email" - } - } - }, - "wordTranslations": {} -} -``` - -Inside the `wordTranslations` key, match the structure in `sourceStructure`, but add a translation for each language. - -## Getting Language Codes - -You can use the following command to get the list of language codes: - -```bash -ls locales/*.json | grep -v schema | grep -v en.default.json | sed 's|locales/||g' | sed 's|\.json||g' -``` - -## Constraints - -- Match the sourceStructure in the wordTranslations -- Have each leaf key's value be an object that includes translations for each of the languages -- Do not read any other files -- Only add translations for the keys in sourceStructure -- Never add any other keys - -## Step 2: Run the Translation Script - -After creating the `scripts/translation-data.json` file, run: - -```bash -node scripts/update-translations.js -``` - -This will update all locale translation files with the new translations. - -## Example - -For a `sourceStructure` with: - -```json -{ - "actions": { - "add": "Add" - } -} -``` - -The `wordTranslations` would look like: - -```json -{ - "actions": { - "add": { - "fr": "Ajouter", - "es": "Añadir", - "de": "Hinzufügen" - // ... other languages - } - } -} -``` - -## Tips - -- Use CMD+SHIFT+V to paste the JSON structure into cursor as plain text -- The script will automatically delete the `translation-data.json` file after completion -- This is for storefront translations (customer-facing text), not schema translations diff --git a/.cursor/rules/accordion-accessibility.mdc b/.cursor/rules/accordion-accessibility.mdc deleted file mode 100644 index 2c2bc9e84..000000000 --- a/.cursor/rules/accordion-accessibility.mdc +++ /dev/null @@ -1,218 +0,0 @@ ---- -description: Accordion component accessibility compliance and WAI-ARIA Accordion Pattern -globs: *.vue, *.jsx, *.tsx, *.html, *.php, *.js, *.ts, *.liquid -alwaysApply: false ---- - -# Accordion Component Accessibility Standards - -Ensures accordion components follow WCAG compliance and WAI-ARIA Accordion Pattern specifications. - - -name: accordion_accessibility_standards -description: Enforce accordion component accessibility standards and WAI-ARIA Accordion Pattern compliance -filters: - - type: file_extension - pattern: "\\.(vue|jsx|tsx|html|php|js|ts|liquid)$" - -actions: - -- type: enforce - conditions: - - # Accordion header button role requirement - - - pattern: "(?i)]_(?:accordion|expand|collapse)[^>]_>" - pattern_negate: "role=\"button\"" - message: "Accordion header buttons should have role='button' (or use native button element which has implicit role)." - - # Accordion header missing aria-expanded - - - pattern: "(?i)]_(?:accordion|expand|collapse)[^>]_>" - pattern_negate: "aria-expanded=\"(true|false)\"" - message: "Accordion header buttons must have aria-expanded attribute set to 'true' or 'false'." - - # Accordion header missing aria-controls - - - pattern: "(?i)]_(?:accordion|expand|collapse)[^>]_>" - pattern_negate: "aria-controls=\"[^\"]+\"" - message: "Accordion header buttons must have aria-controls attribute referencing the ID of the associated panel." - - # Heading wrapper missing role - - - pattern: "(?i)<(div|section)[^>]*(?:accordion.*header|header._accordion)[^>]_>" - pattern_negate: "role=\"heading\"" - message: "Accordion header wrappers should have role='heading' or use native heading elements (h1-h6)." - - # Heading role missing aria-level - - - pattern: "(?i)<[^>]_role=\"heading\"[^>]_>" - pattern_negate: "aria-level=\"[1-6]\"" - message: "Elements with role='heading' must have aria-level attribute set to appropriate level (1-6)." - - # Panel missing proper identification - - - pattern: "(?i)<(div|section)[^>]*(?:accordion.*panel|panel._accordion)[^>]_>" - pattern_negate: "id=\"[^\"]+\"" - message: "Accordion panels must have unique ID attributes for aria-controls reference." - - # Panel with region role missing aria-labelledby - - - pattern: "(?i)<[^>]_role=\"region\"[^>]_>" - pattern_negate: "aria-labelledby=\"[^\"]+\"" - message: "Accordion panels with role='region' must have aria-labelledby referencing the controlling button." - - # Missing keyboard event handlers - - - pattern: "(?i)]_(?:accordion|expand|collapse)[^>]_>" - pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown)" - message: "Accordion header buttons should handle keyboard events (Enter, Space, optionally Arrow keys)." - - # Missing Escape key support for accordion content - - - pattern: "(?i)<(div|section)[^>]*(?:accordion.*panel|panel._accordion)[^>]_>" - pattern_negate: "(onKeyDown|onkeydown|@keydown|v-on:keydown)" - message: "Accordion panels should handle Escape key to close panel and return focus to header." - -- type: suggest - message: | - **Accordion Component Accessibility Best Practices:** - - **Required ARIA Attributes:** - - - **role='button':** Set on accordion header elements (or use native button) - - **role='heading':** Set on accordion header container with aria-level - - **aria-expanded:** 'true' if panel is visible, 'false' if collapsed - - **aria-controls:** Reference to the ID of the associated panel content - - **aria-level:** Appropriate heading level (1-6) for information architecture - - **aria-disabled:** 'true' if panel cannot be collapsed (optional) - - **Optional ARIA Attributes:** - - - **role='region':** On panel content containers (avoid with >6 panels) - - **aria-labelledby:** On panels with role='region', referencing the header button - - **Keyboard Interaction Requirements:** - - - **Enter/Space:** Toggle panel expansion/collapse - - **Tab/Shift+Tab:** Move through all focusable elements in page order - - **Down/Up Arrow:** (Optional) Navigate between accordion headers - - **Home/End:** (Optional) Jump to first/last accordion header - - **Escape:** Close open panel and return focus to header button - - **Structure Requirements:** - - - Header button must be the only element inside heading container - - Each panel must have unique ID for aria-controls reference - - Use native heading elements (h1-h6) when possible instead of role='heading' - - Avoid role='region' on panels when many accordions exist (>6 panels) - - **Implementation Patterns:** - - **Single Accordion Item:** - - ```html -
-

- -

- -
- ``` - - **JavaScript for Accordion with Escape Support:** - - ```javascript - function toggleAccordion(button) { - const isExpanded = button.getAttribute('aria-expanded') === 'true'; - const panel = document.getElementById(button.getAttribute('aria-controls')); - - button.setAttribute('aria-expanded', !isExpanded); - panel.hidden = isExpanded; - - if (!isExpanded) { - // Add escape key listener to panel - panel.addEventListener('keydown', handleAccordionEscapeKey); - } else { - // Remove escape key listener - panel.removeEventListener('keydown', handleAccordionEscapeKey); - } - } - - function handleAccordionEscapeKey(event) { - if (event.key === 'Escape') { - const panel = event.target.closest('[hidden]'); - if (panel) { - const button = document.querySelector(`[aria-controls="${panel.id}"]`); - if (button) { - button.setAttribute('aria-expanded', 'false'); - panel.hidden = true; - button.focus(); // Return focus to header - panel.removeEventListener('keydown', handleAccordionEscapeKey); - } - } - } - } - ``` - - **Using Native Heading:** - - ```html -
-

- -

-
-

Panel content...

-
-
- ``` - - **JavaScript Considerations:** - - - Implement Enter and Space key handlers for expansion/collapse - - Optionally implement Arrow key navigation between headers - - Update aria-expanded state when panels toggle - - Consider implementing single-expand vs multi-expand behavior - - Use hidden attribute or CSS to show/hide panels (note: CSS visibility property can be animated) - - Ensure smooth keyboard navigation flow - - Implement Escape key handler to close open panel and return focus to header - - Add/remove event listeners when panels open/close to manage Escape key support - - **Accessibility Notes:** - - - Role 'region' helps screen readers understand panel structure - - Avoid role='region' proliferation with many simultaneous panels - - Button should be direct child of heading element - - Consider aria-disabled='true' for panels that cannot be collapsed - - Test with screen readers to ensure proper announcement - -metadata: -priority: high -version: 1.0 -
diff --git a/.cursor/rules/assets.mdc b/.cursor/rules/assets.mdc deleted file mode 100644 index 10c15dd36..000000000 --- a/.cursor/rules/assets.mdc +++ /dev/null @@ -1,12 +0,0 @@ ---- -description: Static files (css, js, and images) for theme templates -globs: assets/* -alwaysApply: false ---- -# Assets - -The assets directory contains any assets that need to be referenced within a `.liquid` file, usually using the [asset_url](mdc:https:/shopify.dev/docs/api/liquid/filters/asset_url) Liquid filter. - -Assets is a flat directory, it may not contain subdirectories. - -Any images that are required in the code, including icons, may be stored within assets. Icons can be used in `.liquid` files via the [inline_asset_content](mdc:https:/shopify.dev/docs/api/liquid/filters/inline_asset_content) Liquid filter. diff --git a/.cursor/rules/blocks.mdc b/.cursor/rules/blocks.mdc deleted file mode 100644 index c1562d3cd..000000000 --- a/.cursor/rules/blocks.mdc +++ /dev/null @@ -1,341 +0,0 @@ ---- -description: Development standards and best practices for creating/configuring/styling theme blocks, including static and nested blocks, schema configuration, CSS, and usage examples -globs: blocks/*.liquid -alwaysApply: false ---- -# Theme Blocks Development Standards - -Follow [Shopify's theme blocks documentation](mdc:https:/shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid.txt). - -## Theme Block Fundamentals - -Theme blocks are reusable components defined at the theme level that can be: -- Nested under sections and blocks -- Configured using settings in the theme editor -- Given presets and added by merchants -- Used as [static blocks](mdc:https:/shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/static-blocks#statically-vs-dynamically-rendered-theme-blocks) by theme developers - -Blocks render in the editor and storefront when they are referenced in [template files](mdc:.cursor/rules/templates.mdc). - -### Basic Block Structure -```liquid -{% doc %} - Block description and usage examples - - @example - {% content_for 'block', type: 'block-name', id: 'unique-id' %} -{% enddoc %} - -
- -
- -{% stylesheet %} - /* - Scoped CSS for this block - - Use BEM structure - CSS written in here should be for components that are exclusively in this block. If the CSS will be used elsewhere, it should instead be written in [assets/base.css](mdc:@assets/base.css) - */ -{% endstylesheet %} - -{% schema %} -{ - "name": "Block Name", - "settings": [], - "presets": [] -} -{% endschema %} -``` - -### Static Block Usage - -Static blocks are theme blocks that are rendered directly in Liquid templates by developers, rather than being dynamically added through the theme editor. This allows for predetermined block placement with optional default settings. - -**Basic Static Block Syntax:** -```liquid -{% content_for 'block', type: 'text', id: 'header-announcement' %} -``` - -**Example: Product Template with Mixed Static and Dynamic Blocks** -```liquid - -
- {% comment %} Static breadcrumb block {% endcomment %} - {% content_for 'block', type: 'breadcrumb', id: 'product-breadcrumb' %} - -
-
- {% comment %} Static product gallery block {% endcomment %} - {% content_for 'block', type: 'product-gallery', id: 'main-gallery', settings: { - enable_zoom: true, - thumbnails_position: "bottom" - } %} -
- -
- {% comment %} Static product info blocks {% endcomment %} - {% content_for 'block', type: 'product-title', id: 'product-title' %} - {% content_for 'block', type: 'product-price', id: 'product-price' %} - {% content_for 'block', type: 'product-form', id: 'product-form' %} - - {% comment %} Dynamic blocks area for additional content {% endcomment %} -
- {% content_for 'blocks' %} -
-
-
- - {% comment %} Static related products block {% endcomment %} - {% content_for 'block', type: 'related-products', id: 'related-products', settings: { - heading: "You might also like", - limit: 4 - } %} -
-``` - -**Key Points about Static Blocks:** -- They have a fixed `id` that makes them identifiable in the theme editor -- Settings can be overridden in the theme editor despite having defaults -- They appear in the theme editor as locked blocks that can't be removed or reordered -- Useful for consistent layout elements that should always be present -- Can be mixed with dynamic block areas using `{% content_for 'blocks' %}` - -## Schema Configuration - -See [schemas.mdc](mdc:.cursor/rules/schemas.mdc) for rules on schemas - -### Advanced Schema Features - -#### Exclude wrapper - -```json -{ - "tag": null // No wrapper - must include {{ block.shopify_attributes }} for proper editor function -} -``` - -## Block Implementation Patterns - -### Accessing Block Data - -**Block Settings:** -```liquid -{{ block.settings.text }} -{{ block.settings.heading | escape }} -{{ block.settings.image | image_url: width: 800 }} -``` - -**Block Properties:** -```liquid -{{ block.id }} // Unique block identifier -{{ block.type }} // Block type name -{{ block.shopify_attributes }} // Required for theme editor -``` - -**Section Context:** -```liquid -{{ section.id }} // Parent section ID -{{ section.settings.heading | escape }} -{{ section.settings.image | image_url: width: 800 }} -``` - -## Nested Blocks Implementation - -### Rendering Nested Blocks -```liquid -
-

{{ block.settings.heading | escape }}

- -
- {% content_for 'blocks' %} -
-
-``` - -### Nesting with Layout Control -```liquid -
- {% content_for 'blocks' %} -
-``` - -### Presets with Nested Blocks -```json -{ - "presets": [ - { - "name": "t:names.two_column_layout", - "category": "Layout", - "settings": { - "layout_direction": "horizontal" - }, - "blocks": [ - { - "type": "text", - "settings": { - "text": "Column 1 content" - } - }, - { - "type": "text", - "settings": { - "text": "Column 2 content" - } - } - ] - } - ] -} -``` - -## CSS and Styling - -See [css-standards.mdc](mdc:.cursor/rules/css-standards.mdc) for rules on writing CSS - -### Scoped Styles -```liquid -{% stylesheet %} -.block-name { - padding: var(--block-padding, 1rem); - background: var(--block-background, transparent); -} - -.block-name__title { - font-size: var(--title-size, 1.5rem); - color: var(--title-color, inherit); -} - -.block-name--primary { - background-color: var(--color-primary); -} - -.block-name--secondary { - background-color: var(--color-secondary); -} -{% endstylesheet %} -``` - -### Dynamic CSS Variables -```liquid -
-``` - -## Block Targeting - -### Section Schema for Theme Blocks -```json -{ - "blocks": [ - { "type": "@theme" }, // Accept all theme blocks - { "type": "@app" } // Accept app blocks - ] -} -``` - -### Restricted Block Targeting -```json -{ - "blocks": [ - { - "type": "text", - "name": "Text Content" - }, - { - "type": "image", - "name": "Image Content" - } - ] -} -``` - -## Common Block Patterns - -### Content Block -```liquid -
- {% if block.settings.heading != blank %} -

{{ block.settings.heading | escape }}

- {% endif %} - - {% if block.settings.text != blank %} -
{{ block.settings.text }}
- {% endif %} - - {% if block.settings.button_text != blank %} - - {{ block.settings.button_text | escape }} - - {% endif %} -
-``` - -### Media Block -```liquid -
- {% if block.settings.image %} -
- {{ block.settings.image | image_url: width: 800 | image_tag: - alt: block.settings.image.alt | default: block.settings.alt_text - }} -
- {% endif %} - - {% if block.settings.video %} -
- {{ block.settings.video | video_tag: controls: true }} -
- {% endif %} -
-``` - -### Layout Block (Container) -```liquid -
- {% content_for 'blocks' %} -
-``` - -## Performance Best Practices - - -### Conditional Rendering -```liquid -{% liquid - assign has_content = false - if block.settings.heading != blank or block.settings.text != blank - assign has_content = true - endif -%} - -{% if has_content %} -
- -
-{% endif %} -``` - - -## Examples Referenced - -[text.liquid](mdc:.cursor/rules/examples/block-example-text.liquid) - Basic content block from existing project -[group.liquid](mdc:.cursor/rules/examples/block-example-group.liquid) - Container with nested blocks from existing project diff --git a/.cursor/rules/breadcrumb-accessibility.mdc b/.cursor/rules/breadcrumb-accessibility.mdc deleted file mode 100644 index 4da15e627..000000000 --- a/.cursor/rules/breadcrumb-accessibility.mdc +++ /dev/null @@ -1,129 +0,0 @@ ---- -description: Breadcrumb component accessibility compliance pattern -globs: *.vue, *.jsx, *.tsx, *.html, *.php, *.js, *.ts, *.liquid -alwaysApply: false ---- - -# Breadcrumb Accessibility - -Ensures breadcrumb components follow WCAG compliance and WAI-ARIA Breadcrumb Pattern specifications. - - -name: breadcrumb_accessibility_standards -description: Enforce breadcrumb component accessibility standards and WAI-ARIA Breadcrumb Pattern compliance -filters: - - type: file_extension - pattern: "\\.(vue|jsx|tsx|html|php|js|ts|liquid)$" - -actions: - -- type: enforce - conditions: - - # Navigation landmark requirement - - - pattern: "(?i)]_(?:breadcrumb|navigation)[^>]_>" - pattern_negate: "(aria-label|aria-labelledby)=\"[^\"]+\"" - message: "Breadcrumb navigation must have aria-label or aria-labelledby attribute." - - # Current page aria-current requirement - - - pattern: "(?i)<[^>]*(?:breadcrumb.*current|current._breadcrumb)[^>]_>" - pattern_negate: "aria-current=\"page\"" - message: "Current page in breadcrumb must have aria-current='page' attribute." - - # List structure requirement - - - pattern: "(?i)]_(?:breadcrumb|navigation)[^>]_>" - pattern_negate: "]\*>" - message: "Breadcrumb navigation should use ordered list (ol) for proper structure." - -- type: suggest - message: | - **Breadcrumb Component Accessibility Best Practices:** - - **Required ARIA Attributes:** - - - **aria-label/aria-labelledby:** On navigation element to describe the breadcrumb trail - - **aria-current="page":** On the current page link or element - - **role="navigation":** Implicit on nav element, but can be explicit if needed - - **Structure Requirements:** - - - Use `
{% stylesheet %} + .group-block--sticky { + position: sticky; + top: var(--group-sticky-offset, 0px); + align-self: flex-start; + } + .group-block__link { position: absolute; inset: 0; diff --git a/snippets/header-actions.liquid b/snippets/header-actions.liquid index bef8b53be..e2c612a41 100644 --- a/snippets/header-actions.liquid +++ b/snippets/header-actions.liquid @@ -45,7 +45,7 @@ height: 100%; margin: 0 0 0 auto; padding: 0; - border-left: var(--style-border-drawer); + border-left: 1px solid #e5e5e5; box-shadow: var(--shadow-drawer); background-color: var(--color-background); } @@ -77,6 +77,19 @@ .cart-drawer__close-button { margin-right: calc(var(--padding-sm) * -1); + border: none; + background-color: transparent; + box-shadow: none; + + &:focus-visible { + outline: none; + } + } + + @supports not (background-color: rgb(from red 150 g b / alpha)) { + .cart-drawer__close-button:focus-visible::after { + display: none; + } } .cart-drawer--empty .cart-drawer__content { diff --git a/snippets/header-drawer.liquid b/snippets/header-drawer.liquid index 27c748605..eb5694768 100644 --- a/snippets/header-drawer.liquid +++ b/snippets/header-drawer.liquid @@ -15,6 +15,32 @@ assign block_settings = block.settings assign max_featured_items = 4 assign image_border_radius = block_settings.image_border_radius + assign drawer_color_scheme = block_settings.drawer_color_scheme | default: block_settings.color_scheme + assign drawer_brand_column_color_scheme = drawer_color_scheme + if block_settings.drawer_brand_column_custom_color and block_settings.drawer_brand_column_color_scheme != blank + assign drawer_brand_column_color_scheme = block_settings.drawer_brand_column_color_scheme + endif + assign drawer_brand_text = block_settings.drawer_brand_text | default: shop.name + assign drawer_brand_logo_image = block_settings.drawer_brand_logo_image | default: section.settings.drawer_brand_logo_image + assign drawer_logo_image = block_settings.drawer_logo_image | default: section.settings.drawer_logo_image | default: drawer_brand_logo_image + assign drawer_logo_alt = block_settings.drawer_logo_alt_text | default: drawer_logo_image.alt | default: drawer_brand_text + assign drawer_logo_top_image = block_settings.drawer_logo_top_image | default: section.settings.drawer_logo_top_image | default: drawer_logo_image + assign drawer_logo_bottom_image = block_settings.drawer_logo_bottom_image | default: section.settings.drawer_logo_bottom_image | default: drawer_logo_image + assign drawer_brand_column_width_value = block_settings.drawer_brand_column_width + if drawer_brand_column_width_value == blank + assign drawer_brand_column_width_value = 15 + endif + assign drawer_brand_column_padding_value = block_settings.drawer_brand_column_padding + if drawer_brand_column_padding_value == blank + assign drawer_brand_column_padding_value = 16 + endif + assign drawer_menu_row_padding_value = block_settings.drawer_menu_row_padding + if drawer_menu_row_padding_value == blank + assign drawer_menu_row_padding_value = 0 + endif + + assign brand_column_style = '--drawer-brand-column-width: ' | append: drawer_brand_column_width_value | append: 'vw;' | append: ' --drawer-brand-column-padding: ' | append: drawer_brand_column_padding_value | append: 'px;' + assign brand_column_style = brand_column_style | append: ' --drawer-menu-row-padding: ' | append: drawer_menu_row_padding_value | append: 'px;' if block_settings.menu_style == 'featured_collections' assign ratio = block_settings.featured_collections_aspect_ratio @@ -70,31 +96,67 @@ data-header-drawer class=" menu-drawer + menu-drawer--full motion-reduce - color-{{ block_settings.color_scheme }} + color-{{ drawer_color_scheme }} " > - - + + + {% liquid + if block_settings.menu_style == 'featured_collections' + assign top_level_featured_collections = linklist.links | where: 'type', 'collection_link' + endif - {% liquid - if block_settings.menu_style == 'featured_collections' - assign top_level_featured_collections = linklist.links | where: 'type', 'collection_link' - endif - - if eager_loading and block_settings.menu_style == 'featured_products' - for link in linklist.links - if link.type == 'collection_link' - assign collection_linklist = link - assign top_level_featured_products_collection = collection_linklist.object - break - elsif link.type == 'catalog_link' or link.type == 'collections_link' - assign top_level_featured_products_collection = collections.all - break + if eager_loading and block_settings.menu_style == 'featured_products' + for link in linklist.links + if link.type == 'collection_link' + assign collection_linklist = link + assign top_level_featured_products_collection = collection_linklist.object + break + elsif link.type == 'catalog_link' or link.type == 'collections_link' + assign top_level_featured_products_collection = collections.all + break + endif + endfor endif - endfor - endif - -%} - {% if top_level_featured_collections.size > 0 or top_level_featured_products_collection.products_count > 0 %} - - {% endif %} +