Corrections d’accessibilité dans le code de votre thème Shopware

Validiscan corrige les échecs d’accessibilité dans les templates Twig, le SCSS, le JavaScript de la Storefront et les fichiers de snippets de votre plugin de thème Shopware 6. Rien n’atteint votre boutique en ligne avant que votre développeur ait fusionné ou appliqué les modifications et déployé le thème.

Dernière mise à jour :

Que modifie Validiscan dans un thème Shopware ?

Validiscan ne modifie que les fichiers des dossiers où un thème de la Storefront de Shopware 6 range ses sources, Resources/views, Resources/app/storefront/src et Resources/snippet, et son theme.json. Une modification de tout autre fichier échoue à la validation et n’est jamais livrée, et aucune correction n’installe de plugin ni n’ajoute de script d’overlay.

Les réglages que vous gérez dans l’Administration de Shopware, comme les méta-titres ou les couleurs de thème qui y sont enregistrées, ne sont pas des fichiers du thème : les corrections ne les modifient donc pas. Un projet couvre un domaine et un thème ; les contenus tiers intégrés, comme les widgets de paiement ou de chat, n’y sont pas compris.

Détails : les dossiers, l’aspect d’une modification et les deux recettes qui modifient le script de la Storefront

Validiscan ne modifie que des fichiers situés dans les dossiers où un thème de la Storefront de Shopware 6 range ses sources : les templates Twig dans Resources/views, les sources SCSS et JavaScript dans Resources/app/storefront/src, les fichiers de snippets qui contiennent les textes de la Storefront dans Resources/snippet, et le theme.json du thème. Une modification de tout autre fichier, comme les fichiers compilés de Resources/app/storefront/dist ou le code PHP et la configuration du plugin, échoue à la validation. Une modification qui échoue à la validation ne peut pas être approuvée : elle n’est donc jamais livrée.

Chaque changement est une modification ordinaire d’un fichier du thème, que votre développeur peut lire ligne par ligne. Les petites modifications, comme un attribut ajouté ou une valeur de couleur corrigée, sont faites là où se trouve déjà le code. Les nouvelles règles de style et les nouveaux blocs de script se placent entre des commentaires qui portent le nom de la recette, par exemple /* validiscan:focus-visible-styles */, et sont donc faciles à retrouver. Deux recettes modifient le script de la Storefront propre au thème : l’une relie un bouton de pause au slider d’images, l’autre empêche le défilement automatique d’un script de démarrer lorsqu’un visiteur a demandé moins de mouvement. Aucune correction n’installe de plugin ni n’ajoute de script qui corrige vos pages après leur chargement, comme le fait un overlay.

Comment Validiscan accède-t-il à votre thème Shopware ?

Par un dépôt GitHub ou un ZIP du plugin de thème téléversé dans le portail client, car Validiscan prépare chaque correction sur une copie du code source de votre plugin de thème Shopware. Le choix dépend de l’endroit où se trouve le code : un dépôt donne lieu à une pull request, un ZIP à un lot de correctifs.

Les deux voies d’accès à un plugin de thème Shopware
Voie d’accès Ce que vous fournissez Les modifications reviennent sous forme de
Par votre dépôt GitHub L’accès au dépôt qui contient le plugin de thème, par l’application GitHub de Validiscan ou un jeton d’accès Une pull request
À partir d’un ZIP du plugin de thème Le dossier du plugin de thème sous custom/plugins, en un seul fichier ZIP de 100 Mo au maximum, téléversé dans le portail client par un administrateur de votre organisation Un lot de correctifs

Par votre dépôt GitHub

Si le code de votre plugin de thème est conservé dans un dépôt GitHub, vous donnez à Validiscan l’accès à ce dépôt : vous y installez l’application GitHub de Validiscan, ou vous donnez à Validiscan un jeton d’accès pour ce dépôt. La configuration de Validiscan fixe les autorisations de l’application GitHub : accès en lecture et en écriture au contenu du dépôt et à ses pull requests, accès en lecture à ses métadonnées, et rien d’autre. Un jeton doit avoir les mêmes autorisations, sur ce seul dépôt.

Le plugin de thème peut se trouver à la racine du dépôt ou plus profondément, comme dans custom/plugins/YourTheme/src/Resources : les recettes retrouvent les fichiers du thème d’après les conventions de dossiers de Shopware. Les modifications arrivent alors sous forme de pull request.

À partir d’un ZIP du plugin de thème

Si le code ne se trouve pas dans un dépôt GitHub, votre développeur prépare un ZIP du plugin de thème. L’instruction du portail client pour Shopware est la suivante : « Demandez à votre développeur un ZIP du plugin de votre thème : son dossier sous custom/plugins, tel qu’il est déployé sur votre boutique. » Un administrateur de votre organisation téléverse ensuite le fichier dans le portail client, sous la forme d’un seul fichier ZIP de 100 Mo au maximum. Validiscan prépare les modifications sur cette copie de votre thème et les renvoie sous forme de lot de correctifs.

Sans l’une ou l’autre de ces deux voies, Validiscan n’a aucun code à modifier, et aucune correction n’est préparée.

Comment les modifications parviennent-elles à votre boutique ?

Les modifications arrivent sous forme de pull request ou de lot de correctifs, et votre développeur décide quand elles passent en ligne. Validiscan ne livre que des modifications relues et approuvées et ne modifie jamais le thème ni le code de la version en ligne de votre boutique Shopware ; la livraison d’une copie non publiée du thème n’existe que sur Shopify.

De la livraison à votre boutique en ligne, en cinq étapes
  1. Validiscan livre les modifications qu’il a relues et approuvées : une pull request sur une nouvelle branche, ou un lot de correctifs pour votre développeur.
  2. Votre développeur relit les modifications. Pour une pull request, la liste de contrôle demande un environnement de prévisualisation ou de préproduction, et non la version en ligne de la boutique, ainsi qu’une vérification des pages concernées, y compris au clavier seul.
  3. Votre développeur fusionne la pull request ou applique le lot de correctifs.
  4. Votre développeur compile le thème : les modifications du SCSS prennent effet une fois le thème recompilé, celles du JavaScript une fois la Storefront reconstruite.
  5. Votre développeur déploie le thème. Une fois les modifications en ligne, un scan de vérification contrôle de nouveau les pages concernées.

Une pull request

Avec l’accès à votre dépôt, Validiscan crée une nouvelle branche nommée validiscan/a11y-<date>-<id>, enregistre chaque correction dans un commit distinct et ouvre une seule pull request, avec un tableau récapitulatif, une explication de chaque modification et une liste de contrôle pour la relecture. Rien n’est poussé vers votre branche par défaut. La liste de contrôle demande à votre développeur de déployer la branche dans un environnement de prévisualisation ou de préproduction, et non sur la version en ligne de la boutique, puis d’y vérifier les pages concernées, y compris au clavier seul, avant de fusionner.

Un lot de correctifs

À partir du ZIP téléversé, Validiscan prépare une archive zip et l’envoie à votre développeur. Elle contient PATCH.md, avec chaque modification et la façon de l’appliquer, toutes les modifications dans un seul fichier de correctif qui s’applique avec git apply, un fichier de correctif par correction, les fichiers modifiés en entier et un README.md. Votre développeur applique les modifications, les teste et déploie le thème. Ce que contiennent une pull request et un lot de correctifs

Compiler le thème

Une boutique Shopware ne sert pas telles quelles les sources SCSS et JavaScript du thème : elles sont d’abord compilées. Les modifications du SCSS prennent effet une fois le thème recompilé, par exemple avec bin/console theme:compile, comme le décrit la documentation de Shopware pour les développeurs sur le style des thèmes. Les modifications du JavaScript de la Storefront prennent effet une fois la Storefront reconstruite, comme le décrit la documentation de Shopware sur le JavaScript de la Storefront. Votre développeur exécute ces étapes lors du déploiement du thème modifié. Comment les corrections d’accessibilité parviennent à un thème Shopware, et ce qu’une mise à jour du thème peut défaire

