AgentSkillsCN

variant-selection

调试 Python

SKILL.md
--- frontmatter
name: variant-selection
description: Variant and attribute selection on product detail pages. Use when modifying variant selectors, debugging "Add to Cart" button state, understanding option availability, or adding discount badges to options.

Variant Selection System

Source: Saleor Docs - Attributes - How product/variant attributes work

When to Use

Use this skill when:

  • Modifying variant/attribute selection on product pages
  • Understanding why a variant isn't selectable
  • Adding discount indicators to variant options
  • Debugging "Add to Cart" button state

Instructions

Core Concept: Variants, Not Products

You add VARIANTS to cart, not products. Each variant is a specific attribute combination:

ProductAttributesVariant ID
T-ShirtBlack + Mediumabc123
T-ShirtBlack + Largedef456
T-ShirtWhite + Mediumghi789

The checkoutLinesAdd mutation requires a specific variantId. Without selecting ALL attributes, there's no variant to add.

Two Types of Variant Attributes

Saleor distinguishes between two types of variant attributes:

TypevariantSelectionPurposeUIPassed to Cart?
SelectionVARIANT_SELECTIONIdentify which variant (color, size)Interactive pickerNo - just the variantId
Non-SelectionNOT_VARIANT_SELECTIONDescribe the variant (material, brand)Display-only badgesNo - already on variant

Key insight: Neither type is "passed" to checkout. You only pass the variantId. All attributes are already stored on the variant in Saleor.

graphql
# GraphQL queries use the variantSelection filter:
selectionAttributes: attributes(variantSelection: VARIANT_SELECTION) { ... }
nonSelectionAttributes: attributes(variantSelection: NOT_VARIANT_SELECTION) { ... }

Non-selection attributes are display-only - shown as informational badges, not interactive selectors.

File Structure

code
src/ui/components/pdp/variant-selection/
├── index.ts                      # Public exports
├── types.ts                      # TypeScript interfaces
├── utils.ts                      # Data transformation & logic
├── variant-selector.tsx          # Single attribute selector
├── variant-selection-section.tsx # Main container
├── optional-attributes.tsx       # Non-selection attribute badges
└── renderers/
    ├── color-swatch-option.tsx   # Color swatch (circular)
    └── button-option.tsx         # Button for size/text (unified)

Key Functions in utils.ts

FunctionPurpose
groupVariantsByAttributes()Extract unique attribute values from variants
findMatchingVariant()Find variant matching ALL selected attributes
getOptionsForAttribute()Get options with availability/compatibility info
getAdjustedSelections()Clear conflicting selections when needed
getUnavailableAttributeInfo()Detect dead-end selections

For detailed function signatures and usage, see UTILS_REFERENCE.md.

Option States

StateMeaningVisualClickable?
AvailableIn stockNormal
IncompatibleNo variant with this + current selectionsDimmed✓ (clears others)
Out of stockVariant exists but quantity = 0Strikethrough

URL Parameter Pattern

Selections are stored in URL params:

code
?color=black&size=m&variant=abc123
  ↑           ↑       ↑
Color sel  Size sel  Matching variant (set automatically)

The variant param is only set when ALL attributes are selected.

Discount Badges

Options can show discount percentages:

typescript
// In utils.ts
interface VariantOption {
	id: string;
	name: string;
	available: boolean;
	hasDiscount?: boolean; // Any variant with this option is discounted
	discountPercent?: number; // Max discount percentage
	// ...
}

The renderers display a small badge when discountPercent is set.

Examples

Smart Selection Adjustment

When user selects an incompatible option:

code
State: ?color=red (Red only exists in Size S)
User clicks: Size L
Result: ?size=l (Red is cleared, not blocked)

Users are never "stuck" - they can always explore all options.

Dead End Detection

typescript
const deadEnd = getUnavailableAttributeInfo(variants, groups, selections);
// Returns: { slug: "size", name: "Size", blockedBy: "Red" }
// UI shows: "No size available in Red"

Custom Renderers

tsx
<VariantSelectionSection
	variants={variants}
	renderers={{
		color: MyCustomColorPicker,
		size: MySizeChart,
	}}
/>

State Machine

The selection system has 5 states with automatic conflict resolution. For the full state diagram and transition rules, see STATE_MACHINE.md.

Quick reference:

StateAdd to CartDescription
EmptyNo selections
PartialSome attributes selected
CompleteAll selected, variant found
ConflictAuto-clears to Partial
DeadEndSelection blocks other groups

Key behavior: When user selects an incompatible option, other selections are cleared automatically (not blocked). Users can always explore all options.

Anti-patterns

Don't enable "Add to Cart" without full selection - Needs variant ID
Don't block incompatible options - Let users click, clear others
Don't assume single attribute - Products can have multiple
Don't use 0 in boolean checks for prices - Use typeof === "number"
Don't make non-selection attributes interactive - They're display-only (badges, not toggles)