Two selectors. The purchase mode (one-time vs subscribe) and the subscription frequency are
separate controls: change-purchase-option is the toggle (author two purchase-option
children); change-selling-plan is the frequency picker shown only while subscribing (it
clones one selling-plan-option per real plan). Both emit sf:selling-plan-change, which
storesynk-product catches to set selectedSellingPlanId (null = one-time) - the value
add-to-cart / buy-now send to checkout. Both hide via [ss-empty] when the variant has no
sellingPlanAllocations.
change-purchase-option
Section titled “change-purchase-option”- Purpose: one-time-vs-subscribe toggle. Wires its
purchase-optionchildren: clicking thevalue="single"option clears the plan (one-time →sellingPlanId: null); thevalue="recurring"option applies the current, else last-chosen, else first plan. Reflects[ss-active]on the current mode’s option. It is NOT a template-clone container - it toggles state on author-provided children and captures no template (so no SSK-107). - Attributes:
default-option : "single" | "recurring" = "recurring"- the widget’s initial mode. Subscribe-first is the default: when a product subtree authors this widget, the initial state preselects recurring with the variant’s first selling plan, so the widget paints “Subscribe” active andchange-selling-planopen/filled on first load (both client and SSR). Setdefault-option="single"to opt back to one-time-first.
- Widget-scoped subscribe-first (important): the recurring default applies only to a
storesynk-productsubtree that contains achange-purchase-option. Products without the widget - collection/list cards, search results, bundle members - always start one-time (selectedSellingPlanId: null), so a card’sadd-to-cartcan never silently add a subscription. - Required ancestor / context:
storesynk-product(consumesproductContext). - Children: exactly two
purchase-optionelements - onevalue="single"and onevalue="recurring". - Events: emits
sf:selling-plan-change(bubbles, composed;detail.sellingPlanId- a plan id for recurring, ornullfor one-time). - State it sets:
[ss-active]on the current-modepurchase-option(therecurringoption by default; thesingleoption underdefault-option="single"), paired witharia-pressedon that option’s resolved control;[ss-empty]on itself when the selected variant has no selling plans (hide withchange-purchase-option[ss-empty] { display:none }). - Accessibility (built in): each
purchase-optionis wired through the Clickable primitive - a nested native<button>is used as the control if you provide one, otherwise the bare pill is upgraded torole="button"+tabindex="0"+ Enter/Space - witharia-pressedmirroring[ss-active]on that control, written deterministically every update. Identical to the in-cartchange-line-purchase-option. The SSR fill stamps the same attributes in the same order, so the toggle is operable before hydration.
purchase-option
Section titled “purchase-option”- Purpose: one selectable purchase mode inside a
change-purchase-option(or, in the cart, achange-line-purchase-option). A logic-free styleable element - no logic of its own; the enclosing toggle makes it a control. - Attributes:
value : "single" | "recurring"- which mode this option selects. Author this.- (Engine-set:
[ss-active]on the current mode, plusrole/tabindex/aria-pressedon the resolved control - the pill itself unless you nest a<button>.)*
- Required ancestor / context:
change-purchase-optionorchange-line-purchase-option(SSK-108). - Children: your label markup (e.g. “One-time purchase” / “Subscribe & save”) - give it
visible text, which becomes the control’s accessible name. Style the active one via
[ss-active].
change-selling-plan
Section titled “change-selling-plan”- Purpose: subscription frequency selector, shown only while subscribing. Captures its
first
selling-plan-optionchild as the template, removes it, and clones it once per real selling plan of the selected variant. Selecting one dispatchessf:selling-plan-change. Under the widget’s subscribe-first default (achange-purchase-optionin the same product withoutdefault-option="single"), it renders open and filled on first paint - the variant’s first plan is preselected - rather than starting hidden. - Attributes:
labels : string = ""- optional comma-separated custom plan names, in plan order, overriding the Shopify plan names (e.g.labels="Every week, Every 2 weeks, Every month"). (The oldone-time-labelattribute no longer exists - the one-time choice moved tochange-purchase-option; authoringone-time-labelnow trips SSK-201.)
- Required ancestor / context:
storesynk-product(consumesproductContext; alsostoreContextto money-format each plan’s price). - Children: exactly one
selling-plan-optiontemplate (rule SSK-107 if missing). - Events: emits
sf:selling-plan-change(bubbles, composed;detail.sellingPlanId- the selected plan id). - State it sets:
value(a real plan id) +[ss-active]on each clone, paired witharia-pressedon that clone’s resolved control;[ss-empty]on itself when the variant has no plans or while in one-time mode (selectedSellingPlanId == null) - hide withchange-selling-plan[ss-empty] { display:none }. - Accessibility (built in): every clone is wired through the Clickable primitive - a nested
native
<button>is used as the control if the template provides one, otherwise theselling-plan-optionitself is upgraded torole="button"+tabindex="0"+ Enter/Space - witharia-pressedmirroring[ss-active]. Identical to the in-cartchange-line-selling-plan. The SSR fill stamps the same attributes in the same order.
selling-plan-option
Section titled “selling-plan-option”- Purpose: one selectable subscription plan. The first one authored is the clone template.
A logic-free styleable element - no logic of its own; the enclosing selector sets its
value(a real selling-plan id - there is no synthetic one-time row here anymore), toggles[ss-active], and makes it a keyboard-operable control. - Attributes: none authored. Engine-set:
value,[ss-active], plusrole/tabindex/aria-pressedon the resolved control (the option itself unless the template nests a<button>). - Required ancestor / context:
change-selling-planorchange-line-selling-plan(SSK-108). - Children: optionally
show-selling-plan-nameand/orshow-selling-plan-price, plus any markup (a radio<input>, price text, etc.) - their text becomes the control’s accessible name. Style the selected one via[ss-active].
show-selling-plan-name
Section titled “show-selling-plan-name”- Purpose: invisible (
display:contents) text holder inside aselling-plan-option; the engine writes the plan’s name (or the matchinglabelsoverride) into itstextContent. - Attributes: none. Required ancestor / context:
selling-plan-optionwithinchange-selling-plan(SSK-108). - Events / state: none.
show-selling-plan-price
Section titled “show-selling-plan-price”- Purpose: invisible text holder inside a
selling-plan-option; the engine writes the money-formatted price for that plan into itstextContent. - Attributes: none. Required ancestor / context:
selling-plan-optionwithinchange-selling-plan(SSK-108). - Events / state: none.
Imported from the Storesynk skill v0.1.0 (references/component-reference.md). To change this page, change it there.