Is your storefront agentic?

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">, whose name or id mentions option, size, color, variant, attribute, swatch, style or material.
  • ARIA controls: elements with aria-pressed, role="radio" with aria-checked, or role="option" with aria-selected, inside a form, fieldset or product/variant container.
  • Bare swatches: div, span, li or a elements with swatch-like classes and no role.

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: none is not).
  • Mark unavailable combinations with disabled and 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 with role="radio" and aria-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-label with 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" with aria-checked, inside a container with role="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
View the skill on GitHub