Shopify · Research · · Erick Nolasco
The Shopify variant picker is where most stores lose keyboard shoppers. 9 of 13 failed.
In November 2026 I did hands-on accessibility checks on 13 independent Shopify fashion and beauty stores, mostly in the $500K to $3M range. On each one I tabbed through a product page with a keyboard, read the page the way a screen reader does, and noted what got in the way.
The same component came up again and again: the variant picker. That’s the row of color swatches, size buttons or flavor dropdown above Add to Cart.
9 of the 13 stores had a variant picker problem. On 4 of them, a shopper using only a keyboard could not change the option at all. They could only buy whatever was preselected: the smallest size, the first color, the default flavor.
The picker sits right before the buy button, so a problem here doesn’t just make the page harder to use. It can stop the sale.
What went wrong
| Problem | Stores |
|---|---|
| Options can’t be selected with a keyboard (Tab skips them, or Enter, Space and the arrow keys do nothing) | 4 |
| Sold-out sizes shown only with a line through them, with nothing for screen readers | 1 |
| Options or the option group have no name, the wrong name, or are hidden from screen readers | 5 |
| A size chart or picker panel that keyboard focus can’t get into | 1 |
Some stores had more than one problem, so the rows add up to more than 9.
1. The options can’t be reached or changed with a keyboard
This was the most serious problem. It happens two ways.
The real inputs are hidden completely. Many themes put a visible swatch on top of a real radio button, then hide the radio with display: none. That removes it from the keyboard and from screen readers too. When you press Tab, focus jumps from the reviews link straight to Add to Cart.
The visible swatch is a plain div. Some themes make the swatch focusable with tabindex="0" but only listen for mouse clicks. You can Tab to it and see a focus ring, but Enter, Space and the arrow keys do nothing.
Either way, a keyboard shopper is stuck with the default option. On one store, that meant XS in the first color.
The fix: keep the real radio buttons and hide them visually, not completely, so they still take focus and respond to the arrow keys the way radio buttons normally do.
.swatch input[type="radio"] {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* Show focus on the swatch the shopper can see */
.swatch input[type="radio"]:focus-visible + label {
outline: 2px solid currentColor;
outline-offset: 3px;
}
If your theme uses divs as swatches and you can’t change the markup, each one needs role="radio", aria-checked, a name, and key handlers for Enter, Space and the arrow keys. Using the real radio button is less work and breaks less often.
2. “Sold out” only exists as a line through the text
One store showed sold-out sizes with a strikethrough and nothing else. To a screen reader, the crossed-out size sounds exactly like the available ones: “32, radio button.” The shopper picks it, and the only clue is that Add to Cart quietly changes to a disabled “Sold out” button.
The fix: put the words in the label and hide them visually.
<label for="{{ input_id }}">
{{ value }}
{%- unless value.available -%}
<span class="visually-hidden">, sold out</span>
{%- endunless -%}
</label>
3. The options have no name, or the wrong one
On five stores the picker technically worked, but a screen reader couldn’t say what it was for, or couldn’t see it at all:
- A “Size” heading sat above the buttons, but it wasn’t connected to them, so the group had no name.
- A visible label pointed at the hidden product ID field instead of the dropdown the shopper actually uses.
- A color swatch had no label at all, so it was announced as “radio button, checked.”
- The size buttons were marked as hidden from screen readers, so a blind shopper never heard them.
The fix: wrap each option in a fieldset and make the legend its first child, then give every input a label that says its value.
<fieldset class="variant-option">
<legend>{{ option.name }}</legend>
{%- for value in option.values -%}
<input type="radio" id="{{ section.id }}-{{ option.position }}-{{ forloop.index }}"
name="{{ option.name }}" value="{{ value | escape }}"
{% if option.selected_value == value %}checked{% endif %}>
<label for="{{ section.id }}-{{ option.position }}-{{ forloop.index }}">{{ value }}</label>
{%- endfor -%}
</fieldset>
A common mistake is putting the legend inside a wrapper div. Then it doesn’t count as the legend, and the group is unnamed again.
4. Don’t rebuild the picker on every change
This one didn’t show up on the 13 stores, but it did show up when I tested some popular swatch apps. After each selection, the app replaced its own radio buttons with new ones. Keyboard focus was on the old button, which no longer exists, so it dropped to the top of the page. The arrow keys worked once and then stopped.
If your picker re-renders after a change, update the existing inputs instead of replacing them, or move focus back to the newly selected input.
Where these problems came from
I checked the theme and the code on all 9 stores with a picker problem. 7 of the 9 had no swatch app on the page, so the picker came from the store’s theme. The other 2 also load swatch-app code. The 9 stores used 7 different premium themes, and Pipeline and Turbo each showed up twice.
Shopify’s own current theme, Horizon, passed every picker check I ran. So the problem usually isn’t Shopify. It’s older or heavily customized themes, and every theme breaks in its own way.
That’s also why an overlay widget doesn’t help here. 4 of the 9 stores with picker problems ran an overlay (UserWay, Accessibly or accessiBe). The picker problems were still there with the widget loaded, because the widget sits on top of the page and can’t change how the theme’s picker is built.
Check your own store in two minutes
- Open a product with more than one size or color.
- Click the address bar, then press Tab until you reach the options.
- Try to change the size or color with the arrow keys, Space or Enter.
- Watch whether the price, the image or the URL (
?variant=) changes. - Pick a sold-out option and ask yourself whether anything other than a line through the text tells you it’s unavailable.
If you can’t change the option, or you can’t see where you are, your keyboard shoppers can’t either.
How I tested
These were manual checks, not just a scanner. Automated tools catch some of these problems but miss most of the keyboard ones. On each store I ran axe-core with WCAG 2.2 AA rules, tabbed through a product page in desktop Chrome, and checked names and states in Chrome’s accessibility tree. I didn’t add anything to a cart or submit any forms. These weren’t full audits and I didn’t test with a screen reader, so real screen reader behavior may differ in places. Store names are left out on purpose.
Relevant WCAG 2.2 criteria: 2.1.1 Keyboard, 2.4.7 Focus Visible, 1.3.1 Info and Relationships, 4.1.2 Name, Role, Value, and 3.2.2 On Input.
If you’d like me to check your store’s picker and four other high-impact areas, I’ll send you a free 5-point summary. If the fixes are small, I can do them for a fixed price.
Want to know where your store stands?
I offer a free 5-point accessibility summary: the most important issues on your site, in plain language, with no obligation.
Get a free 5-point summary