Accy

Example report — what an Accy scan looks like

This is a real scan of an example shop carrying the mistakes real themes make. Your report will have exactly this shape.

No sign-up. We scan one public page and store nothing about you.

Scanned on 15 September 2026 at 20:55

tienda-de-ejemplo.es

16/ 100

Critical risk

  • 22 rules failed
  • 9 rules passed
  • 5 to review by hand

29 findings in total, split like this

  • 6 Critical
  • 15 Serious
  • 7 Moderate
  • 1 Minor
Platform detected
Shopify · Dawn
No accessibility statement found
Generate one, free →

What to fix

  1. Form fields with no label

    Critical

    1 time on this page · WCAG 1.3.1, 3.3.2, 4.1.2 (A) · EN 301 549 9.1.3.1, 9.3.3.2, 9.4.1.2

    Some form fields have no accessible name: no <label>, no aria-label, no aria-labelledby.

    Why it matters

    An unlabelled field is announced as "edit text" and nothing more. In a search box that is annoying; in the shipping address at checkout it makes the purchase impossible to complete without guessing.

    A confusing field at checkout is an abandoned basket. This is not only compliance, it is conversion.

    How to fix it

    Associate a <label> using the for attribute pointing at the field id, or wrap the field inside the label. A placeholder does not count: it disappears as soon as someone types, and many screen readers never announce it.

    On your platform · Shopify

    Shopify's own checkout fields are already labelled. Check the ones you added: footer newsletter, header search, and any third-party app forms.

    As it is
    <input type="email" name="email">
    As it should be
    <label for="email">Email address</label>
    <input type="email" id="email" name="email" autocomplete="email">
    
    <!-- Or by wrapping -->
    <label>Postcode
      <input type="text" name="postcode" autocomplete="postal-code">
    </label>
    See where it happens
    • body.template-index > div.newsletter:nth-of-type(5) > form > input:nth-of-type(1)
      <input type="email" name="email">
  2. Buttons with no name

    Critical

    1 time on this page · WCAG 4.1.2 (A) · EN 301 549 9.4.1.2

    Some buttons have no visible text and no accessible name.

    Why it matters

    This usually hits icon-only buttons: the close X, the search magnifier, the basket in the header. A screen reader announces them as "button" and the person has to activate them to find out what they do.

    How to fix it

    If the button has visible text, you are done. If it is icon-only, add an aria-label with the action and mark the icon aria-hidden="true".

    On your platform · Shopify

    In Dawn and its derivatives, header icon buttons live in sections/header.liquid.

    As it is
    <button><svg>…</svg></button>
    As it should be
    <button aria-label="Dismiss notice">
      <svg aria-hidden="true">…</svg>
    </button>
    See where it happens
    • body.template-index > div.header:nth-of-type(1) > button.cart-toggle

      There is only an icon, with no title or label.

      <button class="cart-toggle"><svg viewBox="0 0 24 24"><path d="M7 4h14l-2 9H8z" /></svg></button>
  3. Insufficient colour contrast

    Serious

    4 times on this page · WCAG 1.4.3 (AA) · EN 301 549 9.1.4.3

    There is 18px text with a contrast ratio of 2.35:1 against its background, where the standard requires 4.5:1.

    Why it matters

    This is the most widespread barrier and the one that affects the most people: anyone with tired eyes, a phone in bright sunlight, or a cheap screen. You do not need a diagnosed disability to be unable to read light grey on white.

    The text that usually fails is exactly the text that sells: struck-through prices, delivery costs, stock messages.

    How to fix it

    Darken the text or lighten the background until it meets the required ratio. Normal text needs 4.5:1; from 24px (or 18.66px bold) 3:1 is enough. Greys around #999 on white almost never pass.

    On your platform · Shopify

    In the theme editor, Settings › Colors lets you change the theme colour schemes without touching code.

    As it is
    /* 2.85:1 — fails */
    .old-price { color: #999999; background: #ffffff; }
    As it should be
    /* 4.83:1 — passes */
    .old-price { color: #717171; background: #ffffff; }
    See where it happens
    • body.template-index > div.hero:nth-of-type(2) > p.hero-sub
      <p class="hero-sub">Fabricada en España con algodón orgánico certificado.</p>
    • body.template-index > div.grid:nth-of-type(3) > div.card:nth-of-type(1) > span.price-old:nth-of-type(1)
      <span class="price-old">29,00 €</span>
    • body.template-index > div.grid:nth-of-type(3) > div.card:nth-of-type(1) > a:nth-of-type(2)
      <a href="/products/camiseta-arena">Leer más</a>
    • body.template-index > div.footer:nth-of-type(6) > p.legal
      <p class="legal">© 2026 Tienda de ejemplo. Todos los derechos reservados.</p>
  4. No accessibility statement found

    Serious

    1 time on this page · EN 301 549 Annex V

    We found no link to an accessibility statement on this page.

    Why it matters

    The European Accessibility Act requires service providers to publish information about how the service meets accessibility requirements. It is the one thing a market surveillance authority can check without opening a single developer tool: they look at your footer, and if it is not there, they have a documented failure.

    This is the cheapest failure on the list to fix and the easiest to spot from outside. Fixing it is an afternoon; being caught is a case file.

    How to fix it

    Publish an accessibility page and link it from the footer, next to your legal notice and privacy policy. State the conformance level you claim, the parts you know do not yet conform, the date of the last review, and a way to report barriers.

    On your platform · Shopify

    Create the page under Online Store › Pages, then add it to your footer menu under Navigation.

    As it is
    <footer>
      <a href="/legal">Legal notice</a>
      <a href="/privacy">Privacy</a>
    </footer>
    As it should be
    <footer>
      <a href="/legal">Legal notice</a>
      <a href="/privacy">Privacy</a>
      <a href="/accessibility">Accessibility statement</a>
    </footer>
  5. Alternative text that carries no information

    Serious

    1 time on this page · WCAG 1.1.1 (A) · EN 301 549 9.1.1.1

    Some images have alt text that is a file name, a code, or a generic word like "image" or "photo".

    Why it matters

    An alt that reads "DSC_0042.jpg" or "product" satisfies the form but not the function: the person still has no idea what is in the picture. It usually happens when a catalogue was bulk-imported and the field was filled automatically.

    How to fix it

    Describe the actual content. For products the formula that works is: what it is + distinguishing feature + variant. "Organic cotton t-shirt in sand" works; "product" does not.

    On your platform · Shopify

    If you imported your catalogue from a CSV, check the "Image Alt Text" column and re-import with real descriptions instead of file names.

    As it is
    <img src="/p1.jpg" alt="image">
    <img src="/p2.jpg" alt="DSC_0042.jpg">
    As it should be
    <img src="/p1.jpg" alt="Grey running shoe with orange sole">
    <img src="/p2.jpg" alt="Black canvas 20-litre backpack, side view">
    See where it happens
    • body.template-index > div.grid:nth-of-type(3) > div.card:nth-of-type(2) > a:nth-of-type(1) > img
      <img src="/cdn/shop/products/sudadera.jpg" alt="imagen">
  6. Embedded frame with no title

    Serious

    1 time on this page · WCAG 4.1.2, 2.4.1 (A) · EN 301 549 9.4.1.2

    There is an <iframe> with no title attribute.

    Why it matters

    Screen readers can list the frames on a page. With no title they show up as "frame", "frame", "frame", and the person has to enter each one to find out which is the video, which the map and which the chat widget.

    Thirty seconds per frame to fix, and it appears in every audit report.

    How to fix it

    Add a title describing what the frame contains. If the iframe is an invisible tracking pixel, mark it aria-hidden="true" as well as hiding it.

    On your platform · Shopify

    If the video comes from a theme block, edit the template under Online Store › Themes › Edit code and add the title to the iframe.

    As it is
    <iframe src="https://www.youtube.com/embed/xyz"></iframe>
    As it should be
    <iframe src="https://www.youtube.com/embed/xyz"
            title="Video: how to choose your size"></iframe>
    See where it happens
    • body.template-index > iframe
      <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" width="560" height="315"></iframe>
  7. The placeholder is doing the label’s job

    Serious

    1 time on this page · WCAG 3.3.2 (A) · EN 301 549 9.3.3.2

    Some fields get their only accessible name from the placeholder attribute.

    Why it matters

    Placeholder text vanishes the moment someone starts typing. Anyone who gets distracted halfway through a form can no longer tell what belonged in that field, and anyone with memory or attention difficulties is left with no reference at all. Its contrast is usually very low by design, too.

    How to fix it

    Add a visible <label>. You can keep the placeholder as a format hint ("e.g. SW1A 1AA"), but never as a substitute for the field name.

    As it is
    <input type="text" name="postcode" placeholder="Postcode">
    As it should be
    <label for="postcode">Postcode</label>
    <input type="text" id="postcode" name="postcode"
           placeholder="e.g. SW1A 1AA" autocomplete="postal-code">
    See where it happens
    • body.template-index > div.header:nth-of-type(1) > form.search > input
      <input type="text" name="q" placeholder="Buscar productos">
  8. Data table with no header cells

    Serious

    1 time on this page · WCAG 1.3.1 (A) · EN 301 549 9.1.3.1

    There is a table with data but no <th> cells.

    Why it matters

    In a properly marked-up table, a screen reader announces "Size: M, Price: £19" as the person moves between cells. Without headers it reads only "M, £19", and they have to remember the column order.

    How to fix it

    Turn the first row (or column) into <th> and add scope="col" or scope="row".

    On your platform · Shopify

    Size guide tables are usually written in the rich text editor of the product description; edit the HTML with the "</>" button.

    As it is
    <tr><td>Size</td><td>Price</td></tr>
    <tr><td>M</td><td>£19</td></tr>
    As it should be
    <tr><th scope="col">Size</th><th scope="col">Price</th></tr>
    <tr><td>M</td><td>£19</td></tr>
    See where it happens
    • body.template-index > div.sizes:nth-of-type(4) > table
      <table> <tr><td>Talla</td><td>Pecho</td><td>Largo</td></tr> <tr><td>S</td><td>96 cm</td><td>68 cm</td></tr> <tr><td>M</td><td>101 cm</td><td>70 cm</td></tr> </table>
  9. The focus indicator is switched off

    Serious

    1 time on this page · WCAG 2.4.7 (AA) · EN 301 549 9.2.4.7

    CSS removes the focus outline (a:focus) without putting anything visible in its place.

    Why it matters

    Anyone navigating with Tab needs to see where they are. With no focus indicator the person presses Tab and cannot tell which element is selected: navigating turns into guessing. It is one of the most disabling failures and one of the easiest to fix.

    A single `outline: none` in a theme stylesheet can make the whole shop unusable by keyboard.

    How to fix it

    Never use outline: none without a replacement. If the default ring clashes with your design, draw your own, with at least 3:1 contrast against the background.

    As it is
    a:focus, button:focus { outline: none; }
    As it should be
    a:focus-visible, button:focus-visible {
      outline: 3px solid #005fcc;
      outline-offset: 2px;
    }
    See where it happens
    • a:focus
      a:focus { outline: none }
  10. Clickable element unreachable by keyboard

    Serious

    1 time on this page · WCAG 2.1.1, 4.1.2 (A) · EN 301 549 9.2.1.1

    There is a <div> with a click handler but no role and no place in the tab order.

    Why it matters

    A mouse can click anything; a keyboard cannot. A <div> with onclick is invisible to anyone navigating with Tab, with a switch, or by voice control. If that div is "Add to basket", the shop is unusable.

    This is the failure that most often leaves a checkout completely blocked.

    How to fix it

    Use <button>. It handles focus, activation with Enter and Space, and role announcement, all at once and with no extra JavaScript.

    As it is
    <div onclick="addToCart()">Add to basket</div>
    As it should be
    <button type="button" onclick="addToCart()">Add to basket</button>
    See where it happens
    • body.template-index > div.hero:nth-of-type(2) > div.cta
      <div class="cta" onclick="location.href='/collections/novedades'">Ver la colección</div>
  11. Video with no captions

    Serious

    1 time on this page · WCAG 1.2.2 (A) · EN 301 549 9.1.2.2

    There is a <video> with no captions track.

    Why it matters

    Without captions the video does not exist for anyone who cannot hear. Nor for the majority of people browsing on a phone with the sound off, which is nearly everyone.

    How to fix it

    Add a <track kind="captions"> with a WebVTT file. If the video is hosted on YouTube or Vimeo, turn captions on there and proofread them: the automatic ones are usually wrong.

    As it is
    <video src="/promo.mp4" controls></video>
    As it should be
    <video src="/promo.mp4" controls>
      <track kind="captions" src="/promo-en.vtt" srclang="en" label="English" default>
    </video>
    See where it happens
    • body.template-index > div.hero:nth-of-type(2) > video
      <video src="/cdn/shop/videos/hero.mp4" autoplay loop></video>
  12. Sound that plays by itself

    Serious

    1 time on this page · WCAG 1.4.2 (A) · EN 301 549 9.1.4.2

    There is a <video> with autoplay and no mute.

    Why it matters

    Unexpected audio talks over the screen reader and makes the page unreadable until it can be stopped. Finding the pause button without seeing the screen, while it is playing, is exactly as hard as it sounds.

    How to fix it

    Remove autoplay, or add muted. If you need automatic playback, keep it under three seconds or give it a stop control at the top of the page.

    As it is
    <video src="/hero.mp4" autoplay loop></video>
    As it should be
    <video src="/hero.mp4" autoplay loop muted playsinline></video>
    See where it happens
    • body.template-index > div.hero:nth-of-type(2) > video
      <video src="/cdn/shop/videos/hero.mp4" autoplay loop></video>
  13. Missing document language

    Serious

    1 time on this page · WCAG 3.1.1 (A) · EN 301 549 9.3.1.1

    The <html> element declares no valid language.

    Why it matters

    Screen readers pick the voice and pronunciation from that attribute. Without it, a Spanish shop can be read with English phonetics, and nothing is intelligible.

    A thirty-second fix affecting every page on the site. Start here.

    How to fix it

    Add lang="en" (or your language) to the <html> element. One attribute, one line, and it fixes the whole page.

    On your platform · Shopify

    In layout/theme.liquid, the first line should be <html lang="{{ request.locale.iso_code }}">.

    As it is
    <html>
    As it should be
    <html lang="en">
  14. The page blocks zooming

    Serious

    1 time on this page · WCAG 1.4.4 (AA) · EN 301 549 9.1.4.4

    The viewport tag blocks zoom (width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no).

    Why it matters

    Plenty of people zoom in on their phone to read. Blocking it for design reasons simply cuts them off from the content. The standard requires zooming to at least 200%.

    How to fix it

    Remove user-scalable=no and any maximum-scale below 2.

    As it is
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    As it should be
    <meta name="viewport" content="width=device-width, initial-scale=1">
    See where it happens
    • head > meta:nth-of-type(2)
      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  15. Heading levels skipped

    Moderate

    1 time on this page · WCAG 1.3.1 (A) · EN 301 549 9.1.3.1

    The page jumps from one heading level to another, skipping the ones in between (h1 to h4).

    Why it matters

    Headings are the table of contents people navigate a page by. When levels skip, that outline stops reflecting the real hierarchy and the person cannot tell whether the new section belongs to the previous one or starts fresh.

    How to fix it

    Use levels in order. If you skipped a level because of font size, set the correct level and adjust the size in CSS.

    As it is
    <h1>Shop</h1>
    <h4>Offers</h4>
    As it should be
    <h1>Shop</h1>
    <h2 class="small-heading">Offers</h2>
    See where it happens
    • body.template-index > h4
      <h4>Más vendidos</h4>
  16. Non-existent ARIA role

    Moderate

    1 time on this page · WCAG 4.1.2 (A) · EN 301 549 9.4.1.2

    An element has role="buton", which is not a valid role.

    Why it matters

    A misspelled role does nothing: the browser ignores it and the element is exposed with its original semantics, which is almost always "no meaning at all". The developer believes it is fixed, and it is not.

    How to fix it

    Correct the role name, or drop it and use the native HTML element, which already carries the right role.

    As it is
    <span role="buton">Buy</span>
    As it should be
    <button type="button">Buy</button>
    See where it happens
    • body.template-index > span
      <span role="buton" aria-labl="ayuda" tabindex="4">Ayuda</span>
  17. Non-existent ARIA attribute

    Moderate

    1 time on this page · WCAG 4.1.2 (A) · EN 301 549 9.4.1.2

    An element has the attribute aria-labl, which is not part of the specification.

    Why it matters

    Same as an invented role: the browser discards it silently. These are usually typos (aria-labl, aria-describe) that leave the element without the name or description it was meant to have.

    How to fix it

    Check the attribute spelling against the WAI-ARIA specification.

    See where it happens
    • body.template-index > span
      <span role="buton" aria-labl="ayuda" tabindex="4">Ayuda</span>
  18. Positive tabindex value

    Moderate

    1 time on this page · WCAG 2.4.3 (A) · EN 301 549 9.2.4.3

    Some elements use tabindex="4".

    Why it matters

    A positive tabindex pulls the element out of its natural place in the tab order and puts it ahead of everything else. Focus then jumps around the page in an order that does not match what is on screen, and it becomes unmaintainable as the page grows.

    How to fix it

    Use tabindex="0" to make an element focusable, or tabindex="-1" to focus it programmatically. If the order is wrong, change the order of the HTML.

    See where it happens
    • body.template-index > span
      <span role="buton" aria-labl="ayuda" tabindex="4">Ayuda</span>
  19. Missing main content region

    Moderate

    1 time on this page · WCAG 1.3.1 (A) · EN 301 549 9.1.3.1

    The page has no <main>, or has more than one.

    Why it matters

    The "skip to main content" shortcut is the fastest way past the navigation, which repeats identically on every page. Without <main> that shortcut does not exist and the whole menu has to be tabbed through each time.

    How to fix it

    Wrap the page’s own content (not the header, navigation or footer) in a single <main>.

  20. Duplicate ids

    Minor

    1 time on this page · WCAG 4.1.1 (A) · EN 301 549 9.4.1.1

    The id "col" appears 3 times.

    Why it matters

    Accessibility references (label for, aria-labelledby, aria-describedby) point at an id and always resolve to the first match. With duplicate ids, one field’s label can end up describing another.

    How to fix it

    Make every id unique. If it comes from a repeated component, append the index or the product id.

    See where it happens
    • #col
      <div id="col"><a href="/pages/privacidad">Política de privacidad</a></div>

To review by hand

  • ARIA reference to an id that does not exist

    The attribute aria-labelledby points at the ids titulo-inexistente, which are not on the page.

    Check the id exists and matches exactly, case included. If the target is gone, replace the reference with an aria-label.

  • Personal data fields with no autocomplete

    Some fields asking for common personal data (email, phone, address) have no autocomplete attribute.

    Add the matching standard token: autocomplete="email" in this case.

  • Required field marked visually only

    The field label says it is required (with an asterisk or the word), but the field itself declares neither required nor aria-required.

    Add the required attribute to the field. Keep the visual marker too: both, not one or the other.

  • Missing landmark regions

    The page does not mark up some of its main regions: navigation, banner, contentinfo.

    Use the correct semantic elements: <header>, <nav>, <main> and <footer>, instead of <div class="header">.

  • Extremely small text

    There is text below 10px (9px).

    Go up to at least 12px, and 16px in the main content. Use relative units (rem) so it respects browser settings.

What is already fine

  • Images with no alternative text
  • Redundant alternative text
  • Meaningful SVG icon with no name
  • Links that display the full URL
  • Missing main heading, or more than one
  • The page has no headings
  • Empty headings
  • Element with an interactive role but no name
  • Missing or empty page title

How far this report goes

We scanned a single page, using the HTML and CSS the server returns. We do not execute JavaScript, so anything generated after load (dropdown menus, modals, checkout steps) is out of scope. And no automated tool covers more than 30-40% of WCAG criteria: the rest requires a person testing the shop with a keyboard and a screen reader.

References

This report is not legal advice.

Want this in writing?

We will send you this report as a PDF, the order worth fixing it in, and a note if the law changes in a way that affects you. Written by a person, not an automated newsletter, and your address goes nowhere else.

Used for this and nothing else.

Keep it from breaking again

Every theme change, every new app and every imported product can reintroduce these barriers. Monitoring scans your whole shop weekly, alerts you when something gets worse, and keeps the dated history — which is exactly what you show if anyone asks.

See plans