Skip to content

Engine-managed attributes

The engine sets and removes these attributes at runtime to reflect state. All state

The engine sets and removes these attributes at runtime to reflect state. All state attributes are ss--prefixed - the prefix guarantees no collision with native HTML or other tools’ attributes. There are no state classes anymore: everything is an ss- attribute, so target them in CSS as attribute selectors (e.g. add-to-cart[ss-out-of-stock] { … }, .thumb[ss-active] { … }). Authoring them yourself is at best ignored and at worst breaks cloning - treat them as read-only hooks.

ss--prefixed state attributes set on the component itself:

  • ss-loading - on add-to-cart, buy-now, add-bundle-to-cart, add-volume-to-cart (during the action) and on storesynk-list, search-result-list, predictive-search, storesynk-bundle, storesynk-volume-discount, storesynk-mix-match, storesynk-addons (during fetch); on add-mix-match-to-cart during its multi-line add; on change-country / change-language while a locale switch is in flight (paired with aria-busy="true" and a disabled child <select>); and on a cart-line while one of its own line mutations (increase-line-quantity / decrease-line-quantity / remove-cart-line, and the in-cart subscription controls change-line-purchase-option / change-line-selling-plan) is in flight - the initiating action stamps ss-loading on the enclosing cart-line, sets aria-disabled="true" on that line’s controls, and ignores further clicks until the transport settles (both cleared on completion). Style cart-line[ss-loading] (e.g. dim it) for visible feedback.
  • ss-out-of-stock, ss-unavailable - on add-to-cart, buy-now, add-bundle-to-cart, and add-volume-to-cart (mutually exclusive; on the bundle button ss-unavailable = some member’s selection resolved to no variant, ss-out-of-stock = every member has a variant but at least one is unpurchasable. add-volume-to-cart tracks the page’s selected variant, exactly like add-to-cart).
  • ss-empty - on any product displayer with an empty/null value (paired with hidden); on storesynk-list / search-result-list when there are no results; on show-metafield / metafield-wrapper when the metafield is absent; on change-purchase-option when the variant has no selling plans; on change-selling-plan when there are no plans or while in one-time mode; on their cart counterparts change-line-purchase-option (the line’s merchandise offers no selling plans) and change-line-selling-plan (no plans or the line is one-time); and on change-country / change-language (paired with hidden) on a single-market shop where there is ≤1 option to pick. Also on storesynk-bundle when the product has no live bundles (or no namespace is configured); on show-bundle-original-total / show-bundle-savings / show-bundle-percentage at zero savings; and on show-member-quantity / show-member-subtotal (paired with hidden) when the member’s quantity is 1 / the subtotal is not computable. Also on storesynk-volume-discount when the product has no live volume discount (or no namespace is configured); and (paired with hidden) on show-tier-label / show-tier-badge when the tier has no label/badge, and on show-tier-original-price on rows with no savings (nothing to strike through). Also on storesynk-mix-match when the product has no live mix-match bundles (or no namespace is configured); on the mix-match money/hint displayers with nothing to show (empty selection, no savings, no next tier); on mix-match-selection-list while nothing is picked; and (paired with hidden) on show-item-quantity / show-pick-quantity / show-selection-overflow / show-section-count at their zero states. Also on storesynk-addons when no live offer applies to the product (or no namespace is configured); and (paired with hidden) on show-addons-subtitle when the merchant left it blank, on show-addon-price while the row card resolves no variant, and on show-addon-original-price when the offer takes nothing off the price (none / free-shipping / 0%).
  • ss-on-sale - on show-sale-badge when the selected variant is discounted (no class).
  • ss-open - on storesynk-cart (drawer open) and predictive-search (dropdown open).
  • ss-active - on the currently-selected option-value, the active show-thumbnail, the active selling-plan-option clone and the current-mode purchase-option - in the PDP selectors and their in-cart counterparts alike, each paired with aria-pressed="true|false" on that option’s resolved control (client and SSR) - the selected locale-value pill (template mode; paired with aria-checked="true"), and the selected volume-tier row (also paired with aria-checked="true" - the row is an ARIA radio).
  • ss-unavailable="nonexistent" / ss-unavailable="out-of-stock" - on each option-value to mark combinations that don’t exist or are sold out.
  • ss-adopt - on a host whose children the server already rendered: a list host (e.g. storesynk-list, via a server renderer / the <StoresynkList> wrapper), the bundle widget’s storesynk-bundle / bundle-member-list, the volume-discount widget’s storesynk-volume-discount / volume-tier-list, and the mix-match widget’s storesynk-mix-match / mix-match-section-list / mix-match-item-list / mix-match-selection-list, and the product-addons widget’s storesynk-addons / addon-list. The client keeps the SSR-rendered children and hydrates them in place instead of clearing them. SSR-emitted marker, never hand-authored (see Server-side rendering & adoption).
  • ss-variants-truncated - on a bundle member-card (or mix-match pool-card, or add-ons row-card) storesynk-product whose variant list hit the query cap (100 variants): the card degrades to default-variant mode (its change-option selectors render nothing). CSS hook for a “choose options on the product page” affordance (e.g. storesynk-product[ss-variants-truncated] .member-truncated { display: block }).
  • ss-bundle-key - SERVER-set on each SSR-rendered bundle-offer clone (the bundle’s $app:bundle metaobject gid) so the client can detect that the live bundle set drifted since the HTML was built (date windows open/close). Never authored.
  • ss-volume-key - SERVER-set on the SSR-rendered storesynk-volume-discount clone (the winning $app:volume_discount metaobject gid) - the volume analog of ss-bundle-key, so the client can detect that the live discount drifted since the HTML was built (date windows open/close). Never authored.
  • ss-mix-match-key: SERVER-set on each SSR-rendered mix-match-offer clone (the bundle’s $app:mix_match metaobject gid), the mix-match analog of ss-bundle-key, so the client can detect that the live set drifted since the HTML was built. Never authored.
  • ss-addons-key: SERVER-set on the SSR-rendered storesynk-addons offer clone (the winning $app:product_addons metaobject gid) - the product-addons analog of ss-mix-match-key, so the client can detect that the winning offer drifted since the HTML was built (a date window opened/closed, the overlap rule re-decided). Never authored.
  • ss-selected: engine-set on a mix-match pool card (the item-list clone) while the card’s currently selected variant is in the customer’s selection - the CSS hook that swaps the card’s Add row for its quantity stepper (see the mix-match recipes below) - and on an add-ons row card (the addon-list clone) while its row is ticked. Never authored.
  • ss-open: also on a mix-match-section whose accordion panel is open (alongside the drawer and predictive-search uses listed above).
  • ss-met: engine-set on a mix-match-section whose min requirement is met (the checkmark / complete-state CSS hook). Never authored.
  • ss-incomplete: engine-set on add-mix-match-to-cart until every section’s count is inside its [min, max] (paired with aria-disabled="true" on the focusable control). Never authored.
  • ss-max: engine-set on add-mix-match-item / increase-mix-match-item while the item’s section is at its max capacity (adding is a no-op; the control shows why). Never authored.
  • ss-logged-in - set on <storesynk-store> (client and SSR) while a customer session is logged in - the CSS hook for auth-gated UI (e.g. storesynk-store[ss-logged-in] log-in { display: none } / storesynk-store:not([ss-logged-in]) log-out { display: none }). Driven by the customer-accounts module; never authored.
  • ss-member - set on <storesynk-customer-pricing> while it renders the member preview (a logged-in customer whose tags match the rule). CSS hook for member-vs-prompt styling. Never authored.
  • ss-customer-pricing-key - SERVER-set on the SSR-rendered storesynk-customer-pricing clone (the winning $app:customer_pricing metaobject gid) - the customer-pricing analog of ss-volume-key, so the client can detect that the live rule drifted since the HTML was built. Never authored.

