Correcciones de accesibilidad en el código de su tema de Shopware

Validiscan corrige los fallos de accesibilidad web en las plantillas Twig, el SCSS, el JavaScript del storefront y los archivos de snippets de su plugin de tema de Shopware 6. Nada llega a su tienda en producción hasta que su desarrollador fusione o aplique los cambios y despliegue el tema.

Última actualización:

¿Qué cambia Validiscan en un tema de Shopware?

Validiscan solo cambia archivos de las carpetas en las que un tema de storefront de Shopware 6 guarda su código fuente, Resources/views, Resources/app/storefront/src y Resources/snippet, y su theme.json. Un cambio en cualquier otro archivo no supera la validación y nunca se entrega, y ninguna corrección instala un plugin ni añade un script de overlay.

Los ajustes que usted gestiona en la Administración de Shopware, como los metatítulos o los colores del tema guardados allí, no son archivos del tema, así que las correcciones no los cambian. Un proyecto cubre un dominio y un tema; los elementos incrustados de terceros, como los widgets de pago o de chat, no están incluidos en él.

Detalles: las carpetas, cómo es un cambio y las dos recetas que modifican el script del storefront

Validiscan solo cambia archivos de las carpetas en las que un tema de storefront de Shopware 6 guarda su código fuente: las plantillas Twig de Resources/views, el código fuente de SCSS y de JavaScript de Resources/app/storefront/src, los archivos de snippets con los textos del storefront de Resources/snippet y el theme.json del tema. Un cambio en cualquier otro archivo, como los archivos compilados de Resources/app/storefront/dist o el código PHP y la configuración del plugin, no supera la validación. Un cambio que no supera la validación no se puede aprobar, de modo que nunca se entrega.

Cada cambio es una edición corriente de un archivo del tema que su desarrollador puede leer línea por línea. Los cambios pequeños, como un atributo añadido o un valor de color corregido, se hacen donde ya está el código. Las reglas de estilo nuevas y los bloques de script nuevos van entre comentarios que nombran la receta, por ejemplo /* validiscan:focus-visible-styles */, y por eso son fáciles de encontrar. Dos recetas modifican el propio script de storefront del tema: una conecta un botón de pausa con el carrusel de imágenes y la otra impide que el avance automático de un script se inicie cuando el visitante ha pedido menos movimiento. Ninguna corrección instala un plugin ni añade un script que corrija sus páginas una vez cargadas, como hace un overlay.

¿Cómo accede Validiscan a su tema de Shopware?

Mediante un repositorio de GitHub o un ZIP del plugin del tema subido en el portal del cliente, porque Validiscan prepara cada corrección en una copia del código fuente de su plugin de tema de Shopware. La vía depende de dónde esté el código: un repositorio recibe un pull request y un ZIP, un paquete de parches.

Las dos vías de acceso a un plugin de tema de Shopware
Vía de acceso Lo que usted da Los cambios vuelven como
A través de su repositorio de GitHub Acceso al repositorio que contiene el plugin del tema, mediante la GitHub App de Validiscan o un token de acceso Un pull request
Desde un ZIP del plugin del tema La carpeta del plugin del tema en custom/plugins, como un único archivo ZIP de hasta 100 MB, que sube en el portal del cliente un administrador de su organización Un paquete de parches

A través de su repositorio de GitHub

Si el código del plugin de su tema está en un repositorio de GitHub, usted da a Validiscan acceso a ese repositorio: instala en él la GitHub App de Validiscan o entrega a Validiscan un token de acceso para él. La configuración de Validiscan especifica los permisos de la App como acceso de lectura y escritura al contenido y a los pull requests del repositorio, y acceso de lectura a sus metadatos, y nada más. Un token necesita los mismos permisos sobre ese único repositorio.

El plugin del tema puede estar en la raíz del repositorio o más adentro, como en custom/plugins/YourTheme/src/Resources: las recetas encuentran los archivos del tema por las convenciones de carpetas de Shopware. Los cambios llegan entonces como un pull request.

Desde un ZIP del plugin del tema

Si el código no está en un repositorio de GitHub, su desarrollador crea un ZIP del plugin del tema. La instrucción del portal del cliente para Shopware dice: «Pida a su desarrollador un ZIP del plugin de su tema: su carpeta en custom/plugins, tal como está desplegada en su tienda». Después, un administrador de su organización sube el archivo en el portal del cliente, como un único archivo ZIP de hasta 100 MB. Validiscan prepara los cambios en esa copia de su tema y se los devuelve como un paquete de parches.

Sin ninguna de las dos vías, Validiscan no tiene código que cambiar y no se prepara ninguna corrección.

¿Cómo llegan los cambios a su tienda?

Los cambios llegan como un pull request o como un paquete de parches, y su desarrollador decide cuándo salen a producción. Validiscan solo entrega cambios que ha revisado y aprobado y nunca modifica el tema ni el código de su tienda Shopware en producción; la entrega como copia sin publicar del tema existe solo en Shopify.

De la entrega a su tienda en producción, en cinco pasos
  1. Validiscan entrega los cambios que ha revisado y aprobado: un pull request en una rama nueva o un paquete de parches para su desarrollador.
  2. Su desarrollador revisa los cambios. En un pull request, la lista de comprobación pide un entorno de vista previa o de preproducción, no la tienda en producción, y una comprobación de las páginas afectadas, también solo con el teclado.
  3. Su desarrollador fusiona el pull request o aplica el paquete de parches.
  4. Su desarrollador compila el tema: los cambios en el SCSS surten efecto cuando el tema se vuelve a compilar, y los cambios en el JavaScript, cuando el storefront se vuelve a generar.
  5. Su desarrollador despliega el tema. Cuando los cambios están en producción, un escaneo de verificación vuelve a comprobar las páginas afectadas.

Un pull request

Con acceso a su repositorio, Validiscan crea una rama nueva llamada validiscan/a11y-<date>-<id>, registra cada corrección en un commit propio y abre un pull request con una tabla resumen, una explicación de cada cambio y una lista de comprobación para la revisión. No se sube nada a su rama predeterminada. La lista de comprobación pide a su desarrollador que despliegue la rama en un entorno de vista previa o de preproducción, no en la tienda en producción, y que compruebe allí las páginas afectadas, también solo con el teclado, antes de fusionarla.

Un paquete de parches

A partir del ZIP subido, Validiscan prepara un archivo zip y se lo envía a su desarrollador. Contiene PATCH.md, con cada cambio y cómo aplicarlo; todos los cambios en un único archivo de parche que se aplica con git apply; un archivo de parche por corrección; los archivos modificados completos y un README.md. Su desarrollador aplica los cambios, los prueba y despliega el tema. Qué contienen un pull request y un paquete de parches

Compilar el tema

Una tienda Shopware no sirve tal cual el código fuente de SCSS y de JavaScript del tema: antes se compila. Los cambios en el SCSS surten efecto cuando el tema se vuelve a compilar, por ejemplo con bin/console theme:compile, como describe la documentación para desarrolladores de Shopware sobre los estilos de un tema. Los cambios en el JavaScript del storefront surten efecto cuando el storefront se vuelve a generar, como se describe en la documentación de Shopware sobre el JavaScript del storefront. Su desarrollador ejecuta estos pasos al desplegar el tema modificado. Cómo llegan las correcciones de accesibilidad a un tema de Shopware y qué puede deshacer una actualización del tema

¿Qué cambian las recetas en un tema de Shopware?

Cada una de las 20 recetas de Validiscan para temas de Shopware es un cambio determinista para un tipo de fallo: el mismo código recibe siempre el mismo cambio. Una receta hace un cambio solo donde el código del tema muestra cuál es el cambio correcto; donde no lo muestra, no cambia nada.