Que modifient les recettes dans un thème Shopware ?

Chacune des 20 recettes de Validiscan pour les thèmes Shopware est une modification déterministe pour un type d’échec : le même code reçoit toujours la même modification. Une recette n’intervient que là où le code du thème montre quelle modification est la bonne ; là où il ne le montre pas, elle ne change rien.

Structure de la page et navigation

  • Langue de la page dans le layout de base html-lang L’élément <html> du layout de base reçoit un attribut lang valide lorsqu’il manque ou lorsque le thème produit un code de locale comme en_GB.

    L’élément <html> du layout de base de la Storefront reçoit un attribut lang valide, de sorte que les lecteurs d’écran prononcent la page dans la bonne langue. Lorsque l’attribut manque, la recette ajoute le code de la langue active de la boutique, par exemple de-DE, la valeur que produit le template de base de Shopware lui-même. Lorsque le thème produit un code de locale comme en_GB, qui n’est pas une balise de langue valide, le template le transforme désormais en en-GB. Une page dont le contenu est rédigé dans une autre langue que celle de la boutique, comme un texte juridique allemand dans une boutique anglaise, vous revient : la langue de ce contenu se définit dans l’Administration ou se signale dans le texte lui-même.

    Règles couvertes
    • html-has-lang AUTO contrôle assuré par vs-lang
    • html-lang-valid AUTO contrôle assuré par vs-lang
    • vs-lang AUTO
    Types de fichiers modifiés
    .html.twig
    normalises the locale code of the base layout; German content pages are left to the merchant Données d’exemple

    Resources/views/storefront/base.html.twig Modifications apportées à Resources/views/storefront/base.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/base.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    9 9 {% block base_doctype %}<!DOCTYPE html>{% endblock %}
    10 10
    11 11 {% block base_html %}
    12 Supprimée <html lang="{{ page.lang }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    12 Ajoutée <html lang="{{ page.lang|replace({'_': '-'}) }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    13 13 {% endblock %}
    14 14
    15 15 {% block base_head %}
  • Un titre sur chaque page document-title-template Lorsque le layout n’écrit que le méta-titre dans le <title>, le nom de la boutique devient sa valeur de repli, de sorte qu’aucune page n’a de titre vide.

    Lorsque le layout du thème n’écrit que le méta-titre de la page dans le <title>, les catégories et les pages CMS sans méta-titre reçoivent un titre vide. La recette conserve le titre propre au thème et lui donne une valeur de repli : le nom de la boutique, repris de l’endroit où le thème l’affiche déjà ou, à défaut, du nom de la boutique dans les réglages de base de Shopware. Plus aucune page n’a alors de titre vide. Un titre réduit au nom de la boutique n’indique toujours pas ce qu’est la page ; ces pages ont donc besoin d’un méta-titre dans l’Administration. Les titres qui ont déjà une valeur de repli ou un texte fixe sont laissés tels quels.

    Règles couvertes
    • document-title AUTO
    Types de fichiers modifiés
    .html.twig
    gives the layout title a shop-name fallback and the empty CMS title is gone Données d’exemple

    Resources/views/storefront/layout/meta.html.twig Modifications apportées à Resources/views/storefront/layout/meta.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/meta.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    36 36 {% endblock %}
    37 37
    38 38 {% block layout_head_title %}
    39 Supprimée <title>{{ page.metaTitle }}</title>
    39 Ajoutée <title>{{ page.metaTitle|default(shop.name) }}</title>
    40 40 {% endblock %}
    41 41
    42 42 {% block layout_head_stylesheet %}
  • Des titres sans niveau sauté heading-levels Un titre qui saute un niveau remonte, avec les autres titres du template à ce niveau ou à un niveau plus profond, et conserve son aspect.

    Un titre qui saute un niveau, comme un h3 juste après un h1, remonte au niveau suivant, et les autres titres du même template, à ce niveau ou à un niveau plus profond, le suivent, de sorte que le plan propre au template reste intact. Lorsque le template définit la balise du titre dans une variable, c’est la variable qui est modifiée. Les classes restent, et lorsqu’aucune classe ne définit la taille, une classe de titre de Bootstrap conserve l’aspect d’origine. Sur une page sans titre principal, l’élément qui lui sert déjà de titre dans son propre template devient le h1, en conservant son aspect. Si une page n’a aucun élément de ce genre, la recette n’ajoute pas de titre, car un nouveau titre suppose d’en choisir les mots.

    Règles couvertes
    • heading-order AUTO contrôle assuré par vs-headings-landmarks
    • page-has-heading-one AUTO contrôle assuré par vs-headings-landmarks
    • vs-headings-landmarks AUTO
    Types de fichiers modifiés
    .html.twig
    moves a static grid title from h3 to h2; the skipped level is gone Données d’exemple

    Resources/views/storefront/block/cms-block-product-grid.html.twig Modifications apportées à Resources/views/storefront/block/cms-block-product-grid.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/block/cms-block-product-grid.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    5 5 {% block block_product_grid %}
    6 6 <div class="cms-block cms-block-product-grid">
    7 7 <div class="cms-block-container">
    8 Supprimée <h3 class="cms-block-title">{{ block.title }}</h3>
    8 Ajoutée <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 } %}

Utilisation au clavier et focus

  • Un contour de focus visible focus-visible-styles Un seul contour pour le focus clavier, dans la couleur de marque du thème ou la nuance la plus proche de la même teinte dont le contraste avec l’arrière-plan de la page est d’au moins 3:1.

    La feuille de style principale reçoit un seul contour pour le focus clavier : 2 pixels, en trait plein, à 2 pixels de l’élément, dans la couleur de marque du thème. Lorsque cette couleur est trop claire, on utilise la nuance la plus proche de la même teinte dont le contraste avec l’arrière-plan de la page est d’au moins 3:1. Les règles du thème qui suppriment le contour au focus ne s’appliquent désormais qu’au focus à la souris : un clic conserve l’aspect du thème, et le focus clavier affiche le contour. Les règles qui montrent le focus d’une autre façon sont conservées. Sur les zones sombres, comme une barre supérieure, le contour peut demander une couleur plus claire, ce que votre développeur vérifie dans l’aperçu.

    Règles couvertes
    • vs-focus-visible AUTO
    Types de fichiers modifiés
    .scss
    adds a brand-coloured :focus-visible ring and scopes the outline resets Données d’exemple

    Resources/app/storefront/src/scss/_variant-broken.scss Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 5, lignes supprimées : 5)

    Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 5, lignes supprimées : 5)
    Ligne avant Ligne après Modification Code
    21 21 }
    22 22
    23 23 /* "Remove the ugly blue ring" (vs-focus-visible). */
    24 Supprimée .header-actions-btn:focus,
    25 Supprimée .header-search-btn:focus,
    26 Supprimée .main-navigation-link:focus,
    27 Supprimée .product-name:focus,
    28 Supprimée .footer-link:focus {
    24 Ajoutée .header-actions-btn:focus:not(:focus-visible),
    25 Ajoutée .header-search-btn:focus:not(:focus-visible),
    26 Ajoutée .main-navigation-link:focus:not(:focus-visible),
    27 Ajoutée .product-name:focus:not(:focus-visible),
    28 Ajoutée .footer-link:focus:not(:focus-visible) {
    29 29 outline: 0;
    30 30 box-shadow: none;
    31 31 }

    Resources/app/storefront/src/scss/base.scss Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 8, lignes supprimées : 0)

    Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 8, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Ajoutée
    1712 Ajoutée /* validiscan:focus-visible-styles */
    1713 Ajoutée /* Visible keyboard focus (WCAG 2.4.7): brand colour sw-color-brand-primary, 8.9:1 on #ffffff. */
    1714 Ajoutée :focus-visible {
    1715 Ajoutée outline: 2px solid #6b3f1d;
    1716 Ajoutée outline-offset: 2px;
    1717 Ajoutée }
    1718 Ajoutée /* /validiscan:focus-visible-styles */
  • Un ordre de tabulation qui suit la page remove-positive-tabindex Un tabindex supérieur à 0 est retiré ou fixé à 0, afin que la touche Tab suive l’ordre de la page, sauf si le code du template l’écrit.

    Un tabindex supérieur à 0 fait passer un élément en tête de l’ordre de tabulation. La recette le retire des liens, des boutons, des champs de formulaire et des autres éléments qui peuvent de toute façon recevoir le focus, et le fixe à 0 sur les autres éléments, afin qu’ils restent atteignables pendant que la touche Tab suit l’ordre de la page. Un tabindex que le code du template écrit est laissé à une personne. Un scan signale un tel ordre forcé deux fois : par la règle tabindex d’axe-core, dont les constats reçoivent cette modification, et par le contrôle vs-focus-order de Validiscan, du niveau MANUAL, dont les constats s’accompagnent donc d’indications.

    Règles couvertes
    • tabindex AUTO
    • vs-focus-order MANUAL
    Types de fichiers modifiés
    .html.twig
    removes tabindex="1" from the buy button; vs-focus-order and axe tabindex stop firing Données d’exemple

    Resources/views/storefront/page/product-detail/buy-widget.html.twig Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    82 82 </div>
    83 83 <div class="buy-widget-submit">
    84 84 <button class="btn btn-primary btn-buy"
    85 Supprimée title="{{ 'detail.addProduct'|trans }}" tabindex="1">
    85 Ajoutée title="{{ 'detail.addProduct'|trans }}">
    86 86 {{ 'detail.addProduct'|trans }}
    87 87 </button>
    88 88 </div>

