Skip to content

SSK-2xx - attributes

SSK-201 - Unknown attribute on a storesynk tag (likely typo)

Section titled “SSK-201 - Unknown attribute on a storesynk tag (likely typo)”
  • Severity: Warning
  • Applies to: any storesynk tag.
  • Detect: an attribute that is not in that tag’s known attribute set and is not an engine-managed or global attribute.
  • Why it breaks: a misspelled attribute is silently ignored, so its intended effect never happens.
  • Known symptoms: an attribute that “has no effect” (e.g. the product never loads, the out-of-stock text never changes).
  • Fix: suggest the nearest valid attribute - e.g. productIdproduct-id, text-oostext-out-of-stock.
  • JSX/TSX sources: React-only props (className, key, ref, htmlFor, suppressHydrationWarning, on* handlers) are whitelisted and never flagged, so framework files can be validated without noise. TypeScript does not make this rule redundant: @storesynk/elements/jsx types every tag’s attributes, but TS deliberately skips checking JSX attribute names that aren’t valid JS identifiers (the rule that makes data-* work), so a hyphenated typo (text-oos, bogus-attr) compiles clean and only SSK-201 catches it. Validate .tsx sources.

SSK-202 - <storesynk-product> with neither handle nor product-id

Section titled “SSK-202 - <storesynk-product> with neither handle nor product-id”
  • Severity: Warning
  • Applies to: standalone storesynk-product.
  • Detect: a storesynk-product lacking both handle and product-id that is not the first-child template inside a list/search/predictive container (where it is fed via provideProduct and correctly carries neither).
  • Why it breaks: with nothing to fetch by, the product never resolves and its descendants render nothing.
  • Known symptoms: a blank product region outside of any list.
  • Fix: add a handle or a product-id.

SSK-203 - <storesynk-product> with BOTH handle and product-id

Section titled “SSK-203 - <storesynk-product> with BOTH handle and product-id”
  • Severity: Warning
  • Applies to: storesynk-product.
  • Detect: a storesynk-product carrying both handle and product-id.
  • Why it breaks: only one is used (handle wins), so the other is dead/contradictory configuration that misleads readers.
  • Fix: keep exactly one.

SSK-204 - <change-option> with neither name nor group

Section titled “SSK-204 - <change-option> with neither name nor group”
  • Severity: Warning
  • Applies to: change-option.
  • Detect: a change-option lacking both name and group.
  • Why it breaks: it resolves no option, so it renders nothing.
  • Known symptoms: a missing option selector (no swatches/pills/select appear).
  • Fix: add name="Color" or group="1".
  • Severity: Warning
  • Applies to: show-data.
  • Detect: a show-data with no query attribute.
  • Why it breaks: an empty path resolves to nothing, so it renders nothing.
  • Known symptoms: a blank show-data slot.
  • Fix: add query="…".

SSK-206 - <storesynk-store> missing domain or token

Section titled “SSK-206 - <storesynk-store> missing domain or token”
  • Severity: Warning
  • Applies to: storesynk-store.
  • Detect: a storesynk-store missing domain and/or token.
  • Why it breaks: without credentials the client cannot authenticate, so nothing fetches
    • though these may be injected at runtime by a script (common), which is why this is a Warning, not an Error.
  • Known symptoms: the whole storefront is blank if no script supplies them.
  • Fix: set both, or confirm a script sets them before element registration.

SSK-207 - <change-option group="0"> or non-positive group

Section titled “SSK-207 - <change-option group="0"> or non-positive group”
  • Severity: Warning
  • Applies to: change-option.
  • Detect: a group attribute that is 0 or otherwise non-positive.
  • Why it breaks: group is 1-based, so a non-positive index resolves no option and renders nothing.
  • Known symptoms: a missing option selector.
  • Fix: use 1, 2, 3, … or switch to name.

SSK-208 - Hardcoded <change-option name="…"> in a dynamic-route file

