Appearance
Working with variants – mental model and invariants
This document explains how variant-aware actions behave in the bulk editor. It is the conceptual guide that complements the implementation status in filters-variants-actions-status.md.
Variant & option terminology
To stay aligned with Shopify’s current Admin and Storefront APIs, we use the following terms consistently:
- Product option (option name) – Labels such as
Size,Color, orMaterial. Maps to Shopify’sProductOption.name. - Option value – Specific values for a product option, e.g.
Small,Red,Cotton. Maps toProductOptionValue.name. In code you’ll see these stored on variants asoption1/option2/option3. - Variant – A specific combination of option values (for example
Size = Small,Color = Red). Maps toProductVariant. - Variant title – The human-readable label for a variant (e.g.
Red / Small). Shopify exposes this asProductVariant.title; in our DSL the fieldvariantNamerefers to the same combined option values.
When the UI or docs mention “variant title contains …”, it refers to matching against this combined option-value string.
1. Core invariants
Invariant V1 – Variant scope on matching products only
By default, all variants on products that match the top-level product filter are eligible to be edited.- If you choose a variant-aware action (Price, SKU, Barcode, Edit/Delete/Replace Variants) and do not configure a variant filter, the job edits every variant on each matching product.
Invariant V2 – Product filter is the hard gate
Products that do not match the product filter are never edited, even if a variant-level rule would otherwise match some of their variants.- The product filter decides which products “enter the job”.
- Variant filters can only narrow or shape the set of variants within those products, not expand the job to new products.
These invariants should be preserved in:
- UI copy (summaries and helper text).
- Backend behavior (preview + execution).
- Tests (Vitest + Playwright).
For the current implementation details, see filters-variants-actions-status.md (section “2. Variant targeting behavior”).
2. Two-step model: product filter → variant filter
Step 1 – Product filter (Filter products card)
- Controls which products are included in the job.
- Uses the main filter builder and is backed by the DSL + Shopify search query compilation.
Step 2 – Variant filter (per action)
- Optional, lives inside the action card (e.g., “Variants to edit: All variants on each matching product [Change]”).
- Controls which variants on those products are edited for that specific action.
- Uses a variant-only DSL (position, options, SKU/barcode, variant price, etc.).
3. How variant filters refine edits
Given a fixed set of matching products (from Step 1):
- No variant filter configured → all variants on each matching product are edited (V1).
- Variant filter configured (e.g., “Variant position equals 2”, or “Option 2 equals Red”) → only variants that satisfy the variant DSL on each matching product are edited.
- At no point can a variant filter cause a product outside the product filter to be modified (V2).
4. Action-specific semantics (VA‑1/2/Clone, SKU/Barcode, Options)
Given the invariants above, the specific variant actions behave as follows:
VA‑1 – Edit Variants
- Applies the variant DSL to each matching product.
- Only variants that match the DSL on matching products are edited.
- Example: “Variant position equals 2” updates only the second variant on each matching product.
- The Edit Variants action updates option values (Option 1/2/3) only. Updating option values effectively renames the variant (for example, changing Option 2 value from
RedtoCrimsonfor all matching variants). Use the Price, SKU, or Barcode actions (with variant filters) to change those fields on specific variants.
VA‑2 – Delete Variants
- Applies the variant DSL per matching product.
- Only matching variants are deleted; other variants on the product remain.
- If the DSL effectively targets “all variants” on a product, the runner must first ensure the product keeps at least one variant (for example by creating a default variant) so the product itself is not deleted.
VA‑3 – Clone Variants & Options (replace)
- Replaces all options and variants on each matching product with those from a selected source product.
- This is a destructive action and ignores variant filters (the product filter still gates which products are affected).
- Inventory quantities are not copied in v1; SKU/barcode and variant media are optional toggles.
Option-only actions (no variant changes)
- Rename option updates the option label (e.g.,
Size→Dimensions) without changing variant option values. - Reorder options changes the option order (Option 1/2/3) and therefore the variant ordering in admin/UI.
- Reorder option values updates the ordering of values for a single option (variants are re-sorted accordingly).
- Rename option updates the option label (e.g.,
SKU / Barcode actions + “blank SKU” helper
- When the “blank SKU” (or equivalent) helper is active, only variants with a blank SKU on matching products are edited.
- Summary copy should clearly indicate that the scope is constrained (for example, “Only variants with blank SKU on each matching product”).
5. Preview behavior
Product preview (Step 1)
- The product preview table that belongs to the Filter products card does not need to update when the variant filter in the action panel changes.
- It continues to show which products match the top-level product filter.
Action preview (per action)
- Any action-specific preview (e.g., previewing changes in the action card or previewing variant rows) must reflect only the variants that match the variant DSL on the already-matching products.
- This keeps execution and action preview aligned: if a variant is shown as being edited in the action preview, the job should actually edit it, and vice versa.
Zero‑match behavior:
- If, after applying product + variant filters, zero variants match:
- The job is allowed to run and complete as a no‑op, and
- The UI should surface a clear summary (for example, “No variants matched your filters; no changes were applied.”).
6. Selection modes
- Earlier “selection mode” presets (e.g., explicit First/Last variant toggles) were removed from the UI.
- All variant targeting is now expressed via the variant filter DSL (for example, “Variant position equals 1” instead of a dedicated “First variant only” mode).
- Tests and docs should reference these behaviors in terms of DSL rules rather than preset names.
7. Where this shows up in the product
UI
Variants to editsummary in the action cards (default text vs after clicking Change).- Variant Filter & Selection panels for Edit/Delete/Replace Variants and for SKU/Barcode actions.
Code
- Product-level filtering: filter DSL +
compileToShopifyQuery+ preview/runner selection. - Variant-level filtering:
variantPreviewFilters.ts,filterVariants/matchesVariantFilter, and the*VariantFilterfields onActionConfig.
- Product-level filtering: filter DSL +
Tests
- Unit:
tests/variantSelection.test.ts,tests/variantPreviewFilters.test.ts. - E2E:
tests/bulk-variants/variant-actions.pw.spec.ts,tests/bulk-tasks/sku-and-barcode.pw.spec.ts, and variant-related flows intests/non-bulk/filters-and-usage.pw.spec.ts.
- Unit:
When adding or changing variant behavior, update this document first (invariants + mental model), then align filters-variants-actions-status.md, code, and tests with it.