Estructura de la página y navegación

  • Idioma de la página en el layout base html-lang El elemento <html> del layout base recibe un atributo lang válido donde falta o donde el tema imprime un código de configuración regional como en_GB.

    El elemento <html> del layout base del storefront recibe un atributo lang válido, para que los lectores de pantalla pronuncien la página en el idioma correcto. Donde falta el atributo, la receta añade el código del idioma activo de la tienda, por ejemplo de-DE, el valor que imprime la propia plantilla base de Shopware. Cuando el tema imprime un código de configuración regional como en_GB, que no es una etiqueta de idioma válida, la plantilla lo convierte ahora en en-GB. Una página cuyo contenido está escrito en un idioma distinto del de la tienda, como un texto legal en alemán en una tienda en inglés, queda en sus manos: el idioma de ese contenido se define en la Administración o se marca en el propio texto.

    Reglas que cubre
    • html-has-lang AUTO se comprueba mediante vs-lang
    • html-lang-valid AUTO se comprueba mediante vs-lang
    • vs-lang AUTO
    Tipos de archivo que modifica
    .html.twig
    normalises the locale code of the base layout; German content pages are left to the merchant Datos de ejemplo

    Resources/views/storefront/base.html.twig Cambios en Resources/views/storefront/base.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/base.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    9 9 {% block base_doctype %}<!DOCTYPE html>{% endblock %}
    10 10
    11 11 {% block base_html %}
    12 Eliminada <html lang="{{ page.lang }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    12 Añadida <html lang="{{ page.lang|replace({'_': '-'}) }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    13 13 {% endblock %}
    14 14
    15 15 {% block base_head %}
  • Un título en cada página document-title-template Cuando el layout escribe en el <title> solo el metatítulo, el nombre de la tienda pasa a ser su valor alternativo, de modo que ninguna página queda con un título vacío.

    Cuando el layout del tema escribe en el <title> solo el metatítulo de la página, las categorías y las páginas CMS sin metatítulo reciben un título vacío. La receta conserva el título propio del tema y le da un valor alternativo: el nombre de la tienda, tomado de donde el tema ya lo imprime o, si no lo imprime, del nombre de la tienda en los ajustes básicos de Shopware. Así ninguna página queda con un título vacío. Un título que es solo el nombre de la tienda sigue sin decir qué es la página, de modo que esas páginas necesitan un metatítulo en la Administración. Los títulos que ya tienen un valor alternativo o un texto fijo se dejan como están.

    Reglas que cubre
    • document-title AUTO
    Tipos de archivo que modifica
    .html.twig
    gives the layout title a shop-name fallback and the empty CMS title is gone Datos de ejemplo

    Resources/views/storefront/layout/meta.html.twig Cambios en Resources/views/storefront/layout/meta.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/meta.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    36 36 {% endblock %}
    37 37
    38 38 {% block layout_head_title %}
    39 Eliminada <title>{{ page.metaTitle }}</title>
    39 Añadida <title>{{ page.metaTitle|default(shop.name) }}</title>
    40 40 {% endblock %}
    41 41
    42 42 {% block layout_head_stylesheet %}
  • Encabezados sin niveles omitidos heading-levels Un encabezado que se salta un nivel sube, junto con los encabezados de la plantilla que están en ese nivel o en uno más profundo, y mantiene su aspecto.

    Un encabezado que se salta un nivel, como un h3 justo después de un h1, sube al nivel siguiente, y los demás encabezados de la misma plantilla que están en ese nivel o en uno más profundo suben con él, de modo que el esquema propio de la plantilla se mantiene. Cuando la plantilla define la etiqueta del encabezado en una variable, se cambia la variable. Las clases se conservan y, cuando ninguna clase fija el tamaño, una clase de encabezado de Bootstrap mantiene el aspecto anterior. En una página sin encabezado principal, el elemento que ya la titula en su propia plantilla pasa a ser el h1, con su aspecto conservado. Si una página no tiene un elemento así, la receta no añade ningún encabezado, porque un encabezado nuevo requiere redactar su texto.

    Reglas que cubre
    • heading-order AUTO se comprueba mediante vs-headings-landmarks
    • page-has-heading-one AUTO se comprueba mediante vs-headings-landmarks
    • vs-headings-landmarks AUTO
    Tipos de archivo que modifica
    .html.twig
    moves a static grid title from h3 to h2; the skipped level is gone Datos de ejemplo

    Resources/views/storefront/block/cms-block-product-grid.html.twig Cambios en Resources/views/storefront/block/cms-block-product-grid.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/block/cms-block-product-grid.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    5 5 {% block block_product_grid %}
    6 6 <div class="cms-block cms-block-product-grid">
    7 7 <div class="cms-block-container">
    8 Eliminada <h3 class="cms-block-title">{{ block.title }}</h3>
    8 Añadida <h2 class="cms-block-title">{{ block.title }}</h2>
    9 9 <div class="row cms-listing-row">
    10 10 {% for product in block.products %}
    11 11 {% sw_include '@Storefront/storefront/component/product/card/box-standard.html.twig' with { product: product } %}

Uso del teclado y foco

  • Un contorno de foco visible focus-visible-styles Un contorno para el foco del teclado, en el color de marca del tema o en el tono más próximo del mismo matiz que tenga un contraste de al menos 3:1 con el fondo de la página.

    La hoja de estilos principal recibe un contorno para el foco del teclado: de 2 píxeles, continuo, a 2 píxeles del elemento, en el color de marca del tema. Cuando ese color es demasiado claro, se usa el tono más próximo del mismo matiz que tenga un contraste de al menos 3:1 con el fondo de la página. Las reglas del tema que quitan el contorno al recibir el foco se aplican ahora solo al foco por ratón: un clic mantiene el aspecto del tema y el foco del teclado muestra el contorno. Las reglas que muestran el foco de otra manera se conservan. En las zonas oscuras, como una barra superior, el contorno puede necesitar un color más claro, que su desarrollador comprueba en la vista previa.

    Reglas que cubre
    • vs-focus-visible AUTO
    Tipos de archivo que modifica
    .scss
    adds a brand-coloured :focus-visible ring and scopes the outline resets Datos de ejemplo

    Resources/app/storefront/src/scss/_variant-broken.scss Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 5; eliminadas: 5)

    Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 5; eliminadas: 5)
    Línea anterior Línea nueva Cambio Código
    21 21 }
    22 22
    23 23 /* "Remove the ugly blue ring" (vs-focus-visible). */
    24 Eliminada .header-actions-btn:focus,
    25 Eliminada .header-search-btn:focus,
    26 Eliminada .main-navigation-link:focus,
    27 Eliminada .product-name:focus,
    28 Eliminada .footer-link:focus {
    24 Añadida .header-actions-btn:focus:not(:focus-visible),
    25 Añadida .header-search-btn:focus:not(:focus-visible),
    26 Añadida .main-navigation-link:focus:not(:focus-visible),
    27 Añadida .product-name:focus:not(:focus-visible),
    28 Añadida .footer-link:focus:not(:focus-visible) {
    29 29 outline: 0;
    30 30 box-shadow: none;
    31 31 }

    Resources/app/storefront/src/scss/base.scss Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 8; eliminadas: 0)

    Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 8; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Añadida
    1712 Añadida /* validiscan:focus-visible-styles */
    1713 Añadida /* Visible keyboard focus (WCAG 2.4.7): brand colour sw-color-brand-primary, 8.9:1 on #ffffff. */
    1714 Añadida :focus-visible {
    1715 Añadida outline: 2px solid #6b3f1d;
    1716 Añadida outline-offset: 2px;
    1717 Añadida }
    1718 Añadida /* /validiscan:focus-visible-styles */
  • Un orden de tabulación que sigue el orden de la página remove-positive-tabindex Un tabindex mayor que 0 se quita o se pone a 0, de modo que la tecla Tab recorre la página en su orden, salvo que lo escriba el código de la plantilla.

    Un tabindex mayor que 0 lleva un elemento al principio del orden del teclado. La receta lo quita de los enlaces, los botones, los campos de formulario y otros elementos que reciben el foco de todos modos, y lo pone a 0 en los demás elementos, de modo que siguen siendo alcanzables y la tecla Tab recorre la página en su orden. Un tabindex que escribe el código de la plantilla queda en manos de una persona. Un escaneo informa dos veces de ese orden forzado: mediante la regla tabindex de axe-core, cuyos hallazgos reciben este cambio, y mediante la comprobación vs-focus-order de Validiscan, cuyo nivel es MANUAL, por lo que sus hallazgos vienen con orientación.

    Reglas que cubre
    • tabindex AUTO
    • vs-focus-order MANUAL
    Tipos de archivo que modifica
    .html.twig
    removes tabindex="1" from the buy button; vs-focus-order and axe tabindex stop firing Datos de ejemplo

    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
    82 82 </div>
    83 83 <div class="buy-widget-submit">
    84 84 <button class="btn btn-primary btn-buy"
    85 Eliminada title="{{ 'detail.addProduct'|trans }}" tabindex="1">
    85 Añadida title="{{ 'detail.addProduct'|trans }}">
    86 86 {{ 'detail.addProduct'|trans }}
    87 87 </button>
    88 88 </div>