Section titled “SSK-208 - Hardcoded <change-option name="…"> in a dynamic-route file”
  • Severity: Warning
  • Applies to: change-option with name (and no group), when the file path contains a dynamic-route segment ([handle], [...slug], Remix $handle).
  • Detect: name="…" on change-option inside a file whose path marks it as serving many products.
  • Why it breaks: option names differ per product, and a change-option whose name matches nothing self-hides (display: none) instead of erroring. The page looks fine - the picker is just gone - and add-to-cart silently buys the default variant (“wrong item ships”). Spot-checking one product cannot catch it: the page is correct for products that happen to share the hardcoded names and broken for the rest of the catalog.
  • Known symptoms: “the picker shows on some products but not others”; “the jacket has three colours in Shopify but the page has no way to pick one”; customers receiving the default variant.
  • Fix: use the positional 1-based group= - three blocks (group="1"/"2"/"3") cover every Shopify product (max three option groups); extras self-hide. See build-patterns §2.2 “Option pickers on multi-product routes”.

SSK-209 - Product with selectable options but no <change-option>

Section titled “SSK-209 - Product with selectable options but no <change-option>”
  • Severity: Warning
  • Applies to: storesynk-product in server-rendered output (an embedded data-storesynk-product payload is present), outside list/search item templates.
  • Detect: the payload’s options include a group with more than one value, yet the product’s subtree contains no change-option.
  • Why it breaks: the shopper demonstrably has a choice to make and no control to make it - add-to-cart silently resolves to the default variant.
  • Known symptoms: single price shown for a multi-variant product; no size/colour control; customers receiving the default variant. Grid/search cards are exempt (they link to the PDP, which is where options are picked).
  • Fix: add option pickers - on a multi-product route the group="1"/"2"/"3" set (build-patterns §2.2 “Option pickers on multi-product routes”).

SSK-210 - RETIRED (open-on-add is now the default)

Section titled “SSK-210 - RETIRED (open-on-add is now the default)”
  • Status: removed; the validator no longer emits this code and it must not be reused.
  • Why: the engine flipped the default - <storesynk-cart> now opens on a successful add by default (cart.ts resolves open-on-add with a true fallback), so a bare drawer already confirms the add. The old rule warned when the attribute was absent; that premise is gone and the warning became a false positive.
  • New truth: the open-on-add attribute is only an explicit override - it forces the drawer back on over a site-wide opt-out. The opt-out is {"cart":{"openOnAdd":false}} in the data-storesynk-config block. There is no misconfiguration left to warn about, so no rule.

SSK-211 - app-metafield widget with no namespace configured

Section titled “SSK-211 - app-metafield widget with no namespace configured”
  • Severity: Warning
  • Applies to: storesynk-bundle, storesynk-volume-discount, storesynk-customer-pricing, storesynk-mix-match (in both modes: the metafield read and the handle lookup, whose metaobject type is <namespace>--mix_match), and storesynk-addons (all five read the app’s metaobjects).
  • Detect: a storesynk-bundle / storesynk-volume-discount / storesynk-customer-pricing / storesynk-mix-match / storesynk-addons with no namespace attribute, on a page whose storesynk-store has no app-namespace attribute and with no inline script that appears to inject a namespace.
  • Why it breaks: the Storesynk app’s concrete metafield namespace (app--<numeric-app-id>) is required configuration - Storefront queries cannot use the $app: shorthand - so the widget can never query the product’s bundles / volume discounts / customer-pricing rules / mix & match bundles / add-ons offers and stays [ss-empty] forever.
  • Known symptoms: the widget never renders anything, on every product, with no errors and no network request for its metaobjects.
  • Fix: set app-namespace="app--…" on <storesynk-store> (recommended - serves every widget), or namespace="app--…" on the widget, or confirm a script injects it before the components register (this is a Warning, not an Error, for exactly that reason - like SSK-206’s script-injected credentials).

Imported from the Storesynk skill v0.1.0 (references/structure-rules.md). To change this page, change it there.