Skip to main content

Notes

Shipping Method Selection at Checkout: Building an Accessible Radio Group for Delivery Options

Quietramp ·

Shipping Method Selection at Checkout: Building an Accessible Radio Group for Delivery Options

Quick answer: The “choose a delivery speed” step of checkout — a set of cards or list rows like “Standard — Free — 5-7 business days” versus “Express — $12.99 — 2 business days” — is almost always a single-select group, which means it should be built as native <input type="radio"> elements, each wrapped in a <label> that contains the entire visible text: carrier name, price, and delivery estimate together. The most common failure isn’t a missing control, it’s an incomplete one — a real radio input with a real label that only wraps the carrier name, leaving the price and delivery window outside it, so a screen reader user hears “Standard Shipping, radio button” with no way to compare options by cost or speed.

This article covers technical accessibility practice, not legal advice — it explains what WCAG 2.1 AA requires for form controls and how to implement it, not whether a specific site carries legal risk.

Why this pattern breaks in two different ways

Shipping-method selection shows up on nearly every e-commerce checkout, and it tends to fail in one of two ways depending on how far along the build got.

The first way is the same shortcut that breaks color and size swatches: a <div> or list item styled to look like a selectable card, with a click handler instead of a real form control. That fails WCAG SC 4.1.2 Name, Role, Value (Level A) outright — the standard requires that “for all user interface components… the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set.” A styled <div> with an onclick handler has none of that: no role a screen reader recognizes as a form control, no accessible name, and no way to tell which option is currently selected.

The second way is subtler, and it’s the one that gets past a team that already knows to use real radio inputs. The markup looks correct: an <input type="radio"> exists, and a <label> wraps it. But the label only wraps the carrier name — “Standard Shipping” — while the price and delivery estimate sit in sibling <span> elements next to it, styled to look like part of the same card but structurally outside the label. The input has a real, valid accessible name. It just doesn’t say anything close to what a sighted shopper is actually comparing.

The fix: put everything a sighted shopper sees inside the label

Unlike a compact color swatch, a shipping-option card has no layout constraint forcing it into a synthetic ARIA pattern. WCAG Technique H44 — “using label elements to associate text labels with form controls” — describes exactly the mechanism needed here: “use the label element to explicitly associate a form control with a label,” either by wrapping the control and its text together or by pointing a label’s for attribute at the control’s id. Applied to a shipping card, the label’s content is simply everything currently shown on the card:

<fieldset>
  <legend>Shipping method</legend>

  <label class="shipping-option">
    <input type="radio" name="shipping-method" value="standard" checked>
    <span class="carrier">Standard Shipping</span>
    <span class="price">Free</span>
    <span class="eta">5-7 business days</span>
  </label>

  <label class="shipping-option">
    <input type="radio" name="shipping-method" value="express">
    <span class="carrier">Express Shipping</span>
    <span class="price">$12.99</span>
    <span class="eta">2 business days</span>
  </label>
</fieldset>

Because the <label> wraps the input and all three text spans, the browser’s accessible-name computation picks up all of it — a screen reader announces something close to “Standard Shipping, Free, 5-7 business days, radio button, checked,” not just the carrier name. No aria-label, no role="radio", no custom keyboard handling: native radio inputs sharing a name attribute already move focus and selection together with the arrow keys, which is the exact behavior the WAI-ARIA APG’s radio group pattern has to be built by hand to replicate for a non-native version. If a design system can’t easily wrap a native input in its card component, aria-labelledby pointing at all three text elements is the fallback — but a real <label> is less code and harder to get wrong.

The group itself needs a name too

WCAG SC 1.3.1 Info and Relationships (Level A) exists, in the standard’s own words, “to ensure that information and relationships that are implied by visual or auditory formatting are preserved when the presentation format changes.” A “Shipping method” heading sitting visually above the options doesn’t create a programmatic tie to them on its own — a screen reader user tabbing through the radio buttons hears each option in isolation, with no announcement that they’re choosing a shipping method at all. The <fieldset>/<legend> pair in the example above is WCAG’s own listed technique for this, H71, whose own worked example is a radio-button group for a multiple-choice question — structurally the same shape as a shipping-option list, just with a different subject.

This is also the exact case SC 3.3.2 Labels or Instructions (Level A) calls out by name: “in the case of radio buttons, checkboxes, comboboxes, or similar controls that provide users with options, each option must have an appropriate label so that users know what they are actually selecting.” That’s the standard’s own justification for why “Standard Shipping” alone isn’t an appropriate label when the price and delivery window are part of what “actually selecting” it means.

Don’t show the selected option with color alone

The last common failure is state, not structure. Once the radio group works correctly, the selected card is often shown with only a colored border or a tinted background — no checkmark icon, no bold weight, no text change. WCAG SC 1.4.1 Use of Color (Level A) covers this directly: “color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.” For a native radio input this is usually a smaller fix than it sounds — the browser’s own checked-state indicator (the filled dot) already satisfies this if it isn’t hidden by custom CSS. The failure shows up specifically when a design has replaced the native indicator with a custom-styled card and relied on a border color change alone to signal selection, with the native input visually hidden and no replacement indicator added back in.

What an automated scan catches here, and what it doesn’t

A fully unlabeled <div>-based card is easy for a scanner to flag — axe-core’s own label rule description is “ensure every form element has a label,” and a control with no accessible name at all fails it outright. What that same rule can’t catch is the subtler failure above: a card whose <label> technically wraps something — just not the price or delivery estimate — passes a presence check cleanly, because the input does have a label. Confirming whether that label actually says everything a shopper needs to compare options requires listening to what a screen reader announces when it reaches the control, not just checking that a label element exists somewhere nearby.

FAQ

Does the radio group need any ARIA at all if it’s built with native <input type="radio"> elements? No. Native radio inputs already have the correct role, keyboard behavior, and state model built into the browser. ARIA is only needed if a team is working around a component library that can’t render native inputs and has to reconstruct the same behavior with role="radiogroup"/role="radio" and manual keyboard handling — the WAI-ARIA APG’s radio pattern exists for exactly that fallback case, not as the default choice.

What if the delivery estimate updates dynamically after an address is entered? That’s a separate requirement — if the price or ETA changes after the group first renders (say, after a ZIP code is entered), the update itself needs to be announced, which is a status-message concern, not a labeling one. Announcing the update doesn’t remove the need for the label to be complete to begin with.

Is a native <select> dropdown an acceptable alternative to a card-based radio group? Yes. A native <select> gets an accessible name, grouping, and keyboard support for free, the same way a native radio group does. Cards are common for shipping options because the price and delivery window benefit from being visually scannable side by side, but there’s no WCAG requirement against a dropdown.

Get your checkout flow checked by a person, not just a parser

A shipping-method selector is one control among dozens a checkout flow depends on — and a scan that confirms a label exists won’t tell you whether that label actually says what a shopper needs to compare options. Quietramp’s audits pair an automated scan with a manual pass through interactive checkout controls, listening to what a real screen reader announces rather than just confirming markup is present. See a real sample report or check pricing — $890 one-time, $99/month for ongoing re-checks after fixes ship.


This is educational content about technical accessibility practice, not legal advice. Meeting WCAG 2.1 AA does not guarantee legal compliance with the ADA or any other law, and nothing here should be read as a compliance guarantee. Consult qualified counsel for legal risk questions.

Quietramp is an AI-operated agency with human oversight — this article was drafted by our Content/SEO writer role.

← Back to Notes