Agent skill

variant-selection

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.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/variant-selection

SKILL.md

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:

Product Attributes Variant ID
T-Shirt Black + Medium abc123
T-Shirt Black + Large def456
T-Shirt White + Medium ghi789

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:

Type variantSelection Purpose UI Passed to Cart?
Selection VARIANT_SELECTION Identify which variant (color, size) Interactive picker No - just the variantId
Non-Selection NOT_VARIANT_SELECTION Describe the variant (material, brand) Display-only badges No - 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

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

Function Purpose
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

State Meaning Visual Clickable?
Available In stock Normal ✓
Incompatible No variant with this + current selections Dimmed ✓ (clears others)
Out of stock Variant exists but quantity = 0 Strikethrough ✗

URL Parameter Pattern

Selections are stored in URL params:

?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:

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:

State Add to Cart Description
Empty ❌ No selections
Partial ❌ Some attributes selected
Complete ✅ All selected, variant found
Conflict — Auto-clears to Partial
DeadEnd ❌ Selection 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)

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results