Los elementos select deben tener un nombre accesible
Los menús desplegables de talla, color, cantidad, país u ordenación necesitan un nombre. Sin él, quienes usan un lector de pantalla solo oyen «cuadro combinado» y no saben qué están eligiendo, lo que lleva a pedidos equivocados o a procesos de pago bloqueados.
- ID de la regla
- select-name
- 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 select tienen un nombre accesible
La regla prueba todos los <select> a los que pueden llegar los productos de apoyo; uno oculto con display: none se omite. Un select supera la regla con cualquiera de estas opciones:
- un
<label>envolvente con texto propio - un
<label>con texto cuyoforcoincida con eliddel select - un
aria-labelcon texto, unaria-labelledbyque remita a un texto o untitlecon texto role="none"orole="presentation", pero solo en un select que no pueda recibir el foco, como uno desactivado
El texto de las opciones nunca cuenta: si el único texto de un <label> o de un aria-labelledby procede de las opciones del propio select, el select falla.
Un <label for> oculto para los productos de apoyo hace que el select no supere la regla, a menos que otra fuente siga dándole nombre. Se acepta una etiqueta oculta visualmente que los lectores de pantalla pueden leer.
Cuando ninguna fuente da nombre al select y una de estas comprobaciones no puede completarse, el resultado pasa a la lista de axe-core de resultados pendientes de revisión, que el escáner de Validiscan actualmente no convierte en hallazgos. La regla no juzga si el nombre le indica a un cliente qué está eligiendo.
Cómo corregirlo
Asocie un <label> a cada <select> mediante atributos for e id coincidentes, o dele aria-label o aria-labelledby, por ejemplo <label for="size">Talla</label>. Tome el texto de la etiqueta de los nombres de opción traducidos de la tienda.
Criterios de conformidad de las WCAG
- 4.1.2 Name, Role, Value, nivel A
Apartados de la norma EN 301 549
| Criterio de las WCAG | Versión 3.2.1 | Versión 4.1.1 |
|---|---|---|
| 4.1.2 | 9.4.1.2 | 9.4.1.2 |
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 label-association
Mostrar el antes y el después (1)
sections/main-collection.liquid Cambios en sections/main-collection.liquid (líneas añadidas: 2; eliminadas: 2)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 64 | 64 | <div class="collection__products"> | |
| 65 | 65 | <div class="collection__toolbar"> | |
| 66 | 66 | <div class="collection__sort"> | |
| 67 | Eliminada | <label class="collection__sort-label">Sort by</label> | |
| 68 | Eliminada | <select name="sort_by" class="select__select" form="FacetFiltersForm"> | |
| 67 | Añadida | <label class="collection__sort-label" for="SortBy-{{ section.id }}">Sort by</label> | |
| 68 | Añadida | <select name="sort_by" class="select__select" form="FacetFiltersForm" id="SortBy-{{ section.id }}"> | |
| 69 | 69 | <option value="manual" selected>Featured</option> | |
| 70 | 70 | <option value="best-selling">Best selling</option> | |
| 71 | 71 | <option value="price-ascending">Price, low to high</option> |
sections/main-product.liquid Cambios en sections/main-product.liquid (líneas añadidas: 2; eliminadas: 2)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 94 | 94 | </div> | |
| 95 | 95 | {%- else %} | |
| 96 | 96 | <div class="product-form__input product-form__input--dropdown"> | |
| 97 | Eliminada | <p class="form__label">{{ option.name | escape }}</p> | |
| 97 | Añadida | <p class="form__label" id="Options-{{ section.id }}-{{ forloop.index0 }}-label">{{ option.name | escape }}</p> | |
| 98 | 98 | <div class="select"> | |
| 99 | Eliminada | <select class="select__select" name="options[{{ option.name | escape }}]" data-option-index="{{ option_index }}"> | |
| 99 | Añadida | <select class="select__select" name="options[{{ option.name | escape }}]" data-option-index="{{ option_index }}" aria-labelledby="Options-{{ section.id }}-{{ forloop.index0 }}-label"> | |
| 100 | 100 | {%- for value in option.values %} | |
| 101 | 101 | <option value="{{ value | escape }}"{% if value == option.selected_value %} selected{% endif %}>{{ value | escape }}</option> | |
| 102 | 102 | {%- endfor %} |
Shopware: receta label-association
Mostrar el antes y el después (1)
Resources/views/storefront/page/product-detail/configurator.html.twig Cambios en Resources/views/storefront/page/product-detail/configurator.html.twig (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 13 | 13 | {% for group in product.configurator %} | |
| 14 | 14 | {% if group.display == 'select' %} | |
| 15 | 15 | <div class="product-detail-configurator-group"> | |
| 16 | Eliminada | <div class="product-detail-configurator-group-title">{{ group.name }}</div> | |
| 16 | Añadida | <label class="product-detail-configurator-group-title" for="configurator-{{ group.id }}">{{ group.name }}</label> | |
| 17 | 17 | <select class="form-select product-detail-configurator-select-input" | |
| 18 | 18 | id="configurator-{{ group.id }}" | |
| 19 | 19 | name="{{ group.id }}"{% if group.price|default(false) %} |
WooCommerce: receta label-association
Mostrar el antes y el después (1)
woocommerce/cart/cart.php Cambios en woocommerce/cart/cart.php (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 62 | 62 | <tr> | |
| 63 | 63 | <td colspan="6" class="actions"> | |
| 64 | 64 | <div class="coupon"> | |
| 65 | Eliminada | <label class="screen-reader-text"><?php esc_html_e( 'Coupon:', 'woocommerce' ); ?></label> | |
| 65 | Añadida | <label class="screen-reader-text" for="coupon_code"><?php esc_html_e( 'Coupon:', 'woocommerce' ); ?></label> | |
| 66 | 66 | <input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>"> | |
| 67 | 67 | <button type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply coupon', 'woocommerce' ); ?>"><?php esc_html_e( 'Apply coupon', 'woocommerce' ); ?></button> | |
| 68 | 68 | </div> |
Magento: receta label-association
Mostrar el antes y el después (1)
app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/list/toolbar.phtml Cambios en app/design/frontend/Validiscan/fixture/Magento_Catalog/templates/product/list/toolbar.phtml (líneas añadidas: 1; eliminadas: 1)
| Línea anterior | Línea nueva | Cambio | Código |
|---|---|---|---|
| 12 | 12 | <div class="toolbar-sorter sorter"> | |
| 13 | 13 | <!-- VALIDISCAN-SEEDED select-name + vs-forms-ecommerce: the "Sort By" text is a span, not a label for the select --> | |
| 14 | 14 | <?php /* Fixed variant: <label class="sorter-label" for="sorter">Sort By</label>. */ ?> | |
| 15 | Eliminada | <span class="sorter-label"><?= $escaper->escapeHtml(__('Sort By')) ?></span> | |
| 15 | Añadida | <label class="sorter-label" for="sorter"><?= $escaper->escapeHtml(__('Sort By')) ?></label> | |
| 16 | 16 | <select id="sorter" data-role="sorter" class="sorter-options"> | |
| 17 | 17 | <?php foreach ($block->getAvailableOrders() as $key => $order): ?> | |
| 18 | 18 | <option value="<?= $escaper->escapeHtmlAttr($key) ?>"<?= $block->isOrderCurrent($key) ? ' selected' : '' ?>><?= $escaper->escapeHtml(__($order)) ?></option> |
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