Zoom et cibles tactiles

  • Zoom autorisé sur téléphone meta-viewport-zoom La balise viewport perd user-scalable=no et tout maximum-scale inférieur à 2, afin que les visiteurs puissent agrandir le texte à 200 % au moins sur un téléphone.

    La recette retire user-scalable=no et tout maximum-scale inférieur à 2 de la balise viewport des templates de la Storefront, en général dans layout/meta.html.twig, afin que les visiteurs puissent agrandir le texte à 200 % au moins sur un téléphone. Pour la règle de bonne pratique meta-viewport-large, la limite est de 5. Les autres réglages du viewport restent tels quels. Si le blocage servait à empêcher iOS de zoomer sur les champs de formulaire, une taille de police d’au moins 16 pixels pour les champs y suffit sans bloquer le zoom.

    Règles couvertes
    • meta-viewport AUTO
    • meta-viewport-large AUTO
    Types de fichiers modifiés
    .html.twig
    removes the zoom lock from the fixture theme and the rule stops firing Données d’exemple

    Resources/views/storefront/layout/meta.html.twig Modifications apportées à Resources/views/storefront/layout/meta.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/meta.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    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 Supprimée <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    9 Ajoutée <meta name="viewport" content="width=device-width, initial-scale=1">
    10 10 {% endblock %}
    11 11
    12 12 {% block layout_head_meta_tags_general %}
  • Des cibles tactiles plus grandes target-size-padding Les boutons et les liens que le scan a trouvés plus petits que 24 × 24 pixels reçoivent cette taille minimale, lorsque la recette peut les identifier par leur classe.

    Les boutons et les liens que le scan a trouvés plus petits que 24 × 24 pixels, comme les icônes de l’en-tête, les boutons de quantité ou les pastilles de couleur, reçoivent une taille minimale de 24 × 24 pixels. Ce minimum est ajouté à la règle de style du thème qui a rendu la commande petite, à côté de la déclaration qui l’a réduite, de sorte que l’ordre des styles du thème ne change pas et que les icônes à l’intérieur gardent leur taille. Une commande petite sans règle de ce genre reçoit, dans la feuille de style principale, une taille minimale pour sa classe. Une commande que la recette ne peut pas identifier par sa classe est laissée à une personne.

    Règles couvertes
    • vs-target-size AUTO
    Types de fichiers modifiés
    .scss
    gives the header icons, steppers and swatches a 24px minimum; no page reports small targets Données d’exemple

    Resources/app/storefront/src/scss/_variant-broken.scss Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 6, lignes supprimées : 2)

    Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 6, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    10 10
    11 11 .header-actions-btn {
    12 12 width: 18px;
    13 Ajoutée min-width: 24px;
    13 14 height: 18px;
    14 Supprimée min-height: 0;
    15 Ajoutée 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 Ajoutée min-width: 24px;
    99 101 height: 18px;
    102 Ajoutée 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 Ajoutée min-width: 24px;
    106 110 height: 20px;
    107 Supprimée min-height: 0;
    111 Ajoutée min-height: 24px;
    108 112 padding: 0;
    109 113 }
    110 114