Non-ss- engine-set attributes:

  • hidden - set (native) on product displayers when their value is empty (alongside ss-empty).
  • aria-disabled="true" - set on the focusable control of add-to-cart / buy-now (the nested <button>/<a>, or the element itself if none) when there is no purchasable variant; also on unavailable option-value radios. aria-current marks the active show-thumbnail.
  • value - on each option-value (the option value name), on each selling-plan-option clone (a real selling-plan id - there is no synthetic one-time value anymore), and on each locale-value pill (the country/language ISO code the picker dispatches on select).
  • index - on show-thumbnail clones (and assigned to the template instance during expansion). Authoring index on a show-thumbnail template is rule SSK-303.

Always-allowed global/standard attributes (never flagged): class, id, style, slot, title, role, tabindex, hidden, data-*, aria-*.

SSR adoption payload markers (server-embedded, not authored by hand - see Server-side rendering & adoption below): the data-storesynk-product / data-storesynk-shop attributes mark an inline <script type="application/json"> payload placed as a direct child of storesynk-product / storesynk-store. They are data-* markers on a native <script>, so they’re always allowed. When a server renderer fills the markup, it writes the same state attributes into the built HTML (e.g. ss-empty on an empty displayer, ss-on-sale on a discounted show-sale-badge) - again read-only hooks, not something you author.

Centralized config block (data-storesynk-config) - the one author-facing inline JSON, unlike the two payload markers above. A <script type="application/json" data-storesynk-config> as a direct child of <storesynk-store> carries the shared StoresynkConfig (domain, token, country, language, persistLocale, the customer* fields, and the nested pixels { enabled, client[], server[], relayEndpoint, requireConsent } / cart { openOnAdd, closeOnOutside, noteDebounce } groups - no pixels.exclude; the app namespace is not here). The store reads it (_readConfigPayload_applyConfigDefaults) and reflects each key onto its own attributes only where the attribute is absent, so a tag attribute always wins (precedence: tag attribute > config block > default; a boolean presence attribute can only turn a config default on). It is an inert data block - there is no window.storesynkConfig / configureStoresynk global in @storesynk/elements (removed); the no-build surface hand-writes the block and the Astro/Next SSR wrappers emit its pixels/cart groups. Also a data-* marker on a native <script>, so it’s always allowed. Integrators typing the block import the StoresynkConfig / StoresynkPixelsConfig / StoresynkCartConfig types from @storesynk/core - @storesynk/elements’ published type surface is deliberately narrow (the tag union + HTMLElementTagNameMap; see Typing the tags below) and exports no values.



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