Las imágenes deben tener texto alternativo
Las personas ciegas o con baja visión dependen del lector de pantalla para saber qué muestra una imagen. Las fotos de producto, los banners y los logotipos sin texto alternativo se anuncian como un nombre de archivo o se omiten, así que los clientes no saben qué color, modelo u oferta están viendo y pueden abandonar la compra.
- ID de la regla
- image-alt
- Origen
- axe-core 4.13.0
- Impacto
- Crítico
- Nivel de corrección
- ASSISTED
- en Shopify, Shopware, WooCommerce y Magento
- Estado
- se ejecuta
Qué hace la comprobación
Comprueba que los elementos <img> tienen texto alternativo o el rol none o presentation
La regla comprueba cada <img> al que pueden llegar los productos de apoyo, y omite las imágenes ocultas con display: none o aria-hidden="true" en la propia imagen o en un ancestro. Un <img> con otro role válido también se omite, salvo que ese rol exija un nombre accesible o la imagen sea enfocable.
Una imagen supera la prueba con cualquiera de estas opciones: un atributo alt, aunque sea un alt="" vacío; un aria-label no vacío; un aria-labelledby que apunte a un elemento con texto; un title con texto; o un rol resuelto de none o presentation. Ese rol se ignora en una imagen enfocable o que tenga un atributo ARIA global como aria-live. Un alt formado solo por espacios en blanco falla salvo que el rol sea de presentación.
De forma predeterminada, el escáner de Validiscan ejecuta la regla a un tamaño de escritorio y a uno de móvil, así que una imagen oculta con display: none en uno de los tamaños se comprueba solo en el otro. La regla comprueba que exista una alternativa textual, no su redacción; no decide si una imagen es decorativa ni si su texto es adecuado para ella.
Cómo corregirlo
Añada un atributo alt a cada <img>. En las imágenes informativas y en las que funcionan como enlace, describa lo que muestra o hace la imagen, por ejemplo el nombre del producto y la variante de color; use alt="" solo en imágenes puramente decorativas. En los temas, rellene alt con el campo de texto alternativo del archivo multimedia en el panel de administración de la tienda y, si está vacío, con el título del producto.
Criterios de conformidad de las WCAG
- 1.1.1 Non-text Content, nivel A
Apartados de la norma EN 301 549
| Criterio de las WCAG | Versión 3.2.1 | Versión 4.1.1 |
|---|---|---|
| 1.1.1 | 9.1.1.1 | 9.1.1.1 |
Recetas que enumeran esta regla
Esta regla tiene el nivel ASSISTED. En un proyecto, un hallazgo de esta regla sigue la vía ASSISTED, y estas recetas no se le aplican.
Shopify: receta decorative-image-alt
Mostrar el antes y el después (1)
sections/footer.liquid Cambios en sections/footer.liquid (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 12 | 12 | <input type="hidden" name="contact[tags]" value="newsletter"> | |
| 13 | 13 | <div class="newsletter-form__field-wrapper"> | |
| 14 | 14 | <input type="email" name="contact[email]" class="field__input" placeholder="{{ 'newsletter.label' | t }}" autocorrect="off" autocapitalize="off"> | |
| 15 | Eliminada | <button type="submit" class="button newsletter-form__button" name="commit"><img src="{{ 'logo.svg' | asset_url }}" class="newsletter-form__icon" width="16" height="16">{{ 'newsletter.button' | t }}</button> | |
| 15 | Añadida | <button type="submit" class="button newsletter-form__button" name="commit"><img src="{{ 'logo.svg' | asset_url }}" class="newsletter-form__icon" width="16" height="16" alt="">{{ 'newsletter.button' | t }}</button> | |
| 16 | 16 | </div> | |
| 17 | 17 | {%- endform -%} | |
| 18 | 18 | </div> |
sections/header.liquid Cambios en sections/header.liquid (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 46 | 46 | ||
| 47 | 47 | <div class="header__icons"> | |
| 48 | 48 | {%- if section.settings.show_help_link %} | |
| 49 | Eliminada | <a href="{{ section.settings.help_link_url }}" class="header__utility-link link"><svg class="header__utility-icon" role="img" viewBox="0 0 24 24" width="16" height="16"><circle cx="12" cy="12" r="9"/></svg>{{ section.settings.help_link_label | escape }}</a> | |
| 49 | Añadida | <a href="{{ section.settings.help_link_url }}" class="header__utility-link link"><svg class="header__utility-icon" viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/></svg>{{ section.settings.help_link_label | escape }}</a> | |
| 50 | 50 | {%- endif %} | |
| 51 | 51 | <a href="{{ routes.account_login_url }}" class="header__icon header__icon--account link"> | |
| 52 | 52 | {%- render 'icon', icon: 'account', size: icon_size -%} |
Shopify: receta icon-accessible-name
Mostrar el antes y el después (2)
locales/en.default.json Cambios en locales/en.default.json (líneas añadidas: 3; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 34 | 34 | "total_reviews": "{{ count }} reviews", | |
| 35 | 35 | "link_messages": { | |
| 36 | 36 | "new_window": "Opens in a new window." | |
| 37 | Eliminada | } | |
| 37 | Añadida | }, | |
| 38 | Añadida | "accept_cookies": "Accept cookies", | |
| 39 | Añadida | "reject_cookies": "Reject cookies" | |
| 38 | 40 | }, | |
| 39 | 41 | "sections": { | |
| 40 | 42 | "header": { |
sections/header.liquid Cambios en sections/header.liquid (líneas añadidas: 1; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 40 | 40 | <input class="search__input field__input" type="search" name="q" value="" placeholder="{{ 'general.search.placeholder' | t }}" tabindex="1"> | |
| 41 | 41 | <button type="submit" class="search__button"> | |
| 42 | 42 | {%- render 'icon', icon: 'search', size: 20 -%} | |
| 43 | Añadida | <span class="visually-hidden">{{ 'general.search.search' | t }}</span> | |
| 43 | 44 | </button> | |
| 44 | 45 | </form> | |
| 45 | 46 | </div> |
sections/main-product.liquid Cambios en sections/main-product.liquid (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 88 | 88 | <p class="form__label">{{ option.name | escape }}</p> | |
| 89 | 89 | <div class="swatch-list swatch-list--compact"> | |
| 90 | 90 | {%- for swatch in option.swatches %} | |
| 91 | Eliminada | <button type="button" class="swatch swatch--button{% if swatch.selected %} swatch--active{% endif %}" style="--swatch-color: {{ swatch.color }};" data-option-index="{{ option_index }}" data-value="{{ swatch.value | escape }}"></button> | |
| 91 | Añadida | <button type="button" class="swatch swatch--button{% if swatch.selected %} swatch--active{% endif %}" style="--swatch-color: {{ swatch.color }};" data-option-index="{{ option_index }}" data-value="{{ swatch.value | escape }}"><span class="visually-hidden">{{ swatch.value | escape }}</span></button> | |
| 92 | 92 | {%- endfor %} | |
| 93 | 93 | </div> | |
| 94 | 94 | </div> |
sections/slideshow.liquid Cambios en sections/slideshow.liquid (líneas añadidas: 2; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 59 | 59 | <div class="slideshow__controls"> | |
| 60 | 60 | <button type="button" class="slider-button slider-button--prev" name="previous" data-slide-prev> | |
| 61 | 61 | {%- render 'icon', icon: 'chevron-left', size: 20 -%} | |
| 62 | Añadida | <span class="visually-hidden">{{ 'accessibility.previous_slide' | t }}</span> | |
| 62 | 63 | </button> | |
| 63 | 64 | <div class="slider-counter"> | |
| 64 | 65 | {%- for block in section.blocks %} | |
| 67 | 68 | </div> | |
| 68 | 69 | <button type="button" class="slider-button slider-button--next" name="next" data-slide-next> | |
| 69 | 70 | {%- render 'icon', icon: 'chevron-right', size: 20 -%} | |
| 71 | Añadida | <span class="visually-hidden">{{ 'accessibility.next_slide' | t }}</span> | |
| 70 | 72 | </button> | |
| 71 | 73 | </div> | |
| 72 | 74 | </slideshow-component> |
snippets/cookie-banner.liquid Cambios en snippets/cookie-banner.liquid (líneas añadidas: 2; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 23 | 23 | <div class="cookie-banner__actions"> | |
| 24 | 24 | <button type="button" class="cookie-banner__icon-button cookie-banner__icon-button--accept" data-cookie-accept> | |
| 25 | 25 | {%- render 'icon', icon: 'check', size: 20 -%} | |
| 26 | Añadida | <span class="visually-hidden">{{ 'accessibility.accept_cookies' | t }}</span> | |
| 26 | 27 | </button> | |
| 27 | 28 | <button type="button" class="cookie-banner__icon-button" data-cookie-reject> | |
| 28 | 29 | {%- render 'icon', icon: 'close', size: 20 -%} | |
| 30 | Añadida | <span class="visually-hidden">{{ 'accessibility.reject_cookies' | t }}</span> | |
| 29 | 31 | </button> | |
| 30 | 32 | </div> | |
| 31 | 33 | </div> |
snippets/quantity-input.liquid Cambios en snippets/quantity-input.liquid (líneas añadidas: 2; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 9 | 9 | {%- endcomment -%} | |
| 10 | 10 | <div class="quantity quantity--compact" data-quantity{% if line %} data-line="{{ line }}"{% endif %}><button class="quantity__button" name="minus" type="button" data-quantity-minus> | |
| 11 | 11 | {%- render 'icon', icon: 'minus', size: 12 -%} | |
| 12 | Añadida | <span class="visually-hidden">{{ 'products.product.quantity.decrease' | t: product: product_title }}</span> | |
| 12 | 13 | </button> | |
| 13 | 14 | <input class="quantity__input" type="number" name="{{ input_name }}" id="{{ input_id }}" value="{{ value | default: 1 }}" min="{% if line %}0{% else %}1{% endif %}" data-quantity-input> | |
| 14 | 15 | <button class="quantity__button" name="plus" type="button" data-quantity-plus> | |
| 15 | 16 | {%- render 'icon', icon: 'plus', size: 12 -%} | |
| 17 | Añadida | <span class="visually-hidden">{{ 'products.product.quantity.increase' | t: product: product_title }}</span> | |
| 16 | 18 | </button></div> |
sections/footer.liquid Cambios en sections/footer.liquid (líneas añadidas: 1; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 37 | 37 | {%- for social in shop.social %} | |
| 38 | 38 | <a href="{{ social.url }}" class="list-social__link link"> | |
| 39 | 39 | {%- render 'icon', icon: social.icon, size: 20 -%} | |
| 40 | Añadida | <span class="visually-hidden">{{ 'general.social.links.' | append: social.icon | t }}</span> | |
| 40 | 41 | </a> | |
| 41 | 42 | {%- endfor %} | |
| 42 | 43 | </ul> |
sections/header.liquid Cambios en sections/header.liquid (líneas añadidas: 3; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 22 | 22 | </details> | |
| 23 | 23 | ||
| 24 | 24 | {% if logo_is_heading %}<h1 class="header__heading">{% else %}<div class="header__heading">{% endif %} | |
| 25 | Eliminada | <a href="{{ routes.root_url }}" class="header__heading-link"><img src="{{ 'logo.svg' | asset_url }}" width="{{ settings.logo_width }}" height="32" class="header__heading-logo"></a> | |
| 25 | Añadida | <a href="{{ routes.root_url }}" class="header__heading-link"><img src="{{ 'logo.svg' | asset_url }}" width="{{ settings.logo_width }}" height="32" class="header__heading-logo" alt="{{ shop.name | escape }}"></a> | |
| 26 | 26 | {% if logo_is_heading %}</h1>{% else %}</div>{% endif %} | |
| 27 | 27 | ||
| 28 | 28 | <nav class="header__inline-menu"> | |
| 50 | 50 | {%- endif %} | |
| 51 | 51 | <a href="{{ routes.account_login_url }}" class="header__icon header__icon--account link"> | |
| 52 | 52 | {%- render 'icon', icon: 'account', size: icon_size -%} | |
| 53 | Añadida | <span class="visually-hidden">{{ 'sections.header.account' | t }}</span> | |
| 53 | 54 | </a> | |
| 54 | 55 | <a href="{{ routes.cart_url }}" class="header__icon header__icon--cart link" id="cart-icon-bubble"> | |
| 55 | 56 | {%- render 'icon', icon: 'cart', size: icon_size -%} | |
| 57 | 58 | <span class="cart-count-bubble" aria-hidden="true"><span data-cart-count>{{ cart.item_count }}</span></span> | |
| 58 | 59 | {%- endif -%} | |
| 59 | 60 | ||
| 61 | Añadida | <span class="visually-hidden">{{ 'sections.header.cart' | t }}</span> | |
| 60 | 62 | </a> | |
| 61 | 63 | </div> | |
| 62 | 64 | </header> |
sections/main-cart.liquid Cambios en sections/main-cart.liquid (líneas añadidas: 1; eliminadas: 0)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 26 | 26 | {% render 'quantity-input', input_id: qty_id, input_name: 'updates[]', value: item.quantity, product_title: item.product.title, line: line %} | |
| 27 | 27 | <a href="{{ routes.cart_change_url }}?line={{ line }}&quantity=0" class="cart-item__remove cart-item__remove--compact" data-cart-remove> | |
| 28 | 28 | {%- render 'icon', icon: 'trash', size: 16 -%} | |
| 29 | Añadida | <span class="visually-hidden">{{ 'sections.cart.remove' | t: title: item.product.title }}</span> | |
| 29 | 30 | </a> | |
| 30 | 31 | </div> | |
| 31 | 32 | <div class="cart-item__totals"> |
Shopware: receta decorative-image-alt
Mostrar el antes y el después (1)
Resources/views/storefront/layout/footer/footer.html.twig Cambios en Resources/views/storefront/layout/footer/footer.html.twig (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 9 | 9 | <div class="footer-column-content"> | |
| 10 | 10 | <p class="footer-contact-hotline"> | |
| 11 | 11 | {{ 'footer.serviceHotlineText'|trans }} | |
| 12 | Eliminada | <a class="footer-contact-hotline-link" href="tel:+49405550192"><img src="{{ asset('bundles/storefront/assets/icon/default/headset.svg') }}" class="footer-contact-hotline-icon" width="16" height="16"> {{ shop.company.phone }}</a> | |
| 12 | Añadida | <a class="footer-contact-hotline-link" href="tel:+49405550192"><img src="{{ asset('bundles/storefront/assets/icon/default/headset.svg') }}" class="footer-contact-hotline-icon" width="16" height="16" alt=""> {{ shop.company.phone }}</a> | |
| 13 | 13 | </p> | |
| 14 | 14 | <p class="footer-contact-hours">{{ 'footer.serviceHours'|trans }}</p> | |
| 15 | 15 | <p class="footer-contact-form"> |
WooCommerce: receta decorative-image-alt
Mostrar el antes y el después (1)
header.php Cambios en header.php (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 39 | 39 | <div class="site-branding"> | |
| 40 | 40 | <a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="custom-logo-link" rel="home"> | |
| 41 | 41 | <?php // VALIDISCAN-SEED svg-img-alt: the logo mark is role="img" without a title or aria-label. ?> | |
| 42 | Eliminada | <svg class="kb-logo-mark" role="img" width="40" height="40" viewBox="0 0 40 40"><path d="M3 30c6-11 15-17 29-18-7 3-10 8-11 13 6-4 11-5 17-4-9 2-15 8-19 15L3 30Z" fill="#1d3b53"/></svg> | |
| 42 | Añadida | <svg class="kb-logo-mark" width="40" height="40" viewBox="0 0 40 40" aria-hidden="true" focusable="false"><path d="M3 30c6-11 15-17 29-18-7 3-10 8-11 13 6-4 11-5 17-4-9 2-15 8-19 15L3 30Z" fill="#1d3b53"/></svg> | |
| 43 | 43 | <span class="kb-logo-text"><?php bloginfo( 'name' ); ?></span> | |
| 44 | 44 | </a> | |
| 45 | 45 | </div> |
Magento: receta decorative-image-alt
Mostrar el antes y el después (1)
app/design/frontend/Validiscan/fixture/Magento_Cms/content/home.html Cambios en app/design/frontend/Validiscan/fixture/Magento_Cms/content/home.html (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 111 | 111 | <div class="block-promo-wrapper block-promo-hp"> | |
| 112 | 112 | <!-- VALIDISCAN-SEEDED image-alt: promo image without an alt attribute --> | |
| 113 | 113 | <a class="block-promo home-main" href="/magento/women/tops-women/jackets-women.html"> | |
| 114 | Eliminada | <img src="/magento/media/wysiwyg/home/home-main.svg" width="400" height="240" /> | |
| 114 | Añadida | <img src="/magento/media/wysiwyg/home/home-main.svg" width="400" height="240" alt="" /> | |
| 115 | 115 | <span class="content" | |
| 116 | 116 | ><!-- VALIDISCAN-SEEDED color-contrast: .info text is #929292 on white (3.1:1) --><span | |
| 117 | 117 | class="info" |
Empiece con una revisión gratuita de su tienda.
Envíe la dirección de su tienda. Validiscan la escanea y le responde con cada fallo que el escaneo pueda demostrar, con la página y el código.
Solicitar revisión gratuitaO escriba a info@validiscan.com.
Qué cubre la revisión gratuita y qué deja fuera- Usted envía
- la dirección de su tienda
- Páginas escaneadas
- hasta 15
- Usted recibe
- todos los fallos que el escaneo puede demostrar, con la página y el código
- En su tienda
- solo páginas públicas; sin inicio de sesión, nada comprado, ningún formulario enviado
- Lo que no es
- una auditoría completa