variant-selection
npx skills add https://github.com/saleor/storefront --skill variant-selection
Agent 安装分布
Skill 文档
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 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:
// 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
const deadEnd = getUnavailableAttributeInfo(variants, groups, selections);
// Returns: { slug: "size", name: "Size", blockedBy: "Red" }
// UI shows: "No size available in Red"
Custom Renderers
<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)