Mouvement, diaporamas et son

  • Moins de mouvement lorsque le visiteur le demande reduced-motion-guard Lorsque l’appareil d’un visiteur demande de réduire les mouvements, les animations et les transitions que définissent les feuilles de style se terminent aussitôt et le défilement fluide est désactivé.

    Lorsque l’appareil d’un visiteur est réglé pour réduire les mouvements, un bloc de la feuille de style principale fait se terminer aussitôt les animations et les transitions et désactive le défilement fluide. Le bloc s’applique à chaque élément de la page ; il couvre donc aussi les animations que définissent d’autres feuilles de style, y compris celles des plugins. Lorsque le script de la Storefront du thème démarre un minuteur, comme le défilement automatique d’un diaporama, uniquement si une option de défilement automatique est définie, cette condition tient désormais compte aussi du réglage du visiteur. Les autres mouvements qu’un script déclenche sont consignés à l’intention de la personne qui relit, et non devinés.

    Règles couvertes
    • vs-motion AUTO
    Types de fichiers modifiés
    .js.scss
    guards the pulsing badge and the slider autoplay; the animation stops under reduce Données d’exemple

    Resources/app/storefront/src/main.js Modifications apportées à Resources/app/storefront/src/main.js (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/app/storefront/src/main.js (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    139 139 });
    140 140 });
    141 141
    142 Supprimée if (options.autoplay === true) {
    142 Ajoutée 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 Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 14, lignes supprimées : 0)

    Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 14, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Ajoutée
    1712 Ajoutée /* validiscan:reduced-motion-guard */
    1713 Ajoutée /* Honour the visitor's reduced-motion setting: animations and transitions end at once, no smooth scrolling. */
    1714 Ajoutée @media (prefers-reduced-motion: reduce) {
    1715 Ajoutée *,
    1716 Ajoutée *::before,
    1717 Ajoutée *::after {
    1718 Ajoutée animation-duration: 0.01ms !important;
    1719 Ajoutée animation-iteration-count: 1 !important;
    1720 Ajoutée transition-duration: 0.01ms !important;
    1721 Ajoutée scroll-behavior: auto !important;
    1722 Ajoutée }
    1723 Ajoutée }
    1724 Ajoutée /* /validiscan:reduced-motion-guard */
  • Un bouton de pause pour les diaporamas animés slider-pause-control Le slider de base de Shopware, derrière le slider d’images du CMS, reçoit un bouton visible et traduit pour le mettre en pause et le relancer.

    Le slider de base de Shopware, celui qui se trouve derrière le slider d’images du CMS, reçoit un bouton visible et traduit pour le mettre en pause et le relancer, à côté de ses flèches précédente et suivante. Un bloc de code du script de la Storefront du thème relie le bouton au défilement automatique du slider, par le plugin de slider de Shopware lui-même. Lorsque le thème pilote le défilement automatique avec son propre minuteur, ce minuteur reçoit une ligne qui saute une étape tant que le slider est en pause. Le bouton reste masqué jusqu’à ce que le script l’ait relié : il n’apparaît donc jamais sans défilement automatique à mettre en pause. Pour les visiteurs dont l’appareil demande de réduire les mouvements, le slider démarre en pause. Les sliders construits avec d’autres bibliothèques sont laissés à une personne.

    Règles couvertes
    • vs-carousel AUTO
    Types de fichiers modifiés
    .html.twig.js.json
    adds a wired pause button to the hero slider; the carousel is no longer unstoppable Données d’exemple

    Resources/app/storefront/src/main.js Modifications apportées à Resources/app/storefront/src/main.js (lignes ajoutées : 52, lignes supprimées : 0)

    Modifications apportées à Resources/app/storefront/src/main.js (lignes ajoutées : 52, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    142 142 if (options.autoplay === true) {
    143 143 window.setInterval(
    144 144 function () {
    145 Ajoutée 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 Ajoutée
    458 Ajoutée /* validiscan:slider-pause-control */
    459 Ajoutée /*
    460 Ajoutée * Pause / play button of auto-advancing sliders (WCAG 2.2.2): wired to the
    461 Ajoutée * slider's autoplay; sliders start paused when the visitor prefers reduced motion.
    462 Ajoutée */
    463 Ajoutée (function sliderPauseControl() {
    464 Ajoutée 'use strict';
    465 Ajoutée
    466 Ajoutée function tinySlider(slider) {
    467 Ajoutée const manager = window.PluginManager;
    468 Ajoutée if (!manager || typeof manager.getPluginInstanceFromElement !== 'function') return null;
    469 Ajoutée const plugin = manager.getPluginInstanceFromElement(slider, 'BaseSlider');
    470 Ajoutée return plugin && plugin._slider ? plugin._slider : null;
    471 Ajoutée }
    472 Ajoutée
    473 Ajoutée function autoplays(slider) {
    474 Ajoutée try {
    475 Ajoutée const options = JSON.parse(slider.getAttribute('data-base-slider-options') || '{}') || {};
    476 Ajoutée return Boolean(options.slider && options.slider.autoplay);
    477 Ajoutée } catch {
    478 Ajoutée return false;
    479 Ajoutée }
    480 Ajoutée }
    481 Ajoutée
    482 Ajoutée function init() {
    483 Ajoutée const reduceMotion =
    484 Ajoutée window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    485 Ajoutée document.querySelectorAll('[data-slider-pause]').forEach(function (button) {
    486 Ajoutée const slider = button.closest('[data-base-slider]');
    487 Ajoutée if (!slider || !autoplays(slider)) return;
    488 Ajoutée
    489 Ajoutée function setPaused(paused) {
    490 Ajoutée slider.setAttribute('data-autoplay-paused', paused ? 'true' : 'false');
    491 Ajoutée button.textContent = button.getAttribute(paused ? 'data-label-play' : 'data-label-pause');
    492 Ajoutée const instance = tinySlider(slider);
    493 Ajoutée if (instance) instance[paused ? 'pause' : 'play']();
    494 Ajoutée }
    495 Ajoutée
    496 Ajoutée button.hidden = false;
    497 Ajoutée button.addEventListener('click', function () {
    498 Ajoutée setPaused(slider.getAttribute('data-autoplay-paused') !== 'true');
    499 Ajoutée });
    500 Ajoutée if (reduceMotion) setPaused(true);
    501 Ajoutée });
    502 Ajoutée }
    503 Ajoutée
    504 Ajoutée if (document.readyState === 'complete') init();
    505 Ajoutée else window.addEventListener('load', init);
    506 Ajoutée })();
    507 Ajoutée /* /validiscan:slider-pause-control */

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 6, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 6, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "slider": {
    199 Ajoutée "pause": "Diashow anhalten",
    200 Ajoutée "play": "Diashow abspielen"
    201 Ajoutée }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 6, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 6, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "slider": {
    199 Ajoutée "pause": "Pause slideshow",
    200 Ajoutée "play": "Play slideshow"
    201 Ajoutée }
    196 202 }
    197 203 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 1, lignes supprimées : 0)

    Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 1, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée <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>
  • Aucun son qui démarre tout seul mute-autoplay Une vidéo qui se lance toute seule est rendue muette et un son ne démarre plus tout seul, sauf la lecture automatique que définit le code d’un template ou un script et les lecteurs dans des cadres ; sur Shopware, la recette n’est pas utilisée pour les constats de vs-media.

    Une vidéo qui se lance toute seule reçoit muted et playsinline : elle continue ainsi de jouer en silence, comme une vidéo d’arrière-plan, et se lit dans la page sous Safari sur téléphone au lieu d’ouvrir le lecteur plein écran. Un son qui démarre tout seul ne le fait plus ; les visiteurs le lancent eux-mêmes. La lecture automatique que définit le code d’un template ou un script, ainsi que les lecteurs intégrés dans des cadres, sont laissés à une personne. Dans un scan Validiscan, le son qui démarre tout seul est signalé par le contrôle des médias vs-media de Validiscan, qui remplace la règle no-autoplay-audio d’axe-core. vs-media, qui couvre aussi les sous-titres et les transcriptions, est MANUAL : sur Shopware, un tel constat s’accompagne donc d’indications plutôt que de cette modification.

    Règles couvertes
    • no-autoplay-audio AUTO contrôle assuré par vs-media
    • vs-media MANUAL
    Types de fichiers modifiés
    .html.twig
    mutes a seeded autoplaying background video; vs-media no longer reports autoplay Données d’exemple

    Resources/views/storefront/block/cms-block-video.html.twig Modifications apportées à Resources/views/storefront/block/cms-block-video.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/block/cms-block-video.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    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 Supprimée <video class="cms-video" autoplay loop preload="none" width="1280" height="720" poster="{{ block.posterUrl }}">
    12 Ajoutée <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>

Formulaires, panier et contenu intégré

  • Des étiquettes reliées à leurs champs label-association Un champ dont le texte d’étiquette figure déjà sur la page, juste avant lui, est relié à ce texte.

    Un champ de formulaire dont le texte d’étiquette figure déjà sur la page, juste avant le champ, est relié à ce texte. Un <label> sans for en reçoit un qui pointe vers le champ. Un élément de texte que le thème désigne par sa classe comme un titre ou une étiquette, comme le nom d’un groupe de variantes sur une fiche produit, devient le <label> du champ, en gardant ses classes et sa propre ligne. Les lecteurs d’écran lisent alors l’étiquette lorsque le champ reçoit le focus. Un champ qui n’a qu’un texte indicatif (placeholder) est laissé à une personne, car rédiger une étiquette est une décision de contenu.

    Règles couvertes
    • label AUTO contrôle assuré par vs-forms-ecommerce
    • select-name ASSISTED
    • vs-forms-ecommerce AUTO
    Types de fichiers modifiés
    .html.twig
    turns the variant group titles into labels of their selects; select-name no longer fires Données d’exemple

    Resources/views/storefront/page/product-detail/configurator.html.twig Modifications apportées à Resources/views/storefront/page/product-detail/configurator.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/page/product-detail/configurator.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    13 13 {% for group in product.configurator %}
    14 14 {% if group.display == 'select' %}
    15 15 <div class="product-detail-configurator-group">
    16 Supprimée <div class="product-detail-configurator-group-title">{{ group.name }}</div>
    16 Ajoutée <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) %}
  • Des champs de compte, d’adresse et de contact que les navigateurs peuvent remplir autocomplete-tokens Les champs qui demandent des données propres au client, comme ceux des formulaires d’inscription, d’adresse et de connexion, reçoivent la valeur autocomplete qui correspond à ce qu’ils demandent, sauf si le code d’un template l’écrit.

    Les champs qui demandent des données propres au client, comme ceux des formulaires d’inscription, d’adresse, de connexion, de contact et de newsletter, reçoivent la valeur autocomplete qui correspond à ce qu’ils demandent, comme l’adresse e-mail, le prénom et le nom, le code postal, le numéro de téléphone ou le mot de passe. La valeur est déduite du type, du nom, de l’identifiant, de l’étiquette et du texte indicatif du champ. Les mots de passe reçoivent new-password dans les formulaires d’inscription et current-password ailleurs. Les champs de recherche, de code promo, de quantité, de captcha et de message restent tels quels, et une valeur que le code d’un template écrit est laissée à une personne. Les navigateurs peuvent alors remplir les champs, et les technologies d’assistance peuvent savoir à quoi sert chacun.

    Règles couvertes
    • autocomplete-valid AUTO contrôle assuré par vs-forms-ecommerce
    • vs-forms-ecommerce AUTO
    Types de fichiers modifiés
    .html.twig
    adds tokens to the register, address, login, contact and newsletter fields; no autocomplete hits remain Données d’exemple

    Resources/views/storefront/component/account/login.html.twig Modifications apportées à Resources/views/storefront/component/account/login.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/component/account/login.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    19 19 name="username"
    20 20 placeholder="{{ 'account.loginMailPlaceholder'|trans }}"
    21 21 required
    22 Supprimée autocomplete="off">
    22 Ajoutée autocomplete="email">
    23 23 </div>
    24 24
    25 25 <div class="form-group">

    Resources/views/storefront/component/account/register.html.twig Modifications apportées à Resources/views/storefront/component/account/register.html.twig (lignes ajoutées : 8, lignes supprimées : 4)

    Modifications apportées à Resources/views/storefront/component/account/register.html.twig (lignes ajoutées : 8, lignes supprimées : 4)
    Ligne avant Ligne après Modification Code
    33 33 id="{{ formId }}FirstName"
    34 34 name="firstName"
    35 35 placeholder="{{ 'account.firstNamePlaceholder'|trans }}"
    36 Supprimée required>
    36 Ajoutée required
    37 Ajoutée 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 Supprimée required>
    45 Ajoutée required
    46 Ajoutée 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 Supprimée required>
    57 Ajoutée required
    58 Ajoutée 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 Supprimée required>
    68 Ajoutée required
    69 Ajoutée 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 Modifications apportées à Resources/views/storefront/component/address/address-form.html.twig (lignes ajoutées : 8, lignes supprimées : 4)

    Modifications apportées à Resources/views/storefront/component/address/address-form.html.twig (lignes ajoutées : 8, lignes supprimées : 4)
    Ligne avant Ligne après Modification Code
    14 14 id="{{ prefix }}AddressStreet"
    15 15 name="{{ prefix }}[street]"
    16 16 placeholder="{{ 'address.streetPlaceholder'|trans }}"
    17 Supprimée required>
    17 Ajoutée required
    18 Ajoutée 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 Supprimée required>
    30 Ajoutée required
    31 Ajoutée 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 Supprimée required>
    42 Ajoutée required
    43 Ajoutée 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 Supprimée required>
    53 Ajoutée required
    54 Ajoutée 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 Modifications apportées à Resources/views/storefront/element/cms-element-form.html.twig (lignes ajoutées : 4, lignes supprimées : 4)

    Modifications apportées à Resources/views/storefront/element/cms-element-form.html.twig (lignes ajoutées : 4, lignes supprimées : 4)
    Ligne avant Ligne après Modification Code
    18 18 </select>
    19 19 </div>
    20 20 <div class="form-group col-md-4">
    21 Supprimée <input type="text" class="form-control" id="contactFormFirstName" name="firstName" placeholder="{{ 'account.firstNamePlaceholder'|trans }}" required>
    21 Ajoutée <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 Supprimée <input type="text" class="form-control" id="contactFormLastName" name="lastName" placeholder="{{ 'account.lastNamePlaceholder'|trans }}" required>
    24 Ajoutée <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 Supprimée <input type="email" class="form-control" id="contactFormMail" name="email" placeholder="{{ 'account.emailPlaceholder'|trans }}" required>
    31 Ajoutée <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 Supprimée <input type="tel" class="form-control" id="contactFormPhone" name="phone">
    35 Ajoutée <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 Modifications apportées à Resources/views/storefront/layout/footer/footer.html.twig (lignes ajoutées : 2, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/footer/footer.html.twig (lignes ajoutées : 2, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    62 62 id="footerNewsletterMail"
    63 63 class="form-control"
    64 64 placeholder="{{ 'footer.newsletterPlaceholder'|trans }}"
    65 Supprimée required>
    65 Ajoutée required
    66 Ajoutée 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">
  • Les changements de prix, de stock et de panier annoncés live-region-cart-variant Le prix, la disponibilité ou les totaux du panier que le script de la Storefront met à jour sur place deviennent des régions live que les lecteurs d’écran lisent sans interrompre, sauf si un script remplace l’élément lui-même.

    Les parties d’une page que le script de la Storefront met à jour sur place, comme le prix après le choix d’une variante par un client, la disponibilité ou les totaux du panier, deviennent des régions live en mode poli. Les lecteurs d’écran lisent alors la nouvelle valeur une fois, en entier, sans interrompre. Lorsqu’un script remplace l’élément lui-même au lieu de son contenu, une région live ne fonctionnerait pas : la recette laisse donc cet élément à une personne.

    Règles couvertes
    • vs-live-updates AUTO
    Types de fichiers modifiés
    .html.twig
    makes the variant price and the cart totals polite live regions; the updates are announced Données d’exemple

    Resources/views/storefront/page/checkout/cart/index.html.twig Modifications apportées à Resources/views/storefront/page/checkout/cart/index.html.twig (lignes ajoutées : 2, lignes supprimées : 2)

    Modifications apportées à Resources/views/storefront/page/checkout/cart/index.html.twig (lignes ajoutées : 2, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    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 Supprimée <div class="line-item-total-price">
    62 Ajoutée <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 Supprimée <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}">
    91 Ajoutée <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 Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    17 17 {% endblock %}
    18 18
    19 19 {% block buy_widget_price %}
    20 Supprimée <div class="product-detail-price-container">
    20 Ajoutée <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>
  • Des titres pour les cartes et les vidéos intégrées iframe-title Un cadre intégré sans titre reçoit un titre traduit qui dit ce qu’il montre, lorsque le code le révèle.

    Un cadre intégré sans titre, comme une carte ou une vidéo YouTube ou Vimeo, reçoit un titre traduit, tiré des fichiers de snippets du thème, qui dit ce qu’il montre. La recette déduit ce que montre le cadre de son adresse, de sa classe et de son nom, ou du template qui l’entoure. Un cadre dont le code ne révèle pas le contenu ne reçoit aucun titre de la recette, car un titre inventé est pire que pas de titre.

    Règles couvertes
    • frame-title AUTO
    Types de fichiers modifiés
    .html.twig.json
    titles the contact map frame through the snippets; frame-title no longer fires Données d’exemple

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 5, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 5, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "iframe": {
    199 Ajoutée "map": "Karte"
    200 Ajoutée }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 5, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 5, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "iframe": {
    199 Ajoutée "map": "Map"
    200 Ajoutée }
    196 201 }
    197 202 }

    Resources/views/storefront/block/cms-block-map.html.twig Modifications apportées à Resources/views/storefront/block/cms-block-map.html.twig (lignes ajoutées : 2, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/block/cms-block-map.html.twig (lignes ajoutées : 2, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    11 11 <iframe class="cms-map-frame"
    12 12 src="{{ url('/widgets/cms/map') }}"
    13 13 width="1280"
    14 Supprimée height="360"></iframe>
    14 Ajoutée height="360"
    15 Ajoutée 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, images et commandes qui demandent du jugement

Le contraste du texte, les alternatives textuelles des images et les noms des commandes qui n’en ont pas demandent généralement du jugement : quelle couleur convient à votre marque, quels mots décrivent une image. Les règles qui les signalent ont le niveau ASSISTED, comme le montrent les listes ci-dessous. Pour un constat d’une règle ASSISTED, l’IA ne rédige une modification que si vous avez accepté la rédaction par IA, et Validiscan relit chaque modification avant sa livraison.

Les quatre recettes ci-dessous ne sont la correction standard d’aucune règle. Chacune ne couvre que les cas précis où le code du thème suffit à lui seul à prouver quelle doit être la modification. Deux d’entre elles, button-semantics et icon-accessible-name, couvrent aussi le contrôle de diaporama vs-carousel, du niveau AUTO : dans un projet, le moteur de correction les essaie sur ses constats lorsque la recette du bouton de pause ci-dessus ne peut pas les corriger. Les deux autres ne couvrent que des règles du niveau ASSISTED.

  • Des couleurs de texte suffisamment contrastées contrast-tokens Une couleur de texte qui échoue au contrôle du contraste et qui est définie à un seul endroit passe à la nuance la plus proche de la même teinte qui atteint le contraste requis ; recette non appliquée dans un projet.

    Lorsqu’un texte échoue au contrôle du contraste et que sa couleur est définie à un seul endroit du thème, cet endroit passe à la nuance la plus proche de la même teinte qui atteint le contraste requis, de sorte que le design reste reconnaissable. Cet endroit peut être un champ de couleur de theme.json avec la variable SCSS qui le répète, une variable SCSS ou une propriété personnalisée utilisée uniquement pour du texte, ou une seule déclaration color. Une couleur utilisée à plusieurs endroits ou aussi comme arrière-plan ou comme bordure, ainsi que les champs de couleur de base de Shopware pour la marque, les bordures, les arrière-plans, les messages d’état et le bouton d’achat, sont laissés à une personne. Une couleur enregistrée dans les réglages du thème de l’Administration l’emporte sur theme.json : si elle y a été modifiée, c’est aussi là qu’il faut la modifier. La règle color-contrast a le niveau ASSISTED : dans un projet, un constat de contraste suit donc la voie ASSISTED et cette recette ne lui est pas appliquée.

    Règles couvertes
    • color-contrast ASSISTED
    Types de fichiers modifiés
    .scss
    darkens the unit price colour to 4.5:1 with the same hue; axe clears it on both pages Données d’exemple

    Resources/app/storefront/src/scss/_variant-broken.scss Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/app/storefront/src/scss/_variant-broken.scss (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    45 45 /* Light grey unit prices, 3.1:1 (color-contrast). */
    46 46 .product-price-unit,
    47 47 .product-detail-price-unit {
    48 Supprimée color: #929292;
    48 Ajoutée color: #757575;
    49 49 }
    50 50
    51 51 /* Fixed two-line product names (vs-text-spacing). */
  • Des images décoratives masquées aux lecteurs d’écran decorative-image-alt Seules les images dont le template prouve qu’elles sont décoratives reçoivent un alt vide ou aria-hidden ; recette non appliquée dans un projet.

    Une image n’est marquée comme décorative que lorsque le template prouve qu’elle n’apporte rien : elle se trouve dans un lien ou un bouton nommé autrement, c’est une icône placée à côté de sa propre étiquette textuelle, ou c’est un espaceur ou un séparateur. Un <img> reçoit un alt vide, une image affichée avec la balise sw_thumbnails de Shopware reçoit un alt vide dans ses attributs, et les icônes SVG reçoivent aria-hidden="true", de sorte que les lecteurs d’écran les ignorent au lieu de lire un nom de fichier. Toute autre image est du contenu, comme les logos de moyens de paiement dans un pied de page, et son alternative textuelle demande des mots qu’une personne choisit. Les règles que couvre cette recette ont le niveau ASSISTED : dans un projet, leurs constats suivent donc la voie ASSISTED et cette recette ne leur est pas appliquée.

    Règles couvertes
    • image-alt ASSISTED
    • role-img-alt ASSISTED
    • svg-img-alt ASSISTED
    Types de fichiers modifiés
    .html.twig
    marks a seeded phone icon inside the hotline link decorative; image-alt stops firing there Données d’exemple

    Resources/views/storefront/layout/footer/footer.html.twig Modifications apportées à Resources/views/storefront/layout/footer/footer.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/footer/footer.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    9 9 <div class="footer-column-content">
    10 10 <p class="footer-contact-hotline">
    11 11 {{ 'footer.serviceHotlineText'|trans }}
    12 Supprimée <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 Ajoutée <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">
  • Des commandes réservées à la souris transformées en boutons button-semantics Les commandes des sliders et des bandeaux cookies qui ne réagissent qu’à la souris deviennent de vrais boutons et gardent leurs classes, leurs attributs data et leurs id ; dans un projet, la recette est essayée sur les constats de diaporama de vs-carousel.

    Les éléments cliquables <div>, <span>, <i> et <li> des sliders et des bandeaux cookies, ainsi que les liens sans adresse, qui ne réagissent qu’à la souris deviennent de vrais boutons. Les classes, les attributs data et les id restent, de sorte que le script du thème les retrouve comme avant, et les touches Entrée et Espace font désormais ce que fait un clic. Un élément de liste garde sa place dans la liste et reçoit le bouton à l’intérieur. Une commande sans texte reçoit un nom traduit d’après sa fonction, par exemple « Show slide 2 », et une petite réinitialisation de style conserve son apparence. Une commande sans texte dont la fonction n’est pas claire, ou dont la mise en forme dépend de sa balise ou de sa position, est laissée à une personne. Dans un projet, cette recette est essayée sur les constats de diaporama de vs-carousel ; les constats du contrôle du bandeau cookies vs-cookie-banner ont le niveau ASSISTED et suivent la voie ASSISTED.

    Règles couvertes
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Types de fichiers modifiés
    .html.twig.json.scss
    turns the mouse-only slider dots into named buttons in their list items; vs-carousel stops reporting them Données d’exemple

    Resources/app/storefront/src/scss/base.scss Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 17, lignes supprimées : 0)

    Modifications apportées à Resources/app/storefront/src/scss/base.scss (lignes ajoutées : 17, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Ajoutée
    1712 Ajoutée /* validiscan:button-semantics */
    1713 Ajoutée /* Former <div>/<span>/<li> controls, now <button>: drop the browser button styles, keep the theme's (no specificity). */
    1714 Ajoutée :where(button.base-slider-dot) {
    1715 Ajoutée display: block;
    1716 Ajoutée margin: 0;
    1717 Ajoutée padding: 0;
    1718 Ajoutée border: 0;
    1719 Ajoutée background: none;
    1720 Ajoutée color: inherit;
    1721 Ajoutée font: inherit;
    1722 Ajoutée line-height: inherit;
    1723 Ajoutée text-align: inherit;
    1724 Ajoutée cursor: pointer;
    1725 Ajoutée appearance: none;
    1726 Ajoutée }
    1727 Ajoutée /* /validiscan:button-semantics */

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 5, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 5, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "showSlide": "Folie %index% anzeigen"
    200 Ajoutée }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 5, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 5, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "showSlide": "Show slide %index%"
    200 Ajoutée }
    196 201 }
    197 202 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    51 51 </button>
    52 52 <ol class="base-slider-dots">
    53 53 {% for slide in slides %}
    54 Supprimée <li class="base-slider-dot{% if loop.first %} is-active{% endif %}" data-slide-to="{{ loop.index0 }}"></li>
    54 Ajoutée <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>
  • Des noms pour les boutons et les liens qui n’ont qu’une icône icon-accessible-name Les boutons et les liens qui n’affichent qu’une icône reçoivent un nom traduit qui dit ce qu’ils font, lorsque le code révèle leur fonction ; dans un projet, la recette est essayée sur les commandes de diaporama que signale vs-carousel.

    Les boutons et les liens qui n’affichent qu’une icône, comme la recherche, le panier, le compte, le lien du logo, les flèches de slider, les boutons de quantité et les choix de cookies, reçoivent un aria-label traduit qui dit ce qu’ils font. La fonction est déduite des classes de la commande, de la cible de son lien, de l’icône qu’elle contient et du composant qui l’entoure. Une commande dont le code ne révèle pas la fonction, comme un lien de réseau social dont le réseau est stocké sous forme de données, est laissée à une personne, car un nom inventé est pire que pas de nom. Dans un projet, cette recette est essayée sur les commandes de diaporama que signale vs-carousel ; les autres commandes qui n’ont qu’une icône sont signalées par des règles du niveau ASSISTED et suivent la voie ASSISTED.

    Règles couvertes
    • button-name ASSISTED
    • link-name ASSISTED
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Types de fichiers modifiés
    .html.twig.json
    names every icon-only button of the home, product and cart pages; button-name no longer fires Données d’exemple

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 12, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 12, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "search": "Suchen",
    200 Ajoutée "previousSlide": "Vorherige Folie",
    201 Ajoutée "nextSlide": "Nächste Folie",
    202 Ajoutée "cookieSettings": "Cookie-Einstellungen",
    203 Ajoutée "acceptCookies": "Alle Cookies akzeptieren",
    204 Ajoutée "decreaseQuantity": "Menge verringern",
    205 Ajoutée "increaseQuantity": "Menge erhöhen",
    206 Ajoutée "removeFromCart": "Aus dem Warenkorb entfernen"
    207 Ajoutée }
    196 208 }
    197 209 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 12, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 12, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "search": "Search",
    200 Ajoutée "previousSlide": "Previous slide",
    201 Ajoutée "nextSlide": "Next slide",
    202 Ajoutée "cookieSettings": "Cookie settings",
    203 Ajoutée "acceptCookies": "Accept all cookies",
    204 Ajoutée "decreaseQuantity": "Decrease quantity",
    205 Ajoutée "increaseQuantity": "Increase quantity",
    206 Ajoutée "removeFromCart": "Remove from cart"
    207 Ajoutée }
    196 208 }
    197 209 }

    Resources/views/storefront/element/cms-element-image-gallery.html.twig Modifications apportées à Resources/views/storefront/element/cms-element-image-gallery.html.twig (lignes ajoutées : 2, lignes supprimées : 2)

    Modifications apportées à Resources/views/storefront/element/cms-element-image-gallery.html.twig (lignes ajoutées : 2, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    31 31
    32 32 {% if media|length > 1 %}
    33 33 <div class="gallery-slider-controls">
    34 Supprimée <button type="button" class="base-slider-controls-prev gallery-slider-controls-prev" data-gallery-prev>
    34 Ajoutée <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 Supprimée <button type="button" class="base-slider-controls-next gallery-slider-controls-next" data-gallery-next>
    37 Ajoutée <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 Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 2, lignes supprimées : 2)

    Modifications apportées à Resources/views/storefront/element/cms-element-image-slider.html.twig (lignes ajoutées : 2, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    46 46
    47 47 {% block element_image_slider_controls %}
    48 48 <div class="image-slider-controls-container">
    49 Supprimée <button type="button" class="base-slider-controls-prev image-slider-controls-btn" data-slide-prev>
    49 Ajoutée <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 Supprimée <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next>
    57 Ajoutée <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 Modifications apportées à Resources/views/storefront/layout/cookie/cookie-permission.html.twig (lignes ajoutées : 2, lignes supprimées : 2)

    Modifications apportées à Resources/views/storefront/layout/cookie/cookie-permission.html.twig (lignes ajoutées : 2, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    22 22 </p>
    23 23 </div>
    24 24 <div class="col-auto cookie-permission-actions">
    25 Supprimée <button type="button" class="btn cookie-permission-icon-btn js-cookie-configuration-toggle">{% sw_icon 'settings' %}</button>
    26 Supprimée <button type="button" class="btn btn-primary cookie-permission-icon-btn js-cookie-accept-all-button">{% sw_icon 'checkmark' %}</button>
    25 Ajoutée <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 Ajoutée <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 Modifications apportées à Resources/views/storefront/layout/header/search.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/header/search.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    13 13 autocomplete="off"
    14 14 autocapitalize="off"
    15 15 placeholder="{{ 'header.searchPlaceholder'|trans }}" value="{{ page.searchTerm }}">
    16 Supprimée <button type="submit" class="btn header-search-btn">
    16 Ajoutée <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 Modifications apportées à Resources/views/storefront/page/checkout/cart/index.html.twig (lignes ajoutées : 3, lignes supprimées : 3)

    Modifications apportées à Resources/views/storefront/page/checkout/cart/index.html.twig (lignes ajoutées : 3, lignes supprimées : 3)
    Ligne avant Ligne après Modification Code
    39 39 </div>
    40 40 <div class="line-item-quantity">
    41 41 <div class="input-group quantity-selector-group" data-quantity-selector="true">
    42 Supprimée <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    42 Ajoutée <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 Supprimée <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    53 Ajoutée <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 Supprimée <button type="button" class="btn btn-light btn-sm line-item-remove-button">
    67 Ajoutée <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 Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 2, lignes supprimées : 2)

    Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 2, lignes supprimées : 2)
    Ligne avant Ligne après Modification Code
    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 Supprimée <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    67 Ajoutée <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 Supprimée <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    78 Ajoutée <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 Données d’exemple

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 6, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json (lignes ajoutées : 6, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "home": "Zur Startseite",
    200 Ajoutée "account": "Ihr Konto"
    201 Ajoutée }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 6, lignes supprimées : 0)

    Modifications apportées à Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json (lignes ajoutées : 6, lignes supprimées : 0)
    Ligne avant Ligne après Modification Code
    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 Ajoutée },
    197 Ajoutée "a11y": {
    198 Ajoutée "control": {
    199 Ajoutée "home": "Go to the homepage",
    200 Ajoutée "account": "Your account"
    201 Ajoutée }
    196 202 }
    197 203 }

    Resources/views/storefront/layout/header/actions.html.twig Modifications apportées à Resources/views/storefront/layout/header/actions.html.twig (lignes ajoutées : 2, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/header/actions.html.twig (lignes ajoutées : 2, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    3 3 {% block layout_header_actions_account %}
    4 4 <div class="header-account">
    5 5 <a href="{{ url('/account/login') }}"
    6 Supprimée class="btn account-menu-btn header-actions-btn">
    6 Ajoutée class="btn account-menu-btn header-actions-btn"
    7 Ajoutée 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 Modifications apportées à Resources/views/storefront/layout/header/logo.html.twig (lignes ajoutées : 1, lignes supprimées : 1)

    Modifications apportées à Resources/views/storefront/layout/header/logo.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
    Ligne avant Ligne après Modification Code
    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 Supprimée <a class="header-logo-main-link" href="{{ url('/') }}">
    5 Ajoutée <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') }}"
Détails : les niveaux, le thème de test, les contrôles de chaque modification et le texte dans les langues de votre boutique

Une recette est une modification déterministe pour un type d’échec : le même code reçoit toujours la même modification. Validiscan dispose de 20 recettes pour les thèmes Shopware. Chacune n’intervient que là où le code du thème montre quelle modification est la bonne. Là où il ne le montre pas, la recette ne change rien. Elle ne devine pas.

Sous chaque recette figurent les règles qu’elle couvre, avec le niveau de chaque règle dans le catalogue de Validiscan pour Shopware. AUTO signifie qu’une recette est la correction standard de la règle. ASSISTED signifie que la correction demande du jugement : une modification est donc rédigée, puis relue. MANUAL signifie que la règle demande la décision d’une personne et s’accompagne d’indications, et non d’une modification du code. Dans un projet, les recettes sont appliquées aux constats des règles du niveau AUTO. Lorsqu’une recette énumère aussi une règle du niveau ASSISTED ou MANUAL, un constat de cette règle suit à la place la voie ASSISTED ou MANUAL. Une règle dont le contrôle est assuré par un autre contrôle n’est pas exécutée seule lors d’un scan : cet autre contrôle signale le même échec.

Les types de fichiers et l’avant et l’après de chaque recette proviennent des tests internes de Validiscan. Ils s’exécutent sur le thème de test Shopware de Validiscan, un plugin de thème construit sur la Storefront par défaut de Shopware 6, dans lequel des échecs d’accessibilité ont été introduits volontairement. Chaque test génère les pages à partir des templates du thème avant et après la modification et exécute la même règle sur les deux : l’échec doit être signalé avant et ne plus l’être après, et la modification ne doit pas provoquer de nouvel échec de cette règle. Les pages sont produites par un moteur de rendu de test, et non par une installation complète de Shopware ; les fichiers de votre thème seront différents.

Avant que Validiscan ne relise une modification, chaque fichier modifié est contrôlé. Les templates Twig sont analysés avec les balises propres à Shopware, comme sw_extends et sw_include, que l’analyseur connaît ; ils ne sont jamais compilés ni exécutés. Les feuilles de style subissent un contrôle de structure, les fichiers de snippets et theme.json sont analysés comme du JSON, et le JavaScript subit un contrôle de syntaxe.

Lorsqu’une recette ajoute un texte visible ou un nom, comme les mots d’un lien d’évitement ou le nom d’un bouton à icône, elle utilise un snippet dans le template. Le texte va dans le fichier de snippets anglais du thème, que la recette crée si le thème n’en a pas, et dans son fichier de snippets allemand lorsque le thème en possède un. Les snippets qui existent déjà conservent leur texte. Aucun texte n’est ajouté pour les autres langues de votre boutique.

Qu’est-ce qui reste MANUAL sur Shopware ?

Les échecs qui exigent une décision sur le contenu ou le comportement qu’une modification de code prédéfinie ne peut pas prendre, par exemple les sous-titres et transcriptions de vos vidéos et contenus audio, restent MANUAL sur Shopware. Pour un constat d’une règle MANUAL, Validiscan donne des indications pour le corriger, et non une modification de votre code.

Les règles ci-dessous sont exécutées lors d’un scan et ont le niveau MANUAL sur Shopware.

  • meta-refresh L’actualisation différée de moins de 20 heures ne doit pas être utilisée
  • nested-interactive Les éléments interactifs ne doivent pas être imbriqués
  • scrollable-region-focusable Les zones défilantes doivent être accessibles au clavier
  • server-side-image-map Les images réactives côté serveur ne doivent pas être utilisées
  • vs-keyboard-trap Le focus clavier ne doit jamais rester piégé
  • vs-focus-order L’ordre de tabulation doit suivre l’ordre visuel
  • vs-dragging Les éléments à faire glisser doivent offrir une alternative à pointeur unique
  • vs-media Les vidéos ont besoin de sous-titres, l’audio d’une transcription, et rien ne doit démarrer automatiquement avec le son
  • vs-documents Les documents liés nécessitent une vérification manuelle de l’accessibilité
Détails : ce que reçoit un constat MANUAL, et ce qu’un projet laisse de côté

Pour un constat d’une règle MANUAL, Validiscan donne des indications sur la façon de le corriger, et non une modification de votre code. Ces échecs exigent une décision sur le contenu ou le comportement qu’une modification de code prédéfinie ne peut pas prendre, par exemple les sous-titres et les transcriptions de vos vidéos et de vos contenus audio, un moyen de sortir d’un widget qui piège le focus clavier, ou un second moyen d’utiliser une commande qui ne fonctionne qu’en la faisant glisser.

Le travail sur les constats MANUAL qui exigent vos propres décisions de contenu ne fait pas partie d’un projet, pas plus que les PDF. Ce que le scan teste automatiquement et ce qui est vérifié à la main

Que devez-vous fournir ?

Vous fournissez votre autorisation d’un scan complet, une voie d’accès à votre plugin de thème, votre réponse sur la rédaction par IA des corrections ASSISTED, un développeur qui relit les modifications, compile et déploie le thème, certains réglages de l’Administration, les nouveaux textes dans d’autres langues que l’anglais et l’allemand, et vos décisions sur ce qui reste MANUAL.

  1. Votre autorisation pour un scan complet de votre boutique, que Validiscan enregistre avant le scan. Un scan complet met un produit dans le panier et ouvre la première étape du tunnel de commande, sans finaliser d’achat ni saisir de données de paiement.
  2. Une voie d’accès à votre thème : l’accès au dépôt GitHub qui contient le plugin de thème, ou un ZIP du dossier du plugin sous custom/plugins, téléversé dans le portail client.
  3. Votre réponse à la question de savoir si l’IA peut rédiger des corrections du niveau ASSISTED. La rédaction par IA reste désactivée tant que vous n’avez pas donné votre accord.
  4. Un développeur qui relit les modifications, fusionne la pull request ou applique le lot de correctifs, compile le thème et le déploie.
  5. Les réglages de l’Administration de Shopware que les corrections ne modifient pas : les méta-titres des pages qui n’en ont pas, la langue d’un contenu rédigé dans une autre langue que celle de votre boutique, et une couleur enregistrée dans les réglages du thème lorsqu’une correction de contraste modifie cette couleur.
  6. Les textes pour les langues de votre boutique autres que l’anglais et l’allemand, lorsqu’une correction ajoute un nouveau texte.
  7. Vos décisions sur ce qui reste MANUAL, comme les sous-titres de vos vidéos.
Détails : une fois les modifications en ligne, et ce que couvre un projet

Une fois les modifications en ligne, un scan de vérification contrôle de nouveau les pages concernées, et le Remediation Evidence Package consigne le résultat. Le déroulement d’un projet, étape par étape

Un projet couvre une boutique : un domaine et un thème. Il vaut pour une vitrine construite sur un thème ; une vitrine headless est traitée sur demande.

Sources

2 sources, toutes vérifiées pour cette page le 6 octobre 2026.

Documentation de Shopware pour les développeurs 2
  1. Add SCSS Styling and JavaScript to a Theme · Shopware · vérifiée le
  2. Add custom JavaScript · Shopware · vérifiée le

Commencez par une vérification gratuite de votre boutique.

Envoyez l’adresse de votre boutique. Validiscan la scanne et vous envoie chaque échec que le scan peut prouver, avec sa page et son code.

Demander une vérification gratuite

Ou écrivez à info@validiscan.com.

Ce que couvre la vérification gratuite, et ce qu’elle laisse de côté
Vous envoyez
l’adresse de votre boutique
Pages scannées
jusqu’à 15
Vous recevez
chaque échec que le scan peut prouver, avec la page et le code
Sur votre boutique
pages publiques uniquement ; aucune connexion, aucun achat, aucun formulaire envoyé
Ce qu’elle n’est pas
un audit complet

Écrire à Validiscan

Pour une vérification gratuite, indiquez l’adresse de votre boutique et sa plateforme.

Validiscan utilise ce que vous envoyez pour vous répondre. Politique de confidentialité