Deben anunciarse los cambios de precio, variante, existencias y carrito

Cuando un cliente elige una talla y cambian el precio o la disponibilidad, o cuando añade un producto al carrito, los clientes videntes ven el cambio. Quienes usan un lector de pantalla no oyen nada, así que no saben que el producto se ha añadido ni que la talla elegida está agotada.

ID de la regla
vs-live-updates
Origen
Comprobación de Validiscan
Impacto
Grave
Nivel de corrección
AUTO
en Shopify, Shopware, WooCommerce y Magento
Estado
se ejecuta

Qué hace la comprobación

Detecta cambios de precio, variante, existencias y carrito en las páginas de producto y de carrito que se producen sin una región dinámica (live region), así como regiones aria-live que envuelven toda la página (4.1.3).

La comprobación se ejecuta solo en las páginas de producto y de carrito. Se notifica cada aria-live="polite" o "assertive" que haya en html, body, main o en un elemento que contenga main.

Con el envío de formularios bloqueado, la comprobación cambia un control situado fuera de la cabecera, la navegación, el pie de página y los cuadros de diálogo. Página de producto: el primer select de variante (cuyo nombre alude a la talla, el color o algo similar, o que está en la zona del producto); si no lo hay, un grupo de botones de opción de variante, y, si tampoco lo hay, botones de muestra con uno seleccionado. Página de carrito: el primer campo de cantidad, que cambia en una unidad.

Antes y después, lee los textos de hasta 120 caracteres cuyo itemprop, clase o id sugiere un precio, las existencias, el número de artículos del carrito o un total, y el botón de añadir al carrito; la página dispone de hasta 2 segundos para estabilizarse. Un texto visible que cambia se notifica, salvo que esté dentro de una región dinámica (live region) o la contenga (aria-live="polite" o "assertive", role="status", "alert", "log", output), o que otra región dinámica haya anunciado un cambio (en el caso de los precios y las existencias, una que contenga el nuevo valor). Un cambio que no se revierte al restablecer el control pasa a «pendiente de revisión», un estado que hoy no se convierte en hallazgo.

Cómo corregirlo

Añada una región dinámica pequeña y permanente (role="status" o aria-live="polite") para el precio, las existencias y el número de artículos del carrito, y actualice su texto cuando cambien la variante o el carrito. No ponga aria-live en contenedores grandes ni en toda la página, porque los lectores de pantalla vuelven a leerlo todo.

Criterios de conformidad de las WCAG

Apartados de la norma EN 301 549

El apartado de la norma EN 301 549 que corresponde a cada criterio, por versión de la norma
Criterio de las WCAG Versión 3.2.1 Versión 4.1.1
4.1.3 9.4.1.3 9.4.1.3

Dónde lo corrige una receta

Shopify: receta live-region-cart-variant

Mostrar el antes y el después (1)
Antes y después Datos de ejemplo

sections/main-cart.liquid Cambios en sections/main-cart.liquid (líneas añadidas: 1; eliminadas: 1)