Zoom y zonas táctiles

  • Zoom permitido en los móviles meta-viewport-zoom La etiqueta viewport pierde user-scalable=no y cualquier maximum-scale inferior a 2, para que los visitantes puedan ampliar el texto al menos hasta un 200 % en el móvil.

    La receta quita user-scalable=no y cualquier maximum-scale inferior a 2 de la etiqueta viewport de las plantillas del storefront, normalmente en layout/meta.html.twig, para que los visitantes puedan ampliar el texto al menos hasta un 200 % en el móvil. Para la regla de buenas prácticas meta-viewport-large el límite es 5. Los demás ajustes del viewport se mantienen como estaban. Si el bloqueo se puso para impedir que iOS haga zoom en los campos de formulario, un tamaño de fuente de al menos 16 píxeles en los campos lo consigue sin bloquear el zoom.

    Reglas que cubre
    • meta-viewport AUTO
    • meta-viewport-large AUTO
    Tipos de archivo que modifica
    .html.twig
    removes the zoom lock from the fixture theme and the rule stops firing Datos de ejemplo

    Resources/views/storefront/layout/meta.html.twig Cambios en Resources/views/storefront/layout/meta.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/meta.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    6 6
    7 7 {% block layout_head_meta_tags_viewport %}
    8 8 {# Added by the merchant's agency to stop "zoom jumps" on iOS. #}
    9 Eliminada <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    9 Añadida <meta name="viewport" content="width=device-width, initial-scale=1">
    10 10 {% endblock %}
    11 11
    12 12 {% block layout_head_meta_tags_general %}
  • Zonas táctiles más grandes target-size-padding Los botones y enlaces que el escaneo encontró con un tamaño inferior a 24 × 24 píxeles reciben ese tamaño mínimo, cuando la receta puede identificarlos por su clase.

    Los botones y enlaces que el escaneo encontró con un tamaño inferior a 24 × 24 píxeles, como los iconos de la cabecera, los botones de cantidad o las muestras de color, reciben un tamaño mínimo de 24 × 24 píxeles. El mínimo se añade a la propia regla de estilo del tema que hizo pequeño el control, junto a la declaración que lo redujo, de modo que el orden de los estilos del tema no cambia y los iconos del interior mantienen su tamaño. Un control que es pequeño sin una regla así recibe un tamaño mínimo para su clase en la hoja de estilos principal. Un control que la receta no puede identificar por su clase queda en manos de una persona.

    Reglas que cubre
    • vs-target-size AUTO
    Tipos de archivo que modifica
    .scss
    gives the header icons, steppers and swatches a 24px minimum; no page reports small targets Datos de ejemplo

    Resources/app/storefront/src/scss/_variant-broken.scss Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 6; eliminadas: 2)

    Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 6; eliminadas: 2)
    Línea anterior Línea nueva Cambio Código
    10 10
    11 11 .header-actions-btn {
    12 12 width: 18px;
    13 Añadida min-width: 24px;
    13 14 height: 18px;
    14 Eliminada min-height: 0;
    15 Añadida min-height: 24px;
    15 16 padding: 0;
    16 17 }
    17 18
    96 97 /* 18 px colour swatches, 20 px quantity and remove buttons (vs-target-size). */
    97 98 .product-detail-configurator-option-input {
    98 99 width: 18px;
    100 Añadida min-width: 24px;
    99 101 height: 18px;
    102 Añadida min-height: 24px;
    100 103 margin-right: 4px;
    101 104 }
    102 105
    103 106 .quantity-selector-group .btn,
    104 107 .line-item-remove-button {
    105 108 width: 20px;
    109 Añadida min-width: 24px;
    106 110 height: 20px;
    107 Eliminada min-height: 0;
    111 Añadida min-height: 24px;
    108 112 padding: 0;
    109 113 }
    110 114

Movimiento, carruseles y sonido

  • Menos movimiento cuando el visitante lo pide reduced-motion-guard Cuando el dispositivo del visitante pide reducir el movimiento, las animaciones y las transiciones que definen las hojas de estilos terminan de inmediato y se desactiva el desplazamiento suave.

    Cuando el dispositivo del visitante está configurado para reducir el movimiento, un bloque de la hoja de estilos principal hace que las animaciones y las transiciones terminen de inmediato y desactiva el desplazamiento suave. El bloque se aplica a todos los elementos de la página, por lo que también cubre las animaciones que definen otras hojas de estilos, incluidas las de los plugins. Si el script de storefront del tema inicia un temporizador, como el avance automático de un carrusel, únicamente cuando hay una opción de avance automático activada, esa condición comprueba ahora también el ajuste del visitante. El resto del movimiento que inicia un script se enumera para quien revisa, y no se adivina.

    Reglas que cubre
    • vs-motion AUTO
    Tipos de archivo que modifica
    .js.scss
    guards the pulsing badge and the slider autoplay; the animation stops under reduce Datos de ejemplo

    Resources/app/storefront/src/main.js Cambios en Resources/app/storefront/src/main.js (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/app/storefront/src/main.js (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    139 139 });
    140 140 });
    141 141
    142 Eliminada if (options.autoplay === true) {
    142 Añadida if (options.autoplay === true && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    143 143 window.setInterval(
    144 144 function () {
    145 145 show(current + 1);

    Resources/app/storefront/src/scss/base.scss Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 14; eliminadas: 0)

    Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 14; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Añadida
    1712 Añadida /* validiscan:reduced-motion-guard */
    1713 Añadida /* Honour the visitor's reduced-motion setting: animations and transitions end at once, no smooth scrolling. */
    1714 Añadida @media (prefers-reduced-motion: reduce) {
    1715 Añadida *,
    1716 Añadida *::before,
    1717 Añadida *::after {
    1718 Añadida animation-duration: 0.01ms !important;
    1719 Añadida animation-iteration-count: 1 !important;
    1720 Añadida transition-duration: 0.01ms !important;
    1721 Añadida scroll-behavior: auto !important;
    1722 Añadida }
    1723 Añadida }
    1724 Añadida /* /validiscan:reduced-motion-guard */
  • Un botón de pausa para los carruseles en movimiento slider-pause-control El carrusel base de Shopware, el que hay detrás del carrusel de imágenes del CMS, recibe un botón visible y traducido para ponerlo en pausa y reanudarlo.

    El carrusel base de Shopware, el que hay detrás del carrusel de imágenes del CMS, recibe un botón visible y traducido para ponerlo en pausa y reanudarlo, junto a sus flechas de anterior y siguiente. Un bloque de código del script de storefront del tema conecta el botón con el avance automático del carrusel mediante el plugin de carrusel propio de Shopware. Cuando el tema ejecuta el avance automático con su propio temporizador, ese temporizador recibe una línea que omite un paso mientras el carrusel está en pausa. El botón permanece oculto hasta que el script lo ha conectado, de modo que nunca aparece sin un avance automático que poner en pausa. Para los visitantes cuyo dispositivo pide reducir el movimiento, el carrusel empieza en pausa. Los carruseles creados con otras bibliotecas quedan en manos de una persona.

    Reglas que cubre
    • vs-carousel AUTO
    Tipos de archivo que modifica
    .html.twig.js.json
    adds a wired pause button to the hero slider; the carousel is no longer unstoppable Datos de ejemplo

    Resources/app/storefront/src/main.js Cambios en Resources/app/storefront/src/main.js (líneas añadidas: 52; eliminadas: 0)

    Cambios en Resources/app/storefront/src/main.js (líneas añadidas: 52; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    142 142 if (options.autoplay === true) {
    143 143 window.setInterval(
    144 144 function () {
    145 Añadida if (slider.getAttribute('data-autoplay-paused') === 'true') return;
    145 146 show(current + 1);
    146 147 },
    147 148 Number(options.autoplayTimeout) || 5000,
    453 454 initFilterRanges();
    454 455 initToggles();
    455 456 })();
    457 Añadida
    458 Añadida /* validiscan:slider-pause-control */
    459 Añadida /*
    460 Añadida * Pause / play button of auto-advancing sliders (WCAG 2.2.2): wired to the
    461 Añadida * slider's autoplay; sliders start paused when the visitor prefers reduced motion.
    462 Añadida */
    463 Añadida (function sliderPauseControl() {
    464 Añadida 'use strict';
    465 Añadida
    466 Añadida function tinySlider(slider) {
    467 Añadida const manager = window.PluginManager;
    468 Añadida if (!manager || typeof manager.getPluginInstanceFromElement !== 'function') return null;
    469 Añadida const plugin = manager.getPluginInstanceFromElement(slider, 'BaseSlider');
    470 Añadida return plugin && plugin._slider ? plugin._slider : null;
    471 Añadida }
    472 Añadida
    473 Añadida function autoplays(slider) {
    474 Añadida try {
    475 Añadida const options = JSON.parse(slider.getAttribute('data-base-slider-options') || '{}') || {};
    476 Añadida return Boolean(options.slider && options.slider.autoplay);
    477 Añadida } catch {
    478 Añadida return false;
    479 Añadida }
    480 Añadida }
    481 Añadida
    482 Añadida function init() {
    483 Añadida const reduceMotion =
    484 Añadida window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    485 Añadida document.querySelectorAll('[data-slider-pause]').forEach(function (button) {
    486 Añadida const slider = button.closest('[data-base-slider]');
    487 Añadida if (!slider || !autoplays(slider)) return;
    488 Añadida
    489 Añadida function setPaused(paused) {
    490 Añadida slider.setAttribute('data-autoplay-paused', paused ? 'true' : 'false');
    491 Añadida button.textContent = button.getAttribute(paused ? 'data-label-play' : 'data-label-pause');
    492 Añadida const instance = tinySlider(slider);
    493 Añadida if (instance) instance[paused ? 'pause' : 'play']();
    494 Añadida }
    495 Añadida
    496 Añadida button.hidden = false;
    497 Añadida button.addEventListener('click', function () {
    498 Añadida setPaused(slider.getAttribute('data-autoplay-paused') !== 'true');
    499 Añadida });
    500 Añadida if (reduceMotion) setPaused(true);
    501 Añadida });
    502 Añadida }
    503 Añadida
    504 Añadida if (document.readyState === 'complete') init();
    505 Añadida else window.addEventListener('load', init);
    506 Añadida })();
    507 Añadida /* /validiscan:slider-pause-control */

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 6; eliminadas: 0)

    Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 6; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
    194 194 "backToHome": "Zurück zur Startseite",
    195 195 "searchInstead": "Unsere Kaffees durchsuchen"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "slider": {
    199 Añadida "pause": "Diashow anhalten",
    200 Añadida "play": "Diashow abspielen"
    201 Añadida }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 6; eliminadas: 0)

    Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 6; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "The page you are looking for does not exist or has moved.",
    194 194 "backToHome": "Back to the homepage",
    195 195 "searchInstead": "Search our coffees"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "slider": {
    199 Añadida "pause": "Pause slideshow",
    200 Añadida "play": "Play slideshow"
    201 Añadida }
    196 202 }
    197 203 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 1; eliminadas: 0)

    Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 1; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    57 57 <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next>
    58 58 {% sw_icon 'arrow-head-right' %}
    59 59 </button>
    60 Añadida <button type="button" class="btn btn-light base-slider-pause" data-slider-pause="true" data-label-pause="{{ 'a11y.slider.pause'|trans|striptags }}" data-label-play="{{ 'a11y.slider.play'|trans|striptags }}" hidden>{{ 'a11y.slider.pause'|trans|sw_sanitize }}</button>
    60 61 </div>
    61 62 {% endblock %}
    62 63 </div>
  • Sin sonido que empiece por sí solo mute-autoplay Un vídeo que se reproduce por sí solo se silencia y el audio deja de empezar por sí solo, salvo la reproducción automática que establecen el código de una plantilla o un script y los reproductores en marcos; en Shopware la receta no se usa para los hallazgos de vs-media.

    Un vídeo que se reproduce por sí solo recibe muted y playsinline, de modo que sigue reproduciéndose en silencio, como un vídeo de fondo, y en Safari en los móviles se reproduce dentro de la página en lugar de abrir el reproductor a pantalla completa. El audio que empieza por sí solo deja de hacerlo; los visitantes lo inician por su cuenta. La reproducción automática que establecen el código de una plantilla o un script, y los reproductores incrustados en marcos, quedan en manos de una persona. En un escaneo de Validiscan, el sonido que empieza por sí solo lo notifica la comprobación multimedia vs-media, que sustituye a la regla no-autoplay-audio de axe-core. vs-media, que también cubre los subtítulos y las transcripciones, es MANUAL, así que en Shopware un hallazgo así viene con orientación en lugar de con este cambio.

    Reglas que cubre
    • no-autoplay-audio AUTO se comprueba mediante vs-media
    • vs-media MANUAL
    Tipos de archivo que modifica
    .html.twig
    mutes a seeded autoplaying background video; vs-media no longer reports autoplay Datos de ejemplo

    Resources/views/storefront/block/cms-block-video.html.twig Cambios en Resources/views/storefront/block/cms-block-video.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/block/cms-block-video.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    9 9 <h2 class="cms-block-title">{{ block.title }}</h2>
    10 10 <p class="cms-video-text">{{ block.text }}</p>
    11 11 <div class="cms-element-video">
    12 Eliminada <video class="cms-video" autoplay loop preload="none" width="1280" height="720" poster="{{ block.posterUrl }}">
    12 Añadida <video class="cms-video" autoplay loop preload="none" width="1280" height="720" poster="{{ block.posterUrl }}" muted playsinline>
    13 13 <source src="{{ block.videoUrl }}" type="video/mp4">
    14 14 {% if block.captionsUrl %}
    15 15 <track kind="captions" src="{{ block.captionsUrl }}" srclang="en" label="{{ 'general.captionsEnglish'|trans }}" default>

