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/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 `