Cambios en sections/main-cart.liquid (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
37 37
38 38 <div class="cart__footer">
39 39 <h2 class="cart__summary-title">Order summary</h2>
40 Eliminada <p class="totals">
40 Añadida <p class="totals" role="status">
41 41 <span class="totals__label">{{ 'sections.cart.subtotal' | t }}</span>
42 42 <span class="totals__value" data-cart-subtotal>{{ cart.total_price | money_with_currency }}</span>
43 43 </p>

sections/main-product.liquid Cambios en sections/main-product.liquid (líneas añadidas: 1; eliminadas: 1)

Cambios en sections/main-product.liquid (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
71 71 </div>
72 72 {%- endif %}
73 73
74 Eliminada <div class="product__price" data-price-container>
74 Añadida <div class="product__price" data-price-container role="status">
75 75 {% render 'price', product: product, use_variant: true %}
76 76 <p class="product__inventory" data-stock>{% if current_variant.available %}{{ 'products.product.in_stock' | t }}{% else %}{{ 'products.product.out_of_stock' | t }}{% endif %}</p>
77 77 <p class="product__sku">{{ 'products.product.sku' | t }}: <span data-sku>{{ current_variant.sku }}</span></p>

Shopware: receta live-region-cart-variant

Mostrar el antes y el después (1)
makes the variant price and the cart totals polite live regions; the updates are announced Datos de ejemplo

Resources/views/storefront/page/checkout/cart/index.html.twig Cambios en Resources/views/storefront/page/checkout/cart/index.html.twig (líneas añadidas: 2; eliminadas: 2)

Cambios en Resources/views/storefront/page/checkout/cart/index.html.twig (líneas añadidas: 2; eliminadas: 2)
Línea anterior Línea nueva Cambio Código
59 59 <span class="line-item-unit-price-label">{{ 'checkout.cartHeaderUnitPrice'|trans }}:</span>
60 60 <span class="line-item-unit-price-value">{{ item.unitPrice|currency }}*</span>
61 61 </div>
62 Eliminada <div class="line-item-total-price">
62 Añadida <div class="line-item-total-price" aria-live="polite" aria-atomic="true">
63 63 <span class="line-item-total-price-label">{{ 'checkout.cartHeaderTotalPrice'|trans }}:</span>
64 64 <span class="line-item-total-price-value" data-line-total>{{ item.totalPrice|currency }}*</span>
65 65 </div>
88 88 <div class="col-lg-4 checkout-aside">
89 89 <div class="checkout-aside-summary">
90 90 <h2 class="checkout-aside-summary-header">{{ 'checkout.summaryHeader'|trans }}</h2>
91 Eliminada <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}">
91 Añadida <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}" aria-live="polite" aria-atomic="true">
92 92 <dl class="checkout-aside-summary-list">
93 93 <dt class="checkout-aside-summary-label">{{ 'checkout.summaryPositionPrice'|trans }}</dt>
94 94 <dd class="checkout-aside-summary-value" data-cart-subtotal>{{ cart.subtotal|currency }}*</dd>

Resources/views/storefront/page/product-detail/buy-widget.html.twig Cambios en Resources/views/storefront/page/product-detail/buy-widget.html.twig (líneas añadidas: 1; eliminadas: 1)

Cambios en Resources/views/storefront/page/product-detail/buy-widget.html.twig (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
17 17 {% endblock %}
18 18
19 19 {% block buy_widget_price %}
20 Eliminada <div class="product-detail-price-container">
20 Añadida <div class="product-detail-price-container" aria-live="polite" aria-atomic="true">
21 21 <meta itemprop="priceCurrency" content="{{ shop.currency.isoCode }}">
22 22 <meta itemprop="price" content="{{ product.price }}">
23 23 <p class="product-detail-price{% if product.listPrice %} with-list-price{% endif %}" data-price-display>{{ product.price|currency }}*</p>

WooCommerce: receta live-region-cart-variant

Mostrar el antes y el después (2)
adds a status region next to the header cart fragment and wires the theme script Datos de ejemplo

assets/js/theme.js Cambios en assets/js/theme.js (líneas añadidas: 15; eliminadas: 0)

Cambios en assets/js/theme.js (líneas añadidas: 15; eliminadas: 0)
Línea anterior Línea nueva Cambio Código
255 255 initCheckout();
256 256 initShowPassword();
257 257 })();
258 Añadida
259 Añadida /* validiscan:live-region-cart-variant */
260 Añadida // Announces cart changes in the status region next to the header cart (WCAG 4.1.3).
261 Añadida (function () {
262 Añadida const region = document.querySelector('[data-kb-cart-status]');
263 Añadida if (!region || !window.jQuery) return;
264 Añadida window.jQuery(document.body).on('added_to_cart removed_from_cart', (event) => {
265 Añadida const message = region.getAttribute(event.type === 'added_to_cart' ? 'data-added' : 'data-removed');
266 Añadida region.textContent = '';
267 Añadida window.setTimeout(() => {
268 Añadida region.textContent = message || '';
269 Añadida }, 100);
270 Añadida });
271 Añadida })();
272 Añadida /* /validiscan:live-region-cart-variant */

header.php Cambios en header.php (líneas añadidas: 1; eliminadas: 0)

