

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
123456# JavaScript Standards78## General Principles910- **Zero external dependencies** - Use native browser APIs11- **Avoid mutation** - Use `const` over `let` unless necessary12- **Use `for (const item of items)`** over `items.forEach()`13- **Add new lines before blocks** with `{` and `}`14- **Use the component framework** - See [the framework code](mdc:assets/component.js) and the [component documentation](mdc:codex/component-framework.md)1516## Async/Await Syntax1718**Always use async/await over .then() chaining:**1920```javascript21const fetchProducts = async () => {22 try {23 const response = await fetch('/products.json');24 const data = await response.json();25 return data.products;26 } catch (error) {27 console.error('Failed to fetch products:', error);28 return [];29 }30};3132## Web Components Pattern3334**Initialize JavaScript components using the Component framework:**3536```javascript37import { Component } from '@theme/component';3839/**40 * @typedef {Object} ProductCardRefs41 * @property {HTMLButtonElement} addButton - Add to cart button42 * @property {HTMLElement} priceDisplay - Price display element43 * @property {HTMLImageElement} [productImage] - Optional product image44 */4546/**47 * @extends {Component<ProductCardRefs>}48 */49class ProductCard extends Component {50 constructor() {51 super();52 this.cache = new Map();53 }5455 connectedCallback() {56 super.connectedCallback();57 this.#initializeCard();58 }5960 disconnectedCallback() {61 super.disconnectedCallback();62 this.#cleanup();63 }6465 // Public method for external use66 updatePrice(newPrice) {67 if (!this.refs.priceDisplay) return;68 this.refs.priceDisplay.textContent = newPrice;69 }7071 // Event handler for add to cart button72 async handleAddToCart(event) {73 event.preventDefault();7475 const productId = this.cache.get('productId');76 this.refs.addButton.disabled = true;77 this.refs.addButton.textContent = 'Adding...';7879 try {80 await addToCart(productId);81 this.refs.addButton.textContent = 'Added!';8283 // Dispatch custom event for cart updates84 this.dispatchEvent(new CustomEvent('cart:item-added', {85 detail: { productId },86 bubbles: true87 }));88 } catch (error) {89 this.refs.addButton.textContent = 'Try again';90 console.error('Add to cart error:', error);91 } finally {92 setTimeout(() => {93 this.refs.addButton.disabled = false;94 this.refs.addButton.textContent = 'Add to cart';95 }, 2000);96 }97 }9899 // Private method requiring instance access100 #initializeCard() {101 const productId = this.dataset.productId;102 this.cache.set('productId', productId);103 }104105 #cleanup() {106 this.cache.clear();107 }108}109110// Module-scoped utility - no instance access needed111const addToCart = async (productId) => {112 const formData = new FormData();113 formData.append('id', productId);114 formData.append('quantity', 1);115116 try {117 const response = await fetch('/cart/add.js', {118 method: 'POST',119 body: formData120 });121122 if (!response.ok) {123 throw new Error('Failed to add to cart');124 }125126 const cartData = await response.json();127 return cartData;128 } catch (error) {129 console.error('Add to cart error:', error);130 throw error;131 }132};133134customElements.define('product-card', ProductCard);135```136137**HTML usage with the Component framework:**138139```liquid140<product-card data-product-id="{{ product.id }}">141 <img ref="productImage" src="{{ product.featured_image | image_url }}" alt="{{ product.title }}">142 <h3>{{ product.title }}</h3>143 <div ref="priceDisplay" class="product-card__price">{{ product.price | money }}</div>144 <button ref="addButton" on:click="/handleAddToCart" data-add-to-cart>145 Add to cart146 </button>147</product-card>148```149150## Early Returns and Conditional Logic151152**Use early returns over nested conditionals:**153154```javascript155// Good156const processOrder = (order) => {157 if (!order) return;158 if (!order.items.length) return;159 if (order.status !== 'pending') return;160161 // Process the order162 updateOrderStatus(order.id, 'processing');163 sendConfirmationEmail(order.email);164};165166// Avoid167const processOrder = (order) => {168 if (order) {169 if (order.items.length) {170 if (order.status === 'pending') {171 updateOrderStatus(order.id, 'processing');172 sendConfirmationEmail(order.email);173 }174 }175 }176};177```178179**Optional chaining guidelines:**180181```javascript182// Multiple chains - use early return183const updateButton = (product) => {184 const button = product.querySelector('[data-ref="button"]');185 if (!button) return;186187 button.disabled = false;188 button.textContent = 'Add to cart';189};190191// Single chain is fine192const updateButton = (product) => {193 const button = product.querySelector('[data-ref="button"]');194 button?.enable();195};196```197198## Simplification Patterns199200**Ternary operators for simple conditions:**201```javascript202const buttonText = isLoading ? 'Loading...' : 'Add to cart';203element.textContent = buttonText;204```205206**One-liner conditionals:**207```javascript208if (isOutOfStock) return;209```210211**Return boolean comparisons directly:**212```javascript213const isAvailable = product.available && product.price > 0;214return isAvailable;215```216217## Event-Driven Architecture218219**Use events for component communication:**220221```javascript222import { Component } from '@theme/component';223224/**225 * @typedef {Object} CartDrawerRefs226 * @property {HTMLElement} itemCountDisplay - Element showing item count227 * @property {HTMLButtonElement} closeButton - Close button228 */229230/**231 * @extends {Component<CartDrawerRefs>}232 */233class CartDrawer extends Component {234 handleCartUpdate() {235 const itemCount = this.getItemCount();236237 // Update local display238 if (this.refs.itemCountDisplay) {239 this.refs.itemCountDisplay.textContent = itemCount;240 }241242 // Dispatch custom event for other components243 this.dispatchEvent(new CustomEvent('cart:updated', {244 bubbles: true,245 detail: { itemCount }246 }));247 }248249 getItemCount() {250 // Implementation to get cart item count251 return this.querySelectorAll('.cart-item').length;252 }253}254255/**256 * @typedef {Object} CartCounterRefs257 * @property {HTMLElement} countDisplay - The count display element258 */259260/**261 * @extends {Component<CartCounterRefs>}262 */263class CartCounter extends Component {264 connectedCallback() {265 super.connectedCallback();266 // Listen for cart updates267 document.addEventListener('cart:updated', this.#handleCartUpdate.bind(this));268 }269270 #handleCartUpdate(event) {271 if (this.refs.countDisplay) {272 this.refs.countDisplay.textContent = event.detail.itemCount;273 }274 }275}276```277278## JavaScript in Liquid Files279280**Use `{% javascript %}` tags for component-specific scripts:**281282```liquid283{% javascript %}284import { Component } from '@theme/component';285286/**287 * @typedef {Object} FeaturedCollectionRefs288 * @property {HTMLElement} productGrid - The product grid container289 * @property {HTMLButtonElement[]} filterButtons - Filter button elements290 * @property {HTMLElement} [loadingIndicator] - Optional loading indicator291 */292293/**294 * @extends {Component<FeaturedCollectionRefs>}295 */296class FeaturedCollection extends Component {297 async handleFilter(filterValue, event) {298 event.preventDefault();299300 const url = new URL(window.location.href);301 url.searchParams.set('filter', filterValue);302303 // Show loading state304 if (this.refs.loadingIndicator) {305 this.refs.loadingIndicator.hidden = false;306 }307308 try {309 const response = await fetch(url.toString());310 const html = await response.text();311 const parser = new DOMParser();312 const doc = parser.parseFromString(html, 'text/html');313314 const newGrid = doc.querySelector('.product-grid');315316 if (newGrid && this.refs.productGrid) {317 this.refs.productGrid.replaceWith(newGrid);318 // Update the ref after replacement319 this.#updateRefs();320 }321322 // Update URL without page reload323 history.pushState({ filter: filterValue }, '', url.toString());324 } catch (error) {325 console.error('Filter error:', error);326 } finally {327 if (this.refs.loadingIndicator) {328 this.refs.loadingIndicator.hidden = true;329 }330 }331 }332}333334customElements.define('featured-collection', FeaturedCollection);335{% endjavascript %}336```337338**HTML usage in Liquid template:**339340```liquid341<featured-collection>342 <div class="filters">343 <button ref="filterButtons[]" on:click="/handleFilter/new" data-filter="new">344 New Arrivals345 </button>346 <button ref="filterButtons[]" on:click="/handleFilter/sale" data-filter="sale">347 On Sale348 </button>349 <button ref="filterButtons[]" on:click="/handleFilter/best-selling" data-filter="best-selling">350 Best Sellers351 </button>352 </div>353354 <div ref="loadingIndicator" class="loading" hidden>Loading...</div>355356 <div ref="productGrid" class="product-grid">357 {% for product in collection.products %}358 {% render 'product-card', product: product %}359 {% endfor %}360 </div>361</featured-collection>362```363364## File Structure365366**Group scripts by feature area:**367- `product.js` - All product-related classes368- `cart.js` - Cart functionality369- `collection.js` - Collection and filtering370- `search.js` - Search functionality371372**Co-locate related classes:**373```javascript374// collection.js375class CollectionFilters extends HTMLElement { }376class CollectionGrid extends HTMLElement { }377class CollectionSort extends HTMLElement { }378```379380## Optimistic UI Patterns381382**Update UI before server response for high-certainty actions:**383384```javascript385import { Component } from '@theme/component';386387/**388 * @typedef {Object} AddToCartButtonRefs389 * @property {HTMLElement} buttonText - The button text element390 * @property {HTMLElement} [loadingSpinner] - Optional loading spinner391 */392393/**394 * @extends {Component<AddToCartButtonRefs>}395 */396class AddToCartButton extends Component {397 async handleAddToCart(event) {398 event.preventDefault();399400 // Optimistic UI update401 this.#updateButtonState('adding');402 this.#updateCartCount(1);403404 try {405 const result = await this.#addToCart();406 this.#updateButtonState('added');407 } catch (error) {408 // Revert optimistic changes409 this.#updateButtonState('error');410 this.#updateCartCount(-1);411 console.error('Add to cart failed:', error);412 }413 }414415 #updateButtonState(state) {416 const states = {417 adding: 'Adding...',418 added: 'Added!',419 error: 'Try again'420 };421422 if (this.refs.buttonText) {423 this.refs.buttonText.textContent = states[state] || 'Add to cart';424 }425426 // Toggle loading spinner if available427 if (this.refs.loadingSpinner) {428 this.refs.loadingSpinner.hidden = state !== 'adding';429 }430 }431432 #updateCartCount(delta) {433 const counter = document.querySelector('cart-counter-component');434 if (!counter || typeof counter.updateCount !== 'function') return;435436 // Call public method on cart counter component437 counter.updateCount(delta);438 }439440 async #addToCart() {441 // Implementation for adding to cart442 const formData = new FormData();443 formData.append('id', this.dataset.variantId);444 formData.append('quantity', '1');445446 const response = await fetch('/cart/add.js', {447 method: 'POST',448 body: formData449 });450451 if (!response.ok) {452 throw new Error('Failed to add to cart');453 }454455 return response.json();456 }457}458459customElements.define('add-to-cart-button', AddToCartButton);460```461462## Error Handling463464**Always handle errors gracefully:**465466```javascript467const fetchData = async (url) => {468 try {469 const response = await fetch(url);470471 if (!response.ok) {472 throw new Error(`HTTP error! status: ${response.status}`);473 }474475 return await response.json();476 } catch (error) {477 console.error('Fetch error:', error);478 // Return fallback data or empty state479 return null;480 }481};482```483484## Type Safety with JSDoc485486**Always annotate function parameters, return types, and complex objects:**487488```javascript489/**490 * @typedef {Object} ProductData491 * @property {string} id - Product identifier492 * @property {number} price - Product price493 * @property {boolean} [available] - Availability status (optional)494 */495496/**497 * Updates product pricing display498 * @param {ProductData} product - The product to update499 * @param {HTMLElement} container - Target container element500 * @returns {Promise<void>}501 * @throws {Error} If container element is invalid502 */503const updateProductDisplay = async (product, container) => {504 if (!(container instanceof HTMLElement)) {505 throw new Error('Invalid container element');506 }507 // Implementation508};509```510511### 2. **Enhance Component Communication Patterns**512513Your current rules mention custom events but lack the detailed parent-child communication patterns. Expand the "Event-Driven Architecture" section:514515```javascript516## Component Communication Patterns517518### Parent-to-Child Communication519**Parents may invoke public methods on child components:**520521```javascript522import { Component } from '@theme/component';523524/**525 * @typedef {Object} ProductGalleryRefs526 * @property {HTMLImageElement[]} images - Gallery images527 * @property {HTMLElement} mainImage - Main display image528 */529530/**531 * @extends {Component<ProductGalleryRefs>}532 */533class ProductGallery extends Component {534 /**535 * Selects a specific image by index536 * @param {number} index - Image index to select537 */538 selectImage(index) {539 const targetImage = this.refs.images[index];540 if (targetImage && this.refs.mainImage) {541 this.refs.mainImage.src = targetImage.dataset.fullSrc || targetImage.src;542 this.#updateActiveState(index);543 }544 }545546 #updateActiveState(activeIndex) {547 this.refs.images.forEach((img, idx) => {548 img.classList.toggle('active', idx === activeIndex);549 });550 }551}552553/**554 * @typedef {Object} ProductPageRefs555 * @property {ProductGallery} productGallery - Product gallery component556 * @property {HTMLSelectElement} variantSelector - Variant selector557 */558559/**560 * @extends {Component<ProductPageRefs>}561 */562class ProductPage extends Component {563 handleVariantChange(event) {564 const variantId = event.target.value;565566 // Direct method invocation is acceptable from parent to child567 if (this.refs.productGallery) {568 this.refs.productGallery.selectImage(0);569 }570 }571}572```573574### Child-to-Parent Communication575**Children should emit custom events with typed details:**576577```javascript578/**579 * @typedef {Object} VariantSelectDetail580 * @property {string} variantId - Selected variant ID581 * @property {number} price - Variant price582 * @property {boolean} available - Variant availability583 */584585/**586 * @typedef {Object} VariantSelectorRefs587 * @property {HTMLSelectElement} variantSelect - Variant dropdown588 * @property {HTMLElement} priceDisplay - Price display element589 */590591/**592 * @extends {Component<VariantSelectorRefs>}593 */594class VariantSelector extends Component {595 handleSelection(event) {596 const selectedOption = event.target.selectedOptions[0];597 const variantId = selectedOption.value;598 const price = Number(selectedOption.dataset.price);599 const available = selectedOption.dataset.available === 'true';600601 /**602 * @type {CustomEvent<VariantSelectDetail>}603 */604 const customEvent = new CustomEvent('variant:select', {605 detail: { variantId, price, available },606 bubbles: true607 });608609 this.dispatchEvent(customEvent);610611 // Update local UI612 if (this.refs.priceDisplay) {613 this.refs.priceDisplay.textContent = this.#formatPrice(price);614 }615 }616617 #formatPrice(price) {618 return new Intl.NumberFormat('en-US', {619 style: 'currency',620 currency: 'USD'621 }).format(price / 100);622 }623}624```625626### 3. **Add URL Handling Best Practices**627628This is completely missing from your current rules and should be a dedicated section:629630```javascript631## URL Manipulation632633**Always use URL and URLSearchParams APIs over string manipulation:**634635```javascript636// Good - Type-safe URL manipulation637const updateFilters = (filters) => {638 const url = new URL(window.location.href);639640 for (const [key, value] of Object.entries(filters)) {641 if (value) {642 url.searchParams.set(key, value);643 } else {644 url.searchParams.delete(key);645 }646 }647648 return url;649};650651// Navigation with proper state management652const navigateToFilters = (filters) => {653 const url = updateFilters(filters);654 const params = url.searchParams.toString();655656 history.pushState({ urlParameters: params }, '', url.toString());657 updateProductGrid(url.searchParams);658};659660// Avoid - String manipulation661const updateFilters = (filters) => {662 let url = window.location.pathname + '?';663 url += Object.entries(filters)664 .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)665 .join('&');666 return url;667};668```669670### 4. **Enhance Error Handling Section**671672Your current error handling is basic. Expand it with defensive programming patterns:673674```javascript675## Defensive Programming and Error Handling676677**Always validate DOM elements before use:**678679```javascript680/**681 * @param {string} selector - CSS selector682 * @returns {HTMLElement}683 * @throws {Error} If element not found or invalid type684 */685const getRequiredElement = (selector) => {686 const element = document.querySelector(selector);687 if (!(element instanceof HTMLElement)) {688 throw new Error(`Required element not found: ${selector}`);689 }690 return element;691};692693**Handle async operations with proper cleanup:**694695```javascript696import { Component } from '@theme/component';697698/**699 * @typedef {Object} DataLoaderRefs700 * @property {HTMLElement} content - Content container701 * @property {HTMLElement} [errorMessage] - Error display element702 * @property {HTMLElement} [loadingIndicator] - Loading indicator703 */704705/**706 * @extends {Component<DataLoaderRefs>}707 */708class DataLoader extends Component {709 /** @type {AbortController|null} */710 #abortController = null;711712 async loadData(url) {713 // Cancel previous request714 this.#abortController?.abort();715 this.#abortController = new AbortController();716717 // Show loading state718 this.#setLoadingState(true);719720 try {721 const response = await fetch(url, {722 signal: this.#abortController.signal723 });724725 if (!response.ok) {726 throw new Error(`HTTP ${response.status}: ${response.statusText}`);727 }728729 const data = await response.json();730 this.#displayData(data);731 return data;732 } catch (error) {733 if (error.name === 'AbortError') {734 console.log('Request cancelled');735 return null;736 }737738 this.#displayError(error.message);739 throw error;740 } finally {741 this.#setLoadingState(false);742 }743 }744745 disconnectedCallback() {746 super.disconnectedCallback();747 this.#abortController?.abort();748 }749750 #setLoadingState(loading) {751 if (this.refs.loadingIndicator) {752 this.refs.loadingIndicator.hidden = !loading;753 }754 if (this.refs.content) {755 this.refs.content.setAttribute('aria-busy', loading);756 }757 }758759 #displayData(data) {760 if (this.refs.content) {761 // Implementation specific to data type762 this.refs.content.textContent = JSON.stringify(data, null, 2);763 }764 }765766 #displayError(message) {767 if (this.refs.errorMessage) {768 this.refs.errorMessage.textContent = message;769 this.refs.errorMessage.hidden = false;770 }771 }772}773```774775### 5. **Strengthen Web Components Pattern**776777Your current web components section is good but could benefit from the new guidance on refs and type safety:778779```javascript780import { Component } from '@theme/component';781782/**783 * @typedef {Object} ProductCardRefs784 * @property {HTMLButtonElement} addButton - Add to cart button785 * @property {HTMLElement} priceDisplay - Price display element786 * @property {HTMLImageElement} productImage - Product image787 * @property {HTMLElement} [stockIndicator] - Optional stock indicator788 */789790/**791 * @extends {Component<ProductCardRefs>}792 */793class ProductCard extends Component {794 /** @type {AbortController|null} */795 #addToCartController = null;796797 // Specify required refs for this component798 requiredRefs = ['addButton', 'priceDisplay'];799800 connectedCallback() {801 super.connectedCallback();802 // Refs are automatically managed by Component base class803 // No need to manually cache or update them804 }805806 disconnectedCallback() {807 super.disconnectedCallback();808 this.#addToCartController?.abort();809 }810811 /**812 * Public method for external control813 * @param {boolean} disabled - Whether to disable the card814 */815 setDisabled(disabled) {816 this.refs.addButton.disabled = disabled;817 this.classList.toggle('product-card--disabled', disabled);818819 // Update optional elements if they exist820 if (this.refs.stockIndicator) {821 this.refs.stockIndicator.hidden = disabled;822 }823 }824825 async handleAddToCart(event) {826 event.preventDefault();827828 // Cancel any pending requests829 this.#addToCartController?.abort();830 this.#addToCartController = new AbortController();831832 try {833 const response = await fetch('/cart/add.js', {834 method: 'POST',835 signal: this.#addToCartController.signal,836 body: new FormData(event.target.form)837 });838839 if (!response.ok) throw new Error('Failed to add to cart');840841 const result = await response.json();842 this.#handleSuccess(result);843 } catch (error) {844 if (error.name !== 'AbortError') {845 this.#handleError(error);846 }847 }848 }849850 #handleSuccess(result) {851 this.refs.addButton.textContent = 'Added!';852 this.dispatchEvent(new CustomEvent('product:added', {853 detail: result,854 bubbles: true855 }));856 }857858 #handleError(error) {859 this.refs.addButton.textContent = 'Error - Try again';860 console.error('Add to cart error:', error);861 }862}863864customElements.define('product-card', ProductCard);865```866867### 6. **Add Performance Optimization Section**868869**Use debouncing for expensive operations:**870871```javascript872/**873 * @param {Function} func - Function to debounce874 * @param {number} wait - Wait time in milliseconds875 * @returns {Function} Debounced function876 */877const debounce = (func, wait) => {878 let timeout;879 return function executedFunction(...args) {880 const later = () => {881 clearTimeout(timeout);882 func.apply(this, args);883 };884 clearTimeout(timeout);885 timeout = setTimeout(later, wait);886 };887};888889class SearchInput extends Component {890 constructor() {891 super();892 this.#debouncedSearch = debounce(this.performSearch.bind(this), 300);893 }894895 handleInput(event) {896 const query = event.target.value.trim();897898 if (query.length < 2) {899 this.#clearResults();900 return;901 }902903 this.#debouncedSearch(query);904 }905906 async performSearch(query) {907 if (this.refs.loadingSpinner) {908 this.refs.loadingSpinner.hidden = false;909 }910911 try {912 const url = new URL('/search/suggest', window.location.origin);913 url.searchParams.set('q', query);914 url.searchParams.set('limit', '5');915916 const response = await fetch(url);917 const results = await response.json();918919 this.#displayResults(results);920 } catch (error) {921 console.error('Search error:', error);922 this.#clearResults();923 } finally {924 if (this.refs.loadingSpinner) {925 this.refs.loadingSpinner.hidden = true;926 }927 }928 }929930 #displayResults(results) {931 if (!this.refs.resultsContainer) return;932933 // Implementation specific to your search results format934 this.refs.resultsContainer.innerHTML = results935 .map(result => `<div class="search-result">${result.title}</div>`)936 .join('');937 }938939 #clearResults() {940 if (this.refs.resultsContainer) {941 this.refs.resultsContainer.innerHTML = '';942 }943 }944945 /** @type {Function} */946 #debouncedSearch;947}948949customElements.define('search-input', SearchInput);950```951
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?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| Shopify/horizon.cursor/rules/accordion-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/animation-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/blocks.mdc · 428 | Cursor rules | stylearchtypesdatabase+2 | 62/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/breadcrumb-accessibility.mdc · 428 | Cursor rules | ui | 36/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/carousel-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/cart-drawer-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/chat-window-accessibility.mdc · 428 | Cursor rules | styleui | 24/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/color-contrast-accessibility.mdc · 428 | Cursor rules | lint-formatui | 44/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/color-swatch-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/commit-messages.mdc · 428 | Cursor rules | setuplint-formattypesgit | 62/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/css-standards.mdc · 428 | Cursor rules | stylearchuiperformance+3 | 49/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/disclosure-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/dropdown-navigation-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/flip-card-accessibility.mdc · 428 | Cursor rules | styleui | 36/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/form-accessibility.mdc · 428 | Cursor rules | ui | 32/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/landmark-accessibility.mdc · 428 | Cursor rules | ui | 40/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/liquid.mdc · 428 | Cursor rules | buildstyletypesdatabase+2 | 77/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/locales.mdc · 428 | Cursor rules | arch | 49/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/localization.mdc · 428 | Cursor rules | style | 54/100 | 14 days ago | |
| Shopify/horizon.cursor/rules/mobile-accessibility-standards.mdc · 428 | Cursor rules | styleui | 36/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/shopify-horizon-cursor-rules-javascript-standards)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.