
Shopify Form Accessibility: Labels, Errors, Autocomplete in Liquid
Form-label failures are the third-most-cited violation in ADA Title III digital-accessibility demand letters, after missing alt text and color contrast. Most Shopify themes ship with at least one — a newsletter signup with a placeholder-only "Email" field, a checkout that omits autocomplete attributes, an error message that turns the input red without saying what went wrong. This guide covers the four form-related WCAG criteria + the exact Liquid pattern that fixes each on Shopify's native templates.
Why "placeholder only" is not enough
The most common Shopify form failure:
{%- comment -%} Wrong — placeholder used as label {%- endcomment -%}
<form action="/contact#newsletter" method="post">
<input type="hidden" name="form_type" value="customer">
<input type="email" name="contact[email]" placeholder="Email">
<button type="submit">Subscribe</button>
</form>
Three problems:
- No programmatic label. Screen readers announce the input as "edit text" with no field-name context.
- No
autocomplete. Browser autofill cannot pre-populate the email field. - Placeholder vanishes when the user types, so users with cognitive disabilities who lose track of what field they're in have no recovery cue.
The right pattern — visible label + autocomplete + label-input association
<form action="/contact#newsletter" method="post" class="newsletter-form">
<input type="hidden" name="form_type" value="customer">
<input type="hidden" name="contact[tags]" value="newsletter">
<label for="newsletter-email">Email</label>
<input
type="email"
id="newsletter-email"
name="contact[email]"
autocomplete="email"
required
aria-describedby="newsletter-email-help">
<p id="newsletter-email-help" class="form-help">
We\'ll email occasional accessibility-compliance updates. Unsubscribe anytime.
</p>
<button type="submit">Subscribe</button>
</form>
Key elements:
<label for="newsletter-email">paired withid="newsletter-email"on the input — the canonical programmatic association.autocomplete="email"— declares the input purpose for autofill + accessibility extensions.aria-describedby="newsletter-email-help"— connects helper text to the input so screen readers read it after the field name.- The label is visible, not hidden in
.sr-only— visible labels help everyone, not just screen-reader users.
If brand design genuinely requires the label to be hidden (a single-field newsletter where space is constrained), use .sr-only rather than removing the label entirely:
<label for="newsletter-email" class="sr-only">Email</label>
<input
type="email"
id="newsletter-email"
name="contact[email]"
placeholder="Email"
autocomplete="email"
required>
The placeholder is now a visual fallback for sighted users; the screen-reader user gets the proper label.
The autocomplete value table — what to use where
WCAG 1.3.5 references the HTML Living Standard's autocomplete value list. The values you'll use most on Shopify forms:
| Field | autocomplete value |
|---|---|
email | |
| Phone | tel |
| Phone (country code) | tel-country-code |
| Full name | name |
| First name | given-name |
| Last name | family-name |
| Street address line 1 | address-line1 |
| Street address line 2 | address-line2 |
| City | address-level2 |
| State / region | address-level1 |
| Postal / ZIP code | postal-code |
| Country | country |
| Country code | country-name |
| Birthday | bday |
| New password | new-password |
| Existing password | current-password |
| One-time code | one-time-code |
| Credit card number | cc-number |
| Credit card expiry month | cc-exp-month |
| Credit card expiry year | cc-exp-year |
| Credit card security code | cc-csc |
Shopify's native checkout sets these correctly. Custom storefront forms (Hydrogen, custom signup, third-party app embeds) often miss them — audit every form on the storefront, not just the native checkout.
Glossary: HTML autocomplete attribute →
Error identification — the right way
Setup: a customer signs up with an existing email. The form should surface the error so screen-reader users know what failed.
The wrong way — color-only:
{%- if form.errors -%}
<input type="email" name="contact[email]" class="error-red-border">
{%- endif -%}
Red border tells sighted users something is wrong. Tells screen-reader users nothing.
The right way — aria-invalid + aria-describedby + role="alert":
{%- assign has_error = form.errors and form.errors.email -%}
<label for="signup-email">Email</label>
<input
type="email"
id="signup-email"
name="customer[email]"
autocomplete="email"
required
{% if has_error %}aria-invalid="true" aria-describedby="signup-email-error"{% endif %}>
{%- if has_error -%}
<p id="signup-email-error" role="alert" class="form-error">
{{ form.errors.translated_fields.email }} {{ form.errors.messages.email }}.
Try a different email or sign in if you already have an account.
</p>
{%- endif -%}
Three things happen:
aria-invalid="true"flags the field as invalid for screen readers.aria-describedbyconnects the input to the error message so the message is announced after the field name.role="alert"tells the screen reader to announce the error immediately when it appears (implicitaria-live="assertive").
The error text itself satisfies WCAG 3.3.3 Error Suggestion — it explains what went wrong AND how to fix it.
Common Shopify form-accessibility failures
1. Newsletter signup with placeholder-only label
Cause: theme <input placeholder="Email"> with no label. Fix: add <label for> + matching id + autocomplete="email".
2. Search bar with no accessible name
Cause: theme renders <input type="search"> with no label or aria-label. Fix: <label for="search-input" class="sr-only">Search</label> or aria-label="Search".
3. Quantity stepper with no label
Cause: cart line-item quantity input is <input type="number"> with no label. Fix: <label for="qty-{{ line.key }}" class="sr-only">Quantity</label> so screen readers know which line item the field controls.
4. Checkout custom field added via cart-attribute script
Cause: merchant adds gift-message or order-note input via Liquid; ships without label. Fix: every custom cart-attribute or line-item-property field needs <label> + autocomplete (where applicable).
5. Customer-account login that blocks paste
Cause: theme has onpaste="return false" on the password field (often inherited from a theme-customization tutorial). Fix: remove onpaste and autocomplete="off"; the password field should accept paste so password managers work. WCAG 3.3.8 Accessible Authentication explicit requirement at WCAG 2.2 AA.
6. Form errors flagged by color only
Cause: stylesheet adds border-color: red on .error class but no text content. Fix: add the aria-invalid + aria-describedby + role="alert" pattern above.
The full Liquid newsletter pattern
{%- form 'customer', id: 'newsletter-form', class: 'newsletter-form' -%}
<input type="hidden" name="contact[tags]" value="newsletter">
<label for="newsletter-email">{{ 'general.newsletter.email_label' | t }}</label>
<div class="newsletter-form__input-wrap">
<input
type="email"
id="newsletter-email"
name="contact[email]"
autocomplete="email"
placeholder="{{ 'general.newsletter.email_placeholder' | t }}"
required
aria-required="true"
{% if form.errors %}aria-invalid="true" aria-describedby="newsletter-email-error"{% endif %}>
<button type="submit">
{{ 'general.newsletter.submit' | t }}
</button>
</div>
{%- if form.errors -%}
<p id="newsletter-email-error" role="alert" class="newsletter-form__error">
{{- form.errors | default_errors -}}
</p>
{%- elsif form.posted_successfully? -%}
<p role="status" class="newsletter-form__success">
{{ 'general.newsletter.success' | t }}
</p>
{%- endif -%}
{%- endform -%}
The role="status" on the success message is the WCAG 4.1.3 Status Messages compliance for the success path.
Quick checklist
- Every
<input>,<select>,<textarea>has a<label for>+ matchingid. - Every user-info field has a correct
autocompletevalue. - Required fields have both
required+aria-required="true". - Error messages use
aria-invalid="true"on the input +aria-describedbylinking to the error +role="alert". - Error messages explain what went wrong AND how to fix it.
- Success messages use
role="status"so screen readers announce them. - Password fields do not block paste (
onpaste="return false"removed). - Search inputs have
<label class="sr-only">oraria-label. - Cart-attribute / line-item-property custom fields have labels.
Verify your fix
Run the free AccessComply scan on the storefront. The scan reports per-form failures with the exact selector + WCAG criterion. Free tier includes 3 scans per month with no signup.
Further reading
- WCAG 3.3.2 Labels or Instructions deep-dive
- WCAG 1.3.5 Identify Input Purpose deep-dive
- WCAG 3.3.1 Error Identification deep-dive
- WCAG 3.3.3 Error Suggestion deep-dive
- WCAG 4.1.3 Status Messages deep-dive
- Glossary — HTML autocomplete attribute
- Glossary — aria-live region
- HTML Living Standard — autocomplete values
- Shopify accessibility complete guide
- Shopify checkout accessibility guide
Scan your store free, fix violations at the source
AccessComply scans your Shopify store for ADA + EAA / WCAG 2.1 + 2.2 AA violations and applies real source-code fixes — no overlays, no widgets.