Cambios en header.php (líneas añadidas: 1; eliminadas: 0)
Línea anterior Línea nueva Cambio Código
67 67 <?php kb_icon( 'bag' ); ?><span class="kb-cart-amount"><?php echo wp_kses_post( WC()->cart->get_cart_subtotal() ); ?></span>
68 68 <span class="count"><?php echo esc_html( sprintf( _n( '%d item', '%d items', WC()->cart->get_cart_contents_count(), 'storefront' ), WC()->cart->get_cart_contents_count() ) ); ?></span>
69 69 </a>
70 Añadida <span class="screen-reader-text" role="status" data-kb-cart-status data-added="<?php esc_attr_e( 'Product added to your cart.', 'kestrel-birch' ); ?>" data-removed="<?php esc_attr_e( 'Product removed from your cart.', 'kestrel-birch' ); ?>"></span>
70 71 </li>
71 72 </ul>
72 73 </div>
makes the variation price and stock of the fixture theme a polite live region Datos de ejemplo

woocommerce/single-product/add-to-cart/variable.php Cambios en woocommerce/single-product/add-to-cart/variable.php (líneas añadidas: 1; eliminadas: 1)

Cambios en woocommerce/single-product/add-to-cart/variable.php (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
65 65 </table>
66 66
67 67 <div class="single_variation_wrap">
68 Eliminada <div class="woocommerce-variation single_variation">
68 Añadida <div class="woocommerce-variation single_variation" aria-live="polite" aria-atomic="true">
69 69 <div class="woocommerce-variation-description"></div>
70 70 <div class="woocommerce-variation-price"><span class="price"><?php echo wp_kses_post( wc_price( $kb_selected['display_price'] ) ); ?></span></div>
71 71 <div class="woocommerce-variation-availability"><?php echo wp_kses_post( $kb_selected['availability_html'] ); ?></div>

Magento: receta live-region-cart-variant

Mostrar el antes y el después (1)
makes the price box and the stock message live regions and validates Datos de ejemplo

app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/price/final_price.phtml Cambios en app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/price/final_price.phtml (líneas añadidas: 1; eliminadas: 1)

Cambios en app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/price/final_price.phtml (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
13 13 // Fixed variant: the product-page box carries aria-live="polite" aria-atomic="true" and the stock
14 14 // message (product/view/type/default.phtml) is role="status".
15 15 ?>
16 Eliminada <div class="price-box price-final_price" data-role="priceBox" data-product-id="<?= (int) $product->getId() ?>" data-price-box="product-id-<?= (int) $product->getId() ?>"<?php if ($isProductPage): ?> data-base-price="<?= (float) $amount ?>"<?php endif; ?>>
16 Añadida <div class="price-box price-final_price" data-role="priceBox" data-product-id="<?= (int) $product->getId() ?>" data-price-box="product-id-<?= (int) $product->getId() ?>"<?php if ($isProductPage): ?> data-base-price="<?= (float) $amount ?>"<?php endif; ?> aria-live="polite" aria-atomic="true">
17 17 <span class="normal-price"><span class="price-container price-final_price tax weee"><span id="product-price-<?= (int) $product->getId() ?>" data-price-amount="<?= (float) $amount ?>" data-price-type="finalPrice" class="price-wrapper"><span class="price"><?= $escaper->escapeHtml($block->renderAmount($block->getPriceType('final_price')->getAmount(), ['display_label' => false])) ?></span></span></span></span>
18 18 </div>

app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/view/type/default.phtml Cambios en app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/view/type/default.phtml (líneas añadidas: 1; eliminadas: 1)

Cambios en app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/view/type/default.phtml (líneas añadidas: 1; eliminadas: 1)
Línea anterior Línea nueva Cambio Código
11 11 ?>
12 12 <div class="product-info-stock-sku">
13 13 <?php if ($block->displayProductStockStatus()): ?>
14 Eliminada <div class="stock available" title="<?= $escaper->escapeHtmlAttr(__('Availability')) ?>"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div>
14 Añadida <div class="stock available" title="<?= $escaper->escapeHtmlAttr(__('Availability')) ?>" role="status"><span><?= $escaper->escapeHtml(__('In stock')) ?></span></div>
15 15 <?php endif; ?>
16 16 <div class="product attribute sku"><strong class="type"><?= $escaper->escapeHtml(__('SKU')) ?></strong> <div class="value" itemprop="sku"><?= $escaper->escapeHtml($product->getSku()) ?></div></div>
17 17 </div>

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 gratuita

O 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

Escribir a Validiscan

Para una revisión gratuita, indique la dirección web de su tienda y su plataforma.

Validiscan utiliza lo que usted envía para responderle. Política de privacidad