Skip to content

Variant options

Color, size, and material pickers with change-option: pills, a select, or radios.

<change-option> picks its mode from your children: an option-value pill makes a swatch group, a <select> makes a dropdown, and an option-value wrapping a radio <input> makes a radio group. The first value element is a template the engine clones once per option value. Selecting a value updates every variant-level displayer on the page.

This version targets one known product, so it can name its options:

<storesynk-product handle="example-product">
<change-option name="Color" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<span class="option-selected">Selected: <show-active-option-title></show-active-option-title></span>
<div class="option-values">
<option-value class="option-pill">
<show-option-swatch type="color" class="swatch"></show-option-swatch>
<show-option-title></show-option-title>
</option-value>
</div>
</change-option>
<change-option name="Size" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<select class="option-select">
<option>Pick a size</option>
</select>
</change-option>
<change-option name="Material" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<div class="option-values">
<option-value class="option-radio">
<input type="radio" />
<show-option-title></show-option-title>
</option-value>
</div>
</change-option>
</storesynk-product>
  • show-option-label prints the group’s name; show-active-option-title prints the selected value. Inside option-value, show-option-title is the value name and show-option-swatch paints the swatch.
  • All three modes are accessible out of the box. Do not add role or tabindex yourself.

Multi-product routes: use group, never a hardcoded name

Section titled “Multi-product routes: use group, never a hardcoded name”

A change-option whose name matches nothing on the current product self-hides. On a dynamic /products/[handle] route a hardcoded name="Color" vanishes on products without that option, and the shopper buys the default variant without seeing a choice. Resolve options positionally instead. Shopify allows at most three option groups, so three group blocks cover every product; extras self-hide:

<storesynk-product handle="example-product">
<change-option group="1" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<div class="option-values">
<option-value class="option-pill"><show-option-title></show-option-title></option-value>
</div>
</change-option>
<change-option group="2" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<div class="option-values">
<option-value class="option-pill"><show-option-title></show-option-title></option-value>
</div>
</change-option>
<change-option group="3" class="option-group">
<show-option-label class="option-group-label"></show-option-label>
<div class="option-values">
<option-value class="option-pill"><show-option-title></show-option-title></option-value>
</div>
</change-option>
</storesynk-product>

Use plain text pills with group, not color swatches: group 1 may be “Model” on another product.

TagAttributeDefaultWhat it does
change-optionname""Option name, matched case-insensitively. Single known product only.
change-optiongroup01-based option index. Use on any multi-product surface. Author exactly one of name / group.
show-option-swatchtype""color paints a background color; image sets an image. Absent falls back to color, then image.
show-option-labelgroup / name0 / ""Standalone mode only: which group’s name to print.
show-active-option-titlegroup / name0 / ""Standalone mode only: which group’s selected value to print.

Each option-value gets ss-active when selected, and ss-unavailable="nonexistent" or ss-unavailable="out-of-stock" for combinations that do not exist or are sold out. In select and radio modes the state is projected onto the native controls too.

.option-pill { padding: 8px 16px; border: 1px solid #ddd; border-radius: 8px; cursor: pointer; }
.option-pill[ss-active] { background: #1a1a1a; color: #fff; }
.option-pill[ss-unavailable="nonexistent"] { opacity: .35; text-decoration: line-through; }
.option-pill[ss-unavailable="out-of-stock"] { opacity: .6; }
  • A change-option with neither name nor group renders nothing, and group="0" also resolves nothing. See options and variants.
  • Pills that never appear are a nesting problem: show-option-title and show-option-swatch must live inside an option-value inside the change-option.
  • value-option, show-label, and show-swatch are old names and stay inert. Use option-value, show-option-title, and show-option-swatch.