Size and color selectors AI agents can use: real form controls
Agent-friendly variant selectors are native radio buttons or select menus with labels, server-rendered inside the add-to-cart form. Choosing a size or color is where AI shopping agents fail most often on product pages, because custom div swatches expose no role or selected state.
Updated · By the Medusa team
- Category
- Operability (20% of score)
- Impact
- High
- Scanner check
- Size/color selectors are real form controls
Why it matters for AI agents
Selecting a variant is the step before add-to-cart, and it is the step where agents most often go wrong. An agent needs to know three things: what the options are (Size: S, M, L), which one is selected, and which combinations are unavailable.
Native form controls expose all three in the accessibility tree that browser agents read. A radio group appears as radiogroup "Size" with radio "M" checked. A grid of <div class="swatch"> elements exposes none of it. Screenshot-based agents have their own problem: Anthropic’s computer use documentation notes that dropdowns are tricky to operate with a mouse (computer use tool), so heavily customized pickers make it worse.
Getting this right also feeds the add-to-cart form: when variant inputs live inside that form, the selected variant is submitted with the request, with no JavaScript glue needed.
What the scanner checks
The scanner looks at the server HTML of up to three sampled product pages and finds products with options. It counts:
- Native controls:
<select>elements with more than one<option>, or<input type="radio">, whosenameoridmentions option, size, color, variant, attribute, swatch, style or material. - ARIA controls: elements with
aria-pressed,role="radio"witharia-checked, orrole="option"witharia-selected, inside a form, fieldset or product/variant container. - Bare swatches:
div,span,lioraelements with swatch-like classes and norole.
A product also counts as having variants if its JSON-LD lists more than one variant or SKU. The worst result across products with variants counts.
| Result | When |
|---|---|
| Pass | Every product with variants uses native selects or radios |
| Partial | At least one product uses ARIA option buttons with state but no native inputs |
| Fail | At least one product has bare swatches, or has variants but no option controls in the server HTML |
| Skipped | None of the sampled products have options (excluded from the score) |
| Inconclusive | No product page could be loaded |
How to fix it
Render each option as a labelled radio group inside the add-to-cart form, with the default selection checked on the server:
<form method="post" action="/cart/add">
<fieldset>
<legend>Color</legend>
<input type="radio" id="color-navy" name="option-color" value="Navy" checked />
<label for="color-navy">Navy</label>
<input type="radio" id="color-charcoal" name="option-color" value="Charcoal" disabled />
<label for="color-charcoal">Charcoal (sold out)</label>
</fieldset>
<label for="option-size">Size</label>
<select id="option-size" name="option-size">
<option>S</option>
<option selected>M</option>
<option>L</option>
</select>
<button type="submit">Add to cart</button>
</form>
- Style the labels as swatches if you like; keep the inputs in the DOM (visually hidden is fine,
display: noneis not). - Mark unavailable combinations with
disabledand say “sold out” in the label text. - If you must use custom buttons, follow the ARIA radio group pattern:
role="radiogroup"with a name, children withrole="radio"andaria-checked, and a text name such as “Color: Navy”. - Describe the same variants in structured data with ProductGroup so agents that read JSON-LD get the full matrix.
Common mistakes
- Swatches rendered only after hydration, so the server HTML has variants in JSON-LD but no controls.
- Color swatches with no text, only a background color. Add a label or
aria-labelwith the color name. - Hiding sold-out options entirely, which makes agents assume the size never existed.
How to fix it on Medusa
Medusa’s DTC starter renders option values as plain <button> elements with no aria-pressed or aria-checked, so agents can’t tell which option is selected. Either:
- Render each product option as a radio group (
<fieldset>,<legend>,<input type="radio">and<label>) inside the add-to-cart form, so the choice is submitted with the server action (Next.js forms), or - Keep the buttons and add
role="radio"witharia-checked, inside a container withrole="radiogroup"named after the option.
Pair it with a ProductGroup in the product page’s JSON-LD; see the Medusa agent-readiness guide.
Sources
Frequently asked questions
- Why do AI agents pick the wrong size or color?
- When swatches are plain div or span elements, an agent can't tell from the accessibility tree that they are options, which one is selected, or which are sold out. Native radios and selects expose all three.
- Can I style radio buttons as color swatches?
- Yes. Keep the native input type=radio with a label and style the label as a swatch. The control stays accessible to agents and screen readers.
- Are ARIA buttons with aria-pressed good enough for variant selection?
- They expose state, so agents can use them, and the scanner scores them partial. Native radio groups or selects score a pass because they also submit with the form and need no extra scripting.
Run your store on a platform built for agentic commerce
Medusa is open-source, headless commerce with a full Store API agents can call and storefronts you control end to end.
Deploy on Medusa Cloud
Open-source commerce with a full Store API, hosted and scaled for you. Your storefront, your data model.
Storefront skills for coding agents
Teach Claude Code, Cursor or Codex to build storefronts with the patterns these checks look for.
npx skills add medusajs/medusa-agent-skills