Formularios, carrito y contenido incrustado

  • Etiquetas conectadas a sus campos label-association Un campo cuyo texto de etiqueta ya está en la página, justo antes del campo, se conecta con ese texto.

    Un campo de formulario cuyo texto de etiqueta ya está en la página, justo antes del campo, se conecta con ese texto. Un <label> sin for recibe uno que apunta al campo. Un elemento de texto que el tema marca como título o etiqueta por su clase, como el nombre de un grupo de variantes en una página de producto, pasa a ser el <label> del campo, con sus clases y su propia línea. Los lectores de pantalla leen entonces la etiqueta cuando el campo recibe el foco. Un campo que solo tiene un placeholder queda en manos de una persona, porque redactar una etiqueta es una decisión de contenido.

    Reglas que cubre
    • label AUTO se comprueba mediante vs-forms-ecommerce
    • select-name ASSISTED
    • vs-forms-ecommerce AUTO
    Tipos de archivo que modifica
    .html.twig
    turns the variant group titles into labels of their selects; select-name no longer fires Datos de ejemplo

    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)

    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) %}
  • Campos de cuenta, dirección y contacto que los navegadores pueden rellenar autocomplete-tokens Los campos que piden datos propios del cliente, como los de los formularios de registro, dirección e inicio de sesión, reciben el valor de autocomplete correspondiente a lo que piden, salvo que lo escriba el código de una plantilla.

    Los campos que piden datos propios del cliente, como los de los formularios de registro, dirección, inicio de sesión, contacto y boletín informativo, reciben el valor de autocomplete correspondiente a lo que piden, como correo electrónico, nombre y apellidos, código postal, número de teléfono o contraseña. El valor se deduce del tipo, el nombre, el id, la etiqueta y el placeholder del campo. Las contraseñas reciben new-password en los formularios de registro y current-password en los demás. Los campos de búsqueda, de cupón, de cantidad, de captcha y de mensaje se dejan como están, y un valor que escribe el código de una plantilla queda en manos de una persona. Así, los navegadores pueden rellenar los campos y los productos de apoyo pueden saber para qué sirve cada uno.

    Reglas que cubre
    • autocomplete-valid AUTO se comprueba mediante vs-forms-ecommerce
    • vs-forms-ecommerce AUTO
    Tipos de archivo que modifica
    .html.twig
    adds tokens to the register, address, login, contact and newsletter fields; no autocomplete hits remain Datos de ejemplo

    Resources/views/storefront/component/account/login.html.twig Cambios en Resources/views/storefront/component/account/login.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/component/account/login.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    19 19 name="username"
    20 20 placeholder="{{ 'account.loginMailPlaceholder'|trans }}"
    21 21 required
    22 Eliminada autocomplete="off">
    22 Añadida autocomplete="email">
    23 23 </div>
    24 24
    25 25 <div class="form-group">

    Resources/views/storefront/component/account/register.html.twig Cambios en Resources/views/storefront/component/account/register.html.twig (líneas añadidas: 8; eliminadas: 4)

    Cambios en Resources/views/storefront/component/account/register.html.twig (líneas añadidas: 8; eliminadas: 4)
    Línea anterior Línea nueva Cambio Código
    33 33 id="{{ formId }}FirstName"
    34 34 name="firstName"
    35 35 placeholder="{{ 'account.firstNamePlaceholder'|trans }}"
    36 Eliminada required>
    36 Añadida required
    37 Añadida autocomplete="given-name">
    37 38 </div>
    38 39 <div class="form-group col-sm-6">
    39 40 <input type="text"
    41 42 id="{{ formId }}LastName"
    42 43 name="lastName"
    43 44 placeholder="{{ 'account.lastNamePlaceholder'|trans }}"
    44 Eliminada required>
    45 Añadida required
    46 Añadida autocomplete="family-name">
    45 47 </div>
    46 48 </div>
    47 49 <div class="row g-2">
    52 54 id="{{ formId }}Mail"
    53 55 name="email"
    54 56 placeholder="{{ 'account.emailPlaceholder'|trans }}"
    55 Eliminada required>
    57 Añadida required
    58 Añadida autocomplete="email">
    56 59 </div>
    57 60 <div class="form-group col-sm-6">
    58 61 <label class="form-label" for="{{ formId }}Password">{{ 'account.passwordLabel'|trans }}</label>
    62 65 name="password"
    63 66 minlength="8"
    64 67 aria-describedby="{{ formId }}PasswordHint"
    65 Eliminada required>
    68 Añadida required
    69 Añadida autocomplete="new-password">
    66 70 <small class="form-text" id="{{ formId }}PasswordHint">{{ 'account.passwordHint'|trans }}</small>
    67 71 </div>
    68 72 </div>

    Resources/views/storefront/component/address/address-form.html.twig Cambios en Resources/views/storefront/component/address/address-form.html.twig (líneas añadidas: 8; eliminadas: 4)

    Cambios en Resources/views/storefront/component/address/address-form.html.twig (líneas añadidas: 8; eliminadas: 4)
    Línea anterior Línea nueva Cambio Código
    14 14 id="{{ prefix }}AddressStreet"
    15 15 name="{{ prefix }}[street]"
    16 16 placeholder="{{ 'address.streetPlaceholder'|trans }}"
    17 Eliminada required>
    17 Añadida required
    18 Añadida autocomplete="address-line1">
    18 19 </div>
    19 20 {% endblock %}
    20 21
    26 27 name="{{ prefix }}[zipcode]"
    27 28 placeholder="{{ 'address.zipcodePlaceholder'|trans }}"
    28 29 inputmode="numeric"
    29 Eliminada required>
    30 Añadida required
    31 Añadida autocomplete="postal-code">
    30 32 </div>
    31 33 {% endblock %}
    32 34
    37 39 class="form-control"
    38 40 id="{{ prefix }}AddressCity"
    39 41 name="{{ prefix }}[city]"
    40 Eliminada required>
    42 Añadida required
    43 Añadida autocomplete="address-level2">
    41 44 </div>
    42 45 {% endblock %}
    43 46
    47 50 <select class="form-select"
    48 51 id="{{ prefix }}AddressCountry"
    49 52 name="{{ prefix }}[countryId]"
    50 Eliminada required>
    53 Añadida required
    54 Añadida autocomplete="country">
    51 55 {% for country in countries %}
    52 56 <option value="{{ country.id }}"{% if loop.first %} selected{% endif %}>{{ country.name }}</option>
    53 57 {% endfor %}

    Resources/views/storefront/element/cms-element-form.html.twig Cambios en Resources/views/storefront/element/cms-element-form.html.twig (líneas añadidas: 4; eliminadas: 4)

    Cambios en Resources/views/storefront/element/cms-element-form.html.twig (líneas añadidas: 4; eliminadas: 4)
    Línea anterior Línea nueva Cambio Código
    18 18 </select>
    19 19 </div>
    20 20 <div class="form-group col-md-4">
    21 Eliminada <input type="text" class="form-control" id="contactFormFirstName" name="firstName" placeholder="{{ 'account.firstNamePlaceholder'|trans }}" required>
    21 Añadida <input type="text" class="form-control" id="contactFormFirstName" name="firstName" placeholder="{{ 'account.firstNamePlaceholder'|trans }}" required autocomplete="given-name">
    22 22 </div>
    23 23 <div class="form-group col-md-4">
    24 Eliminada <input type="text" class="form-control" id="contactFormLastName" name="lastName" placeholder="{{ 'account.lastNamePlaceholder'|trans }}" required>
    24 Añadida <input type="text" class="form-control" id="contactFormLastName" name="lastName" placeholder="{{ 'account.lastNamePlaceholder'|trans }}" required autocomplete="family-name">
    25 25 </div>
    26 26 </div>
    27 27
    28 28 <div class="row g-2">
    29 29 <div class="form-group col-md-6">
    30 30 <label class="form-label" for="contactFormMail">{{ 'account.emailLabel'|trans }}</label>
    31 Eliminada <input type="email" class="form-control" id="contactFormMail" name="email" placeholder="{{ 'account.emailPlaceholder'|trans }}" required>
    31 Añadida <input type="email" class="form-control" id="contactFormMail" name="email" placeholder="{{ 'account.emailPlaceholder'|trans }}" required autocomplete="email">
    32 32 </div>
    33 33 <div class="form-group col-md-6">
    34 34 <label class="form-label" for="contactFormPhone">{{ 'contact.phoneLabel'|trans }}</label>
    35 Eliminada <input type="tel" class="form-control" id="contactFormPhone" name="phone">
    35 Añadida <input type="tel" class="form-control" id="contactFormPhone" name="phone" autocomplete="tel">
    36 36 </div>
    37 37 </div>
    38 38

    Resources/views/storefront/layout/footer/footer.html.twig Cambios en Resources/views/storefront/layout/footer/footer.html.twig (líneas añadidas: 2; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/footer/footer.html.twig (líneas añadidas: 2; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    62 62 id="footerNewsletterMail"
    63 63 class="form-control"
    64 64 placeholder="{{ 'footer.newsletterPlaceholder'|trans }}"
    65 Eliminada required>
    65 Añadida required
    66 Añadida autocomplete="email">
    66 67 <button type="submit" class="btn btn-primary newsletter-submit">{{ 'footer.newsletterSubmit'|trans }}</button>
    67 68 </div>
    68 69 <div class="form-check newsletter-privacy">
  • Cambios de precio, de existencias y del carrito anunciados live-region-cart-variant El precio, la disponibilidad o los totales del carrito que el script del storefront actualiza en el mismo sitio pasan a ser regiones dinámicas que los lectores de pantalla leen sin interrumpir, salvo que un script sustituya el propio elemento.

    Las partes de una página que el script del storefront actualiza en el mismo sitio, como el precio después de que un cliente elija una variante, la disponibilidad o los totales del carrito, pasan a ser regiones dinámicas de tipo polite. Los lectores de pantalla leen entonces el valor nuevo una vez, completo y sin interrumpir. Cuando un script sustituye el propio elemento en lugar de su contenido, una región dinámica no funcionaría, así que la receta deja ese elemento en manos de una persona.

    Reglas que cubre
    • vs-live-updates AUTO
    Tipos de archivo que modifica
    .html.twig
    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>
  • Títulos para mapas y vídeos incrustados iframe-title Un marco incrustado sin título recibe un título traducido que dice qué muestra, cuando el código lo revela.

    Un marco incrustado sin título, como un mapa o un vídeo de YouTube o de Vimeo, recibe un título traducido, tomado de los archivos de snippets del tema, que dice qué muestra. La receta lee lo que muestra el marco a partir de su dirección, de su clase y su nombre, o de la plantilla que lo rodea. Un marco cuyo contenido no revela el código no recibe ningún título de la receta, porque un título inventado es peor que ninguno.

    Reglas que cubre
    • frame-title AUTO
    Tipos de archivo que modifica
    .html.twig.json
    titles the contact map frame through the snippets; frame-title no longer fires Datos de ejemplo

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 5; eliminadas: 0)

    Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 5; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
    194 194 "backToHome": "Zurück zur Startseite",
    195 195 "searchInstead": "Unsere Kaffees durchsuchen"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "iframe": {
    199 Añadida "map": "Karte"
    200 Añadida }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 5; eliminadas: 0)

    Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 5; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "The page you are looking for does not exist or has moved.",
    194 194 "backToHome": "Back to the homepage",
    195 195 "searchInstead": "Search our coffees"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "iframe": {
    199 Añadida "map": "Map"
    200 Añadida }
    196 201 }
    197 202 }

    Resources/views/storefront/block/cms-block-map.html.twig Cambios en Resources/views/storefront/block/cms-block-map.html.twig (líneas añadidas: 2; eliminadas: 1)

    Cambios en Resources/views/storefront/block/cms-block-map.html.twig (líneas añadidas: 2; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    11 11 <iframe class="cms-map-frame"
    12 12 src="{{ url('/widgets/cms/map') }}"
    13 13 width="1280"
    14 Eliminada height="360"></iframe>
    14 Añadida height="360"
    15 Añadida title="{{ 'a11y.iframe.map'|trans|striptags }}"></iframe>
    15 16 </div>
    16 17 <p class="cms-map-address">{{ shop.company.street }}, {{ shop.company.zip }} {{ shop.company.city }}</p>
    17 18 </div>

Contraste, imágenes y controles que requieren criterio

El contraste del texto, las alternativas textuales de las imágenes y los nombres de los controles sin nombre suelen requerir criterio: qué color encaja con su marca, qué palabras describen una imagen. Las reglas que los notifican tienen el nivel ASSISTED, como muestran las listas de más abajo. Para un hallazgo de una regla ASSISTED, la IA redacta un cambio solo si usted ha aceptado la redacción con IA, y Validiscan revisa cada cambio antes de entregarlo.

Las cuatro recetas siguientes no son la corrección estándar de ninguna regla. Cada una cubre solo los casos acotados en los que el código del tema por sí solo demuestra cuál debe ser el cambio. Dos de ellas, button-semantics e icon-accessible-name, también cubren la comprobación de carruseles vs-carousel, que es AUTO: en un proyecto, el motor de correcciones las prueba en sus hallazgos cuando la receta del botón de pausa de más arriba no puede corregirlos. Las otras dos cubren solo reglas de nivel ASSISTED.

  • Colores de texto con contraste suficiente contrast-tokens Un color de texto que no supera la comprobación de contraste y está definido en un solo lugar pasa al tono más próximo del mismo matiz con el contraste requerido; la receta no se aplica en un proyecto.

    Cuando un texto no supera la comprobación de contraste y su color está definido en un solo lugar del tema, ese lugar pasa al tono más próximo del mismo matiz que alcanza el contraste requerido, de modo que el diseño sigue siendo reconocible. Un solo lugar puede ser un campo de color de theme.json junto con la variable SCSS que lo repite, una variable SCSS o una propiedad personalizada de CSS que se usa solo para texto, o una única declaración color. Un color que se usa en varios lugares o también como fondo o borde, y los campos de color básicos de Shopware para la marca, los bordes, los fondos, los mensajes de estado y el botón de compra, quedan en manos de una persona. Un color guardado en los ajustes del tema de la Administración prevalece sobre theme.json: si se cambió allí, también hay que cambiarlo allí. La regla color-contrast tiene el nivel ASSISTED, así que en un proyecto un hallazgo de contraste sigue la vía ASSISTED y esta receta no se le aplica.

    Reglas que cubre
    • color-contrast ASSISTED
    Tipos de archivo que modifica
    .scss
    darkens the unit price colour to 4.5:1 with the same hue; axe clears it on both pages Datos de ejemplo

    Resources/app/storefront/src/scss/_variant-broken.scss Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/app/storefront/src/scss/_variant-broken.scss (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    45 45 /* Light grey unit prices, 3.1:1 (color-contrast). */
    46 46 .product-price-unit,
    47 47 .product-detail-price-unit {
    48 Eliminada color: #929292;
    48 Añadida color: #757575;
    49 49 }
    50 50
    51 51 /* Fixed two-line product names (vs-text-spacing). */
  • Imágenes decorativas ocultas a los lectores de pantalla decorative-image-alt Solo las imágenes que la plantilla demuestra decorativas reciben un alt vacío o aria-hidden; la receta no se aplica en un proyecto.

    Una imagen se marca como decorativa solo cuando la plantilla demuestra que no aporta nada: está dentro de un enlace o un botón que ya tiene nombre por otra vía, es un icono junto a su propia etiqueta de texto, o es un espaciador o un separador. Un <img> recibe un alt vacío, una imagen que imprime la etiqueta sw_thumbnails de Shopware recibe un alt vacío en sus atributos y los iconos SVG reciben aria-hidden="true", de modo que los lectores de pantalla los omiten en lugar de leer en voz alta el nombre de un archivo. Cualquier otra imagen es contenido, como los logotipos de los medios de pago del pie de página, y su alternativa textual necesita palabras que elige una persona. Las reglas que cubre esta receta tienen el nivel ASSISTED, así que en un proyecto sus hallazgos siguen la vía ASSISTED y esta receta no se les aplica.

    Reglas que cubre
    • image-alt ASSISTED
    • role-img-alt ASSISTED
    • svg-img-alt ASSISTED
    Tipos de archivo que modifica
    .html.twig
    marks a seeded phone icon inside the hotline link decorative; image-alt stops firing there Datos de ejemplo

    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)

    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">
  • Controles que solo funcionan con el ratón, convertidos en botones button-semantics Los controles de carruseles y banners de cookies que solo reaccionan al ratón pasan a ser botones de verdad y conservan sus clases, atributos data e ids; en un proyecto, la receta se prueba en los hallazgos de carrusel de vs-carousel.

    Los elementos <div>, <span>, <i> y <li> de carruseles y banners de cookies en los que se puede hacer clic, y los enlaces sin dirección, pasan a ser botones de verdad cuando solo reaccionan al ratón. Las clases, los atributos data y los ids se conservan, de modo que el script del tema los encuentra como antes, y Enter y la barra espaciadora hacen ahora lo que hace un clic. Un elemento de lista conserva su lugar en la lista y recibe el botón en su interior. Un control sin texto recibe un nombre traducido según su finalidad, como «Show slide 2», y un breve restablecimiento de estilos mantiene su aspecto. Un control sin texto cuya finalidad no está clara, o cuyo estilo depende de su etiqueta o de su posición, queda en manos de una persona. En un proyecto, esta receta se prueba en los hallazgos de carrusel de vs-carousel; los hallazgos de la comprobación del banner de cookies vs-cookie-banner tienen el nivel ASSISTED y siguen la vía ASSISTED.

    Reglas que cubre
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Tipos de archivo que modifica
    .html.twig.json.scss
    turns the mouse-only slider dots into named buttons in their list items; vs-carousel stops reporting them Datos de ejemplo

    Resources/app/storefront/src/scss/base.scss Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 17; eliminadas: 0)

    Cambios en Resources/app/storefront/src/scss/base.scss (líneas añadidas: 17; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Añadida
    1712 Añadida /* validiscan:button-semantics */
    1713 Añadida /* Former <div>/<span>/<li> controls, now <button>: drop the browser button styles, keep the theme's (no specificity). */
    1714 Añadida :where(button.base-slider-dot) {
    1715 Añadida display: block;
    1716 Añadida margin: 0;
    1717 Añadida padding: 0;
    1718 Añadida border: 0;
    1719 Añadida background: none;
    1720 Añadida color: inherit;
    1721 Añadida font: inherit;
    1722 Añadida line-height: inherit;
    1723 Añadida text-align: inherit;
    1724 Añadida cursor: pointer;
    1725 Añadida appearance: none;
    1726 Añadida }
    1727 Añadida /* /validiscan:button-semantics */

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 5; eliminadas: 0)

    Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 5; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
    194 194 "backToHome": "Zurück zur Startseite",
    195 195 "searchInstead": "Unsere Kaffees durchsuchen"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "showSlide": "Folie %index% anzeigen"
    200 Añadida }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 5; eliminadas: 0)

    Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 5; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "The page you are looking for does not exist or has moved.",
    194 194 "backToHome": "Back to the homepage",
    195 195 "searchInstead": "Search our coffees"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "showSlide": "Show slide %index%"
    200 Añadida }
    196 201 }
    197 202 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    51 51 </button>
    52 52 <ol class="base-slider-dots">
    53 53 {% for slide in slides %}
    54 Eliminada <li class="base-slider-dot{% if loop.first %} is-active{% endif %}" data-slide-to="{{ loop.index0 }}"></li>
    54 Añadida <li><button class="base-slider-dot{% if loop.first %} is-active{% endif %}" data-slide-to="{{ loop.index0 }}" type="button" aria-label="{{ 'a11y.control.showSlide'|trans({'%index%': loop.index})|striptags }}"></button></li>
    55 55 {% endfor %}
    56 56 </ol>
    57 57 <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next>
  • Nombres para botones y enlaces que solo muestran un icono icon-accessible-name Los botones y enlaces que solo muestran un icono reciben un nombre traducido que dice qué hacen, cuando el código revela su finalidad; en un proyecto, la receta se prueba en los controles de carrusel que notifica vs-carousel.

    Los botones y enlaces que solo muestran un icono, como la búsqueda, el carrito, la cuenta, el enlace del logotipo, las flechas del carrusel, los botones de cantidad y las opciones de cookies, reciben un aria-label traducido que dice qué hacen. La finalidad se deduce de las clases del control, del destino de su enlace, del icono que contiene y del componente que lo rodea. Un control cuya finalidad no revela el código, como un enlace a una red social cuya red se guarda como dato, queda en manos de una persona, porque un nombre inventado es peor que ninguno. En un proyecto, esta receta se prueba en los controles de carrusel que notifica vs-carousel; los demás controles que solo tienen un icono los notifican reglas de nivel ASSISTED y siguen la vía ASSISTED.

    Reglas que cubre
    • button-name ASSISTED
    • link-name ASSISTED
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Tipos de archivo que modifica
    .html.twig.json
    names every icon-only button of the home, product and cart pages; button-name no longer fires Datos de ejemplo

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 12; eliminadas: 0)

    Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 12; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
    194 194 "backToHome": "Zurück zur Startseite",
    195 195 "searchInstead": "Unsere Kaffees durchsuchen"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "search": "Suchen",
    200 Añadida "previousSlide": "Vorherige Folie",
    201 Añadida "nextSlide": "Nächste Folie",
    202 Añadida "cookieSettings": "Cookie-Einstellungen",
    203 Añadida "acceptCookies": "Alle Cookies akzeptieren",
    204 Añadida "decreaseQuantity": "Menge verringern",
    205 Añadida "increaseQuantity": "Menge erhöhen",
    206 Añadida "removeFromCart": "Aus dem Warenkorb entfernen"
    207 Añadida }
    196 208 }
    197 209 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 12; eliminadas: 0)

    Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 12; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "The page you are looking for does not exist or has moved.",
    194 194 "backToHome": "Back to the homepage",
    195 195 "searchInstead": "Search our coffees"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "search": "Search",
    200 Añadida "previousSlide": "Previous slide",
    201 Añadida "nextSlide": "Next slide",
    202 Añadida "cookieSettings": "Cookie settings",
    203 Añadida "acceptCookies": "Accept all cookies",
    204 Añadida "decreaseQuantity": "Decrease quantity",
    205 Añadida "increaseQuantity": "Increase quantity",
    206 Añadida "removeFromCart": "Remove from cart"
    207 Añadida }
    196 208 }
    197 209 }

    Resources/views/storefront/element/cms-element-image-gallery.html.twig Cambios en Resources/views/storefront/element/cms-element-image-gallery.html.twig (líneas añadidas: 2; eliminadas: 2)

    Cambios en Resources/views/storefront/element/cms-element-image-gallery.html.twig (líneas añadidas: 2; eliminadas: 2)
    Línea anterior Línea nueva Cambio Código
    31 31
    32 32 {% if media|length > 1 %}
    33 33 <div class="gallery-slider-controls">
    34 Eliminada <button type="button" class="base-slider-controls-prev gallery-slider-controls-prev" data-gallery-prev>
    34 Añadida <button type="button" class="base-slider-controls-prev gallery-slider-controls-prev" data-gallery-prev aria-label="{{ 'a11y.control.previousSlide'|trans|striptags }}">
    35 35 {% sw_icon 'arrow-head-left' %}
    36 36 </button>
    37 Eliminada <button type="button" class="base-slider-controls-next gallery-slider-controls-next" data-gallery-next>
    37 Añadida <button type="button" class="base-slider-controls-next gallery-slider-controls-next" data-gallery-next aria-label="{{ 'a11y.control.nextSlide'|trans|striptags }}">
    38 38 {% sw_icon 'arrow-head-right' %}
    39 39 </button>
    40 40 </div>

    Resources/views/storefront/element/cms-element-image-slider.html.twig Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 2; eliminadas: 2)

    Cambios en Resources/views/storefront/element/cms-element-image-slider.html.twig (líneas añadidas: 2; eliminadas: 2)
    Línea anterior Línea nueva Cambio Código
    46 46
    47 47 {% block element_image_slider_controls %}
    48 48 <div class="image-slider-controls-container">
    49 Eliminada <button type="button" class="base-slider-controls-prev image-slider-controls-btn" data-slide-prev>
    49 Añadida <button type="button" class="base-slider-controls-prev image-slider-controls-btn" data-slide-prev aria-label="{{ 'a11y.control.previousSlide'|trans|striptags }}">
    50 50 {% sw_icon 'arrow-head-left' %}
    51 51 </button>
    52 52 <ol class="base-slider-dots">
    54 54 <li class="base-slider-dot{% if loop.first %} is-active{% endif %}" data-slide-to="{{ loop.index0 }}"></li>
    55 55 {% endfor %}
    56 56 </ol>
    57 Eliminada <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next>
    57 Añadida <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next aria-label="{{ 'a11y.control.nextSlide'|trans|striptags }}">
    58 58 {% sw_icon 'arrow-head-right' %}
    59 59 </button>
    60 60 </div>

    Resources/views/storefront/layout/cookie/cookie-permission.html.twig Cambios en Resources/views/storefront/layout/cookie/cookie-permission.html.twig (líneas añadidas: 2; eliminadas: 2)

    Cambios en Resources/views/storefront/layout/cookie/cookie-permission.html.twig (líneas añadidas: 2; eliminadas: 2)
    Línea anterior Línea nueva Cambio Código
    22 22 </p>
    23 23 </div>
    24 24 <div class="col-auto cookie-permission-actions">
    25 Eliminada <button type="button" class="btn cookie-permission-icon-btn js-cookie-configuration-toggle">{% sw_icon 'settings' %}</button>
    26 Eliminada <button type="button" class="btn btn-primary cookie-permission-icon-btn js-cookie-accept-all-button">{% sw_icon 'checkmark' %}</button>
    25 Añadida <button type="button" class="btn cookie-permission-icon-btn js-cookie-configuration-toggle" aria-label="{{ 'a11y.control.cookieSettings'|trans|striptags }}">{% sw_icon 'settings' %}</button>
    26 Añadida <button type="button" class="btn btn-primary cookie-permission-icon-btn js-cookie-accept-all-button" aria-label="{{ 'a11y.control.acceptCookies'|trans|striptags }}">{% sw_icon 'checkmark' %}</button>
    27 27 </div>
    28 28 </div>
    29 29 <p class="cookie-permission-configuration" hidden>{{ 'cookie.configurationText'|trans }}</p>

    Resources/views/storefront/layout/header/search.html.twig Cambios en Resources/views/storefront/layout/header/search.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/header/search.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    13 13 autocomplete="off"
    14 14 autocapitalize="off"
    15 15 placeholder="{{ 'header.searchPlaceholder'|trans }}" value="{{ page.searchTerm }}">
    16 Eliminada <button type="submit" class="btn header-search-btn">
    16 Añadida <button type="submit" class="btn header-search-btn" aria-label="{{ 'a11y.control.search'|trans|striptags }}">
    17 17 <span class="header-search-icon">{% sw_icon 'search' %}</span>
    18 18 </button>
    19 19 </div>

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

    Cambios en Resources/views/storefront/page/checkout/cart/index.html.twig (líneas añadidas: 3; eliminadas: 3)
    Línea anterior Línea nueva Cambio Código
    39 39 </div>
    40 40 <div class="line-item-quantity">
    41 41 <div class="input-group quantity-selector-group" data-quantity-selector="true">
    42 Eliminada <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    42 Añadida <button type="button" class="btn btn-outline-light btn-minus js-btn-minus" aria-label="{{ 'a11y.control.decreaseQuantity'|trans|striptags }}">
    43 43 {% sw_icon 'minus' style { size: 'xs' } %}
    44 44 </button>
    45 45 <input type="number"
    50 50 max="100"
    51 51 step="1"
    52 52 value="{{ item.quantity }}">
    53 Eliminada <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    53 Añadida <button type="button" class="btn btn-outline-light btn-plus js-btn-plus" aria-label="{{ 'a11y.control.increaseQuantity'|trans|striptags }}">
    54 54 {% sw_icon 'plus' style { size: 'xs' } %}
    55 55 </button>
    56 56 </div>
    64 64 <span class="line-item-total-price-value" data-line-total>{{ item.totalPrice|currency }}*</span>
    65 65 </div>
    66 66 <div class="line-item-remove">
    67 Eliminada <button type="button" class="btn btn-light btn-sm line-item-remove-button">
    67 Añadida <button type="button" class="btn btn-light btn-sm line-item-remove-button" aria-label="{{ 'a11y.control.removeFromCart'|trans|striptags }}">
    68 68 {% sw_icon 'x' style { size: 'xs' } %}
    69 69 </button>
    70 70 </div>

    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: 2; eliminadas: 2)

    Cambios en Resources/views/storefront/page/product-detail/buy-widget.html.twig (líneas añadidas: 2; eliminadas: 2)
    Línea anterior Línea nueva Cambio Código
    64 64 <div class="buy-widget-container">
    65 65 <div class="buy-widget-quantity">
    66 66 <div class="input-group quantity-selector-group" data-quantity-selector="true">
    67 Eliminada <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    67 Añadida <button type="button" class="btn btn-outline-light btn-minus js-btn-minus" aria-label="{{ 'a11y.control.decreaseQuantity'|trans|striptags }}">
    68 68 {% sw_icon 'minus' style { size: 'xs' } %}
    69 69 </button>
    70 70 <input type="number"
    75 75 max="100"
    76 76 step="1"
    77 77 value="1">
    78 Eliminada <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    78 Añadida <button type="button" class="btn btn-outline-light btn-plus js-btn-plus" aria-label="{{ 'a11y.control.increaseQuantity'|trans|striptags }}">
    79 79 {% sw_icon 'plus' style { size: 'xs' } %}
    80 80 </button>
    81 81 </div>
    names the logo and account links; social links whose network is data are left for review Datos de ejemplo

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 6; eliminadas: 0)

    Cambios en Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (líneas añadidas: 6; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "Die gesuchte Seite existiert nicht oder wurde verschoben.",
    194 194 "backToHome": "Zurück zur Startseite",
    195 195 "searchInstead": "Unsere Kaffees durchsuchen"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "home": "Zur Startseite",
    200 Añadida "account": "Ihr Konto"
    201 Añadida }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 6; eliminadas: 0)

    Cambios en Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (líneas añadidas: 6; eliminadas: 0)
    Línea anterior Línea nueva Cambio Código
    193 193 "notFoundText": "The page you are looking for does not exist or has moved.",
    194 194 "backToHome": "Back to the homepage",
    195 195 "searchInstead": "Search our coffees"
    196 Añadida },
    197 Añadida "a11y": {
    198 Añadida "control": {
    199 Añadida "home": "Go to the homepage",
    200 Añadida "account": "Your account"
    201 Añadida }
    196 202 }
    197 203 }

    Resources/views/storefront/layout/header/actions.html.twig Cambios en Resources/views/storefront/layout/header/actions.html.twig (líneas añadidas: 2; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/header/actions.html.twig (líneas añadidas: 2; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    3 3 {% block layout_header_actions_account %}
    4 4 <div class="header-account">
    5 5 <a href="{{ url('/account/login') }}"
    6 Eliminada class="btn account-menu-btn header-actions-btn">
    6 Añadida class="btn account-menu-btn header-actions-btn"
    7 Añadida aria-label="{{ 'a11y.control.account'|trans|striptags }}">
    7 8 {% sw_icon 'avatar' %}
    8 9 </a>
    9 10 </div>

    Resources/views/storefront/layout/header/logo.html.twig Cambios en Resources/views/storefront/layout/header/logo.html.twig (líneas añadidas: 1; eliminadas: 1)

    Cambios en Resources/views/storefront/layout/header/logo.html.twig (líneas añadidas: 1; eliminadas: 1)
    Línea anterior Línea nueva Cambio Código
    2 2 <div class="header-logo-main">
    3 3 {% block layout_header_logo_link %}
    4 4 {# The agency removed the default title="Go to homepage" and the image alt to "avoid duplicate announcements". #}
    5 Eliminada <a class="header-logo-main-link" href="{{ url('/') }}">
    5 Añadida <a class="header-logo-main-link" href="{{ url('/') }}" aria-label="{{ 'a11y.control.home'|trans|striptags }}">
    6 6 {% block layout_header_logo_image %}
    7 7 <picture class="header-logo-picture">
    8 8 <img src="{{ asset('bundles/storefront/assets/logo/kaffeekontor-nord-logo.svg') }}"
Detalles: los niveles, el tema de prueba, las comprobaciones de cada cambio y el texto en los idiomas de su tienda

Una receta es un cambio determinista para un tipo de fallo: el mismo código recibe siempre el mismo cambio. Validiscan tiene 20 recetas para temas de Shopware. Cada una hace un cambio solo donde el código del tema muestra cuál es el cambio correcto. Donde no lo muestra, la receta no cambia nada. No adivina.

Debajo de cada receta encontrará las reglas que cubre, con el nivel de cada regla en el catálogo de Validiscan para Shopware. AUTO significa que una receta es la corrección estándar de la regla. ASSISTED significa que la corrección requiere criterio, así que se redacta un cambio y después se revisa. MANUAL significa que la regla requiere la decisión de una persona y viene con orientación, no con un cambio de código. En un proyecto, las recetas se aplican a los hallazgos de reglas de nivel AUTO. Cuando una receta enumera también una regla de nivel ASSISTED o MANUAL, un hallazgo de esa regla sigue en su lugar la vía ASSISTED o MANUAL. Una regla que se comprueba mediante otra comprobación no se ejecuta por sí sola en un escaneo; la otra comprobación informa del mismo fallo.

Los tipos de archivo y el antes y el después de cada receta proceden de las pruebas propias de Validiscan. Se ejecutan en el tema de prueba de Shopware de Validiscan, un plugin de tema sobre el storefront predeterminado de Shopware 6 con fallos de accesibilidad introducidos a propósito. Cada prueba renderiza las plantillas del tema como páginas, antes y después del cambio, y ejecuta la misma regla en ambas: el fallo debe notificarse antes y ya no después, y el cambio no debe causar ningún fallo nuevo de esa regla. Las páginas las genera un renderizador de pruebas, no una instalación completa de Shopware, y los archivos de su tema serán distintos.

Antes de que Validiscan revise un cambio, se comprueba cada archivo modificado. Las plantillas Twig se analizan con las etiquetas propias de Shopware, como sw_extends y sw_include, reconocidas por el analizador; nunca se compilan ni se ejecutan. Las hojas de estilos pasan una comprobación de estructura, los archivos de snippets y el theme.json se analizan como JSON y el JavaScript pasa una comprobación de sintaxis.

Cuando una receta añade texto visible o un nombre, como las palabras de un enlace para saltar al contenido o el nombre de un botón de icono, usa un snippet en la plantilla. El texto va al archivo de snippets en inglés del tema, que la receta crea si el tema no tiene ninguno, y a su archivo de snippets en alemán cuando el tema tiene uno. Los snippets que ya existen conservan su texto. No se añade texto para los demás idiomas de su tienda.

¿Qué sigue siendo MANUAL en Shopware?

Los fallos que requieren una decisión sobre el contenido o el comportamiento que un cambio de código fijo no puede tomar, por ejemplo subtítulos y transcripciones de sus vídeos y su audio, siguen siendo MANUAL en Shopware. Para un hallazgo de una regla MANUAL, Validiscan da orientación sobre cómo corregirlo, no un cambio en su código.

Las reglas de más abajo se ejecutan en los escaneos y tienen el nivel MANUAL en Shopware.

  • meta-refresh No debe usarse una actualización diferida de menos de 20 horas
  • nested-interactive Los controles interactivos no deben estar anidados
  • scrollable-region-focusable Las zonas desplazables deben ser accesibles con el teclado
  • server-side-image-map No deben usarse mapas de imagen del lado del servidor
  • vs-keyboard-trap El foco del teclado nunca debe quedar atrapado
  • vs-focus-order El orden del foco debe seguir el orden visual
  • vs-dragging Los controles que solo funcionan arrastrando necesitan una alternativa con un solo puntero
  • vs-media Los vídeos necesitan subtítulos, el audio necesita una transcripción y nada debe reproducirse automáticamente con sonido
  • vs-documents Los documentos enlazados necesitan una revisión manual de accesibilidad
Detalles: qué recibe un hallazgo MANUAL y lo que un proyecto deja fuera

Para un hallazgo de una regla MANUAL, Validiscan da orientación sobre cómo corregirlo, no un cambio en su código. Estos fallos requieren una decisión sobre el contenido o el comportamiento que un cambio de código fijo no puede tomar, por ejemplo los subtítulos y las transcripciones de sus vídeos y su audio, una forma de salir de un widget que atrapa el foco del teclado, o una segunda forma de usar un control que solo funciona arrastrando.

El trabajo que suponen los hallazgos MANUAL que requieren decisiones sobre su propio contenido no forma parte de un proyecto, como tampoco los PDF. Qué comprueba el escaneo de forma automática y qué se revisa a mano

¿Qué aporta usted?

Usted aporta permiso para un escaneo completo, una vía de acceso a su plugin de tema, su respuesta sobre la redacción con IA de correcciones ASSISTED, un desarrollador que revise los cambios, compile el tema y lo despliegue, ajustes de la Administración, texto nuevo en idiomas que no sean inglés ni alemán y sus decisiones sobre lo que sigue siendo MANUAL.

  1. Su permiso para un escaneo completo de su tienda, que Validiscan registra antes del escaneo. Un escaneo completo añade un producto al carrito y abre el primer paso del proceso de pago, sin completar una compra ni introducir datos de pago.
  2. Una vía de acceso a su tema: acceso al repositorio de GitHub que contiene el plugin del tema, o un ZIP de la carpeta del plugin en custom/plugins, subido en el portal del cliente.
  3. Su respuesta sobre si la IA puede redactar correcciones de nivel ASSISTED. La redacción con IA está desactivada hasta que usted lo acepte.
  4. Un desarrollador que revise los cambios, fusione el pull request o aplique el paquete de parches, compile el tema y lo despliegue.
  5. Los ajustes de la Administración de Shopware que las correcciones no cambian: los metatítulos de las páginas que no tienen ninguno, el idioma del contenido escrito en un idioma distinto del de su tienda, y un color guardado en los ajustes del tema cuando una corrección de contraste cambia ese color.
  6. El texto para los idiomas de su tienda distintos del inglés y del alemán, cuando una corrección añade texto nuevo.
  7. Sus decisiones sobre lo que sigue siendo MANUAL, como los subtítulos de sus vídeos.
Detalles: cuando los cambios están en producción y qué cubre un proyecto

Cuando los cambios están en producción, un escaneo de verificación vuelve a comprobar las páginas afectadas y el Remediation Evidence Package registra el resultado. Cómo se desarrolla un proyecto, paso a paso

Un proyecto cubre una tienda: un dominio y un tema. Es para un storefront basado en un tema; un storefront headless se atiende bajo petición.

Fuentes

2 fuentes, todas comprobadas para esta página el 6 de octubre de 2026.

Documentación de Shopware para desarrolladores 2
  1. Add SCSS Styling and JavaScript to a Theme · Shopware · comprobado el
  2. Add custom JavaScript · Shopware · comprobado el

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