Korrekturen zur Barrierefreiheit im Code Ihres Themes für Shopware

Validiscan behebt Fehler der Barrierefreiheit in den Twig-Templates, im SCSS, im JavaScript der Storefront und in den Snippet-Dateien Ihres Shopware-6-Theme-Plugins. Nichts gelangt in Ihren Live-Shop, bevor Ihr Entwicklungsteam die Änderungen zusammenführt oder einspielt und das Theme bereitstellt.

Zuletzt aktualisiert:

Was ändert Validiscan in einem Shopware-Theme?

Validiscan ändert nur Dateien in den Ordnern, in denen ein Storefront-Theme für Shopware 6 seine Quellen ablegt, Resources/views, Resources/app/storefront/src und Resources/snippet, sowie seine theme.json. Eine Änderung an jeder anderen Datei besteht die Validierung nicht und wird nie ausgeliefert, und keine Korrektur installiert ein Plugin oder fügt ein Overlay-Skript hinzu.

Einstellungen, die Sie in der Administration von Shopware pflegen, etwa dort gespeicherte Meta-Titel oder Theme-Farben, sind keine Dateien des Themes, daher ändern die Korrekturen sie nicht. Ein Projekt gilt für eine Domain und ein Theme; eingebundene Dienste von Drittanbietern wie Zahlungs- oder Chat-Widgets sind darin nicht enthalten.

Details: die Ordner, wie eine Änderung aussieht, und die zwei Rezepte, die das Storefront-Skript ändern

Validiscan ändert nur Dateien in den Ordnern, in denen ein Storefront-Theme für Shopware 6 seine Quellen ablegt: die Twig-Templates in Resources/views, die SCSS- und JavaScript-Quellen in Resources/app/storefront/src, die Snippet-Dateien mit den Texten der Storefront in Resources/snippet und die theme.json des Themes. Eine Änderung an jeder anderen Datei besteht die Validierung nicht, etwa an den kompilierten Dateien in Resources/app/storefront/dist oder am PHP-Code und an der Konfiguration des Plugins. Eine Änderung, die die Validierung nicht besteht, kann nicht freigegeben werden und wird deshalb nie ausgeliefert.

Jede Änderung ist eine gewöhnliche Bearbeitung einer Theme-Datei, die Ihr Entwicklungsteam Zeile für Zeile lesen kann. Kleine Änderungen, etwa ein hinzugefügtes Attribut oder ein korrigierter Farbwert, werden dort vorgenommen, wo der Code schon steht. Neue Style-Regeln und neue Skriptblöcke stehen zwischen Kommentaren, die das Rezept nennen, zum Beispiel /* validiscan:focus-visible-styles */, und lassen sich deshalb leicht finden. Zwei Rezepte ändern das eigene Storefront-Skript des Themes: Das eine verbindet eine Pause-Schaltfläche mit dem Bilderslider, das andere verhindert, dass das automatische Abspielen eines Skripts startet, wenn Besucherinnen und Besucher weniger Bewegung wünschen. Keine Korrektur installiert ein Plugin oder fügt ein Skript hinzu, das Ihre Seiten nach dem Laden korrigiert, wie es ein Overlay tut.

Wie erhält Validiscan Zugriff auf Ihr Shopware-Theme?

Über ein GitHub-Repository oder als ZIP des Theme-Plugins, das im Kundenportal hochgeladen wird, denn Validiscan bereitet jede Korrektur an einer Kopie des Quellcodes Ihres Shopware-Theme-Plugins vor. Welcher Weg passt, hängt davon ab, wo der Code liegt: Ein Repository erhält einen Pull Request, ein ZIP ein Patch-Paket.

Die zwei Wege in ein Shopware-Theme-Plugin
Weg Was Sie geben Die Änderungen kommen zurück als
Über Ihr GitHub-Repository Zugriff auf das Repository, in dem das Theme-Plugin liegt, über die GitHub App von Validiscan oder ein Zugriffstoken Ein Pull Request
Aus einem ZIP des Theme-Plugins Den Ordner des Theme-Plugins unter custom/plugins als eine ZIP-Datei von bis zu 100 MB, im Kundenportal hochgeladen von einem Admin Ihrer Organisation Ein Patch-Paket

Über Ihr GitHub-Repository

Liegt der Code Ihres Theme-Plugins in einem GitHub-Repository, geben Sie Validiscan Zugriff auf dieses Repository: Sie installieren die GitHub App von Validiscan darauf oder geben Validiscan ein Zugriffstoken dafür. Die Einrichtung von Validiscan legt die Berechtigungen der App fest: Lese- und Schreibzugriff auf die Inhalte und die Pull Requests des Repositorys, Lesezugriff auf seine Metadaten und sonst nichts. Ein Token braucht dieselben Berechtigungen für dieses eine Repository.

Das Theme-Plugin kann auf der obersten Ebene des Repositorys liegen oder tiefer darin, etwa in custom/plugins/YourTheme/src/Resources: Die Rezepte finden die Dateien des Themes anhand der Ordnerkonventionen von Shopware. Die Änderungen kommen dann als Pull Request.

Aus einem ZIP des Theme-Plugins

Liegt der Code nicht in einem GitHub-Repository, erstellt Ihr Entwicklungsteam ein ZIP des Theme-Plugins. Die Anleitung des Kundenportals für Shopware lautet: „Bitten Sie Ihr Entwicklungsteam um ein ZIP Ihres Theme-Plugins: seinen Ordner unter custom/plugins, so wie er in Ihrem Shop installiert ist.“ Ein Admin Ihrer Organisation lädt die Datei anschließend im Kundenportal hoch, als eine einzige ZIP-Datei von bis zu 100 MB. Validiscan bereitet die Änderungen auf dieser Kopie Ihres Themes vor und schickt sie als Patch-Paket zurück.

Ohne einen der beiden Wege hat Validiscan keinen Code, den es ändern könnte, und es wird keine Korrektur vorbereitet.

Wie gelangen die Änderungen in Ihren Shop?

Die Änderungen kommen als Pull Request oder als Patch-Paket, und Ihr Entwicklungsteam entscheidet, wann sie live gehen. Validiscan liefert nur Änderungen aus, die es geprüft und freigegeben hat, und ändert nie das Theme oder den Code Ihres Live-Shops auf Shopware; die Auslieferung als unveröffentlichte Kopie des Themes gibt es nur auf Shopify.

Von der Auslieferung bis in Ihren Live-Shop, in fünf Schritten
  1. Validiscan liefert die Änderungen aus, die es geprüft und freigegeben hat: einen Pull Request auf einem neuen Branch oder ein Patch-Paket für Ihr Entwicklungsteam.
  2. Ihr Entwicklungsteam prüft die Änderungen. Bei einem Pull Request bittet seine Checkliste um eine Vorschau- oder Staging-Umgebung, nicht den Live-Shop, und um eine Prüfung der betroffenen Seiten, auch allein mit der Tastatur.
  3. Ihr Entwicklungsteam führt den Pull Request zusammen oder spielt das Patch-Paket ein.
  4. Ihr Entwicklungsteam baut das Theme: Änderungen am SCSS werden wirksam, sobald das Theme erneut kompiliert wird, Änderungen am JavaScript, sobald die Storefront erneut gebaut wird.
  5. Ihr Entwicklungsteam stellt das Theme bereit. Sobald die Änderungen live sind, prüft ein Verifizierungsscan die betroffenen Seiten erneut.

Ein Pull Request

Mit Zugriff auf Ihr Repository legt Validiscan einen neuen Branch mit dem Namen validiscan/a11y-<date>-<id> an, committet jede Korrektur einzeln und öffnet einen Pull Request mit einer Übersichtstabelle, einer Erklärung jeder Änderung und einer Checkliste für die Prüfung. In Ihren Standard-Branch wird nichts übertragen. Die Checkliste bittet Ihr Entwicklungsteam, den Branch in einer Vorschau- oder Staging-Umgebung bereitzustellen, nicht im Live-Shop, und die betroffenen Seiten dort vor dem Zusammenführen zu prüfen, auch allein mit der Tastatur.

Ein Patch-Paket

Aus dem hochgeladenen ZIP bereitet Validiscan ein ZIP-Archiv vor und schickt es Ihrem Entwicklungsteam. Es enthält die PATCH.md mit jeder Änderung und der Anleitung, wie sie eingespielt wird, alle Änderungen in einer Patch-Datei, die sich mit git apply einspielen lässt, eine Patch-Datei je Korrektur, die vollständigen geänderten Dateien und eine README.md. Ihr Entwicklungsteam spielt die Änderungen ein, testet sie und stellt das Theme bereit. Was ein Pull Request und ein Patch-Paket enthalten

Das Theme bauen

Ein Shopware-Shop liefert die SCSS- und JavaScript-Quellen des Themes nicht so aus, wie sie sind: Sie werden zuvor gebaut. Änderungen am SCSS werden wirksam, sobald das Theme erneut kompiliert wird, etwa mit bin/console theme:compile, wie die Entwicklerdokumentation von Shopware zum Styling von Themes es beschreibt. Änderungen am JavaScript der Storefront werden wirksam, sobald die Storefront erneut gebaut wird, wie in der Dokumentation von Shopware zum JavaScript der Storefront beschrieben. Ihr Entwicklungsteam führt diese Schritte aus, wenn es das geänderte Theme bereitstellt. Wie Korrekturen zur Barrierefreiheit in ein Shopware-Theme gelangen und was ein Theme-Update rückgängig machen kann

Was ändern die Rezepte in einem Shopware-Theme?

Jedes der 20 Rezepte von Validiscan für Shopware-Themes ist eine deterministische Änderung für eine Art von Fehler: Derselbe Code erhält immer dieselbe Änderung. Ein Rezept ändert nur dort etwas, wo der Code des Themes zeigt, welche Änderung die richtige ist; wo er es nicht zeigt, ändert es nichts.

Seitenstruktur und Navigation

  • Seitensprache im Basis-Layout html-lang Das <html>-Element des Basis-Layouts erhält ein gültiges lang-Attribut, wo es fehlt oder wo das Theme einen Locale-Code wie en_GB ausgibt.

    Das <html>-Element des Basis-Layouts der Storefront erhält ein gültiges lang-Attribut, sodass Screenreader die Seite in der richtigen Sprache aussprechen. Fehlt das Attribut, fügt das Rezept den Code der aktiven Sprache des Shops hinzu, zum Beispiel de-DE, den Wert, den das Basis-Template von Shopware selbst ausgibt. Gibt das Theme einen Locale-Code wie en_GB aus, der kein gültiges Sprach-Tag ist, wandelt das Template ihn jetzt in en-GB um. Eine Seite, deren Inhalt in einer anderen Sprache verfasst ist als der Shop, etwa ein deutscher Rechtstext in einem englischen Shop, bleibt Ihnen überlassen: Die Sprache dieses Inhalts wird in der Administration festgelegt oder im Text selbst ausgezeichnet.

    Abgedeckte Regeln
    • html-has-lang AUTO geprüft durch vs-lang
    • html-lang-valid AUTO geprüft durch vs-lang
    • vs-lang AUTO
    Geänderte Dateitypen
    .html.twig
    normalises the locale code of the base layout; German content pages are left to the merchant Beispieldaten

    Resources/views/storefront/base.html.twig Änderungen an Resources/views/storefront/base.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/base.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    9 9 {% block base_doctype %}<!DOCTYPE html>{% endblock %}
    10 10
    11 11 {% block base_html %}
    12 Entfernt <html lang="{{ page.lang }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    12 Hinzugefügt <html lang="{{ page.lang|replace({'_': '-'}) }}" itemscope="itemscope" itemtype="https://schema.org/WebPage">
    13 13 {% endblock %}
    14 14
    15 15 {% block base_head %}
  • Ein Titel auf jeder Seite document-title-template Schreibt das Layout nur den Meta-Titel in den <title>, erhält er den Namen des Shops als Ersatzwert, sodass keine Seite einen leeren Titel hat.

    Schreibt das Layout des Themes nur den Meta-Titel der Seite in den <title>, erhalten Kategorien und CMS-Seiten ohne Meta-Titel einen leeren Titel. Das Rezept behält den eigenen Titel des Themes und gibt ihm einen Ersatzwert: den Namen des Shops, übernommen von der Stelle, an der das Theme ihn schon ausgibt, oder, wenn es das nicht tut, aus dem Shopnamen in den Grundeinstellungen von Shopware. Keine Seite hat dann einen leeren Titel. Ein Titel, der nur aus dem Namen des Shops besteht, sagt trotzdem nicht, worum es auf der Seite geht; diese Seiten brauchen deshalb einen Meta-Titel in der Administration. Titel, die schon einen Ersatzwert oder einen festen Text haben, bleiben unverändert.

    Abgedeckte Regeln
    • document-title AUTO
    Geänderte Dateitypen
    .html.twig
    gives the layout title a shop-name fallback and the empty CMS title is gone Beispieldaten

    Resources/views/storefront/layout/meta.html.twig Änderungen an Resources/views/storefront/layout/meta.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/meta.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    36 36 {% endblock %}
    37 37
    38 38 {% block layout_head_title %}
    39 Entfernt <title>{{ page.metaTitle }}</title>
    39 Hinzugefügt <title>{{ page.metaTitle|default(shop.name) }}</title>
    40 40 {% endblock %}
    41 41
    42 42 {% block layout_head_stylesheet %}
  • Überschriften ohne übersprungene Ebenen heading-levels Eine Überschrift, die eine Ebene überspringt, rückt mit den weiteren Überschriften des Templates auf dieser oder einer tieferen Ebene auf und behält ihr bisheriges Aussehen.

    Eine Überschrift, die eine Ebene überspringt, etwa ein h3 direkt nach einem h1, rückt zur nächsten Ebene auf, und die weiteren Überschriften desselben Templates auf dieser oder einer tieferen Ebene rücken mit, sodass die eigene Gliederung des Templates erhalten bleibt. Legt das Template das Tag der Überschrift in einer Variablen fest, wird die Variable geändert. Klassen bleiben erhalten, und wo keine Klasse die Größe festlegt, bewahrt eine Bootstrap-Überschriftenklasse das bisherige Aussehen. Auf einer Seite ohne Hauptüberschrift wird das Element, das in ihrem eigenen Template schon als ihr Titel dient, zum h1 gemacht; sein Aussehen bleibt erhalten. Hat eine Seite kein solches Element, fügt das Rezept keine Überschrift hinzu, weil eine neue Überschrift einen Wortlaut braucht.

    Abgedeckte Regeln
    • heading-order AUTO geprüft durch vs-headings-landmarks
    • page-has-heading-one AUTO geprüft durch vs-headings-landmarks
    • vs-headings-landmarks AUTO
    Geänderte Dateitypen
    .html.twig
    moves a static grid title from h3 to h2; the skipped level is gone Beispieldaten

    Resources/views/storefront/block/cms-block-product-grid.html.twig Änderungen an Resources/views/storefront/block/cms-block-product-grid.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/block/cms-block-product-grid.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <h3 class="cms-block-title">{{ block.title }}</h3>
    8 Hinzugefügt <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 } %}

Tastaturbedienung und Fokus

  • Ein sichtbarer Fokusrahmen focus-visible-styles Ein Fokusrahmen für die Tastaturbedienung in der Markenfarbe des Themes oder der nächsten Abstufung desselben Farbtons mit einem Kontrast von mindestens 3:1 gegenüber dem Seitenhintergrund.

    Das Haupt-Stylesheet erhält einen Fokusrahmen für die Tastaturbedienung: 2 Pixel breit, durchgezogen und 2 Pixel vom Element entfernt, in der Markenfarbe des Themes. Ist diese Farbe zu hell, wird die nächste Abstufung desselben Farbtons verwendet, die gegenüber dem Seitenhintergrund einen Kontrast von mindestens 3:1 hat. Regeln des Themes, die den Fokusrahmen beim Fokussieren entfernen, gelten nun nur noch für den Fokus per Maus: Ein Klick behält das Aussehen des Themes, und beim Tastaturfokus erscheint der Fokusrahmen. Regeln, die den Fokus auf andere Weise zeigen, bleiben erhalten. Auf dunklen Flächen, etwa einer Kopfleiste, braucht der Fokusrahmen möglicherweise eine hellere Farbe, was Ihr Entwicklungsteam in der Vorschau prüft.

    Abgedeckte Regeln
    • vs-focus-visible AUTO
    Geänderte Dateitypen
    .scss
    adds a brand-coloured :focus-visible ring and scopes the outline resets Beispieldaten

    Resources/app/storefront/src/scss/_variant-broken.scss Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 5 hinzugefügt, 5 entfernt

    Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 5 hinzugefügt, 5 entfernt
    Zeile vorher Zeile nachher Änderung Code
    21 21 }
    22 22
    23 23 /* "Remove the ugly blue ring" (vs-focus-visible). */
    24 Entfernt .header-actions-btn:focus,
    25 Entfernt .header-search-btn:focus,
    26 Entfernt .main-navigation-link:focus,
    27 Entfernt .product-name:focus,
    28 Entfernt .footer-link:focus {
    24 Hinzugefügt .header-actions-btn:focus:not(:focus-visible),
    25 Hinzugefügt .header-search-btn:focus:not(:focus-visible),
    26 Hinzugefügt .main-navigation-link:focus:not(:focus-visible),
    27 Hinzugefügt .product-name:focus:not(:focus-visible),
    28 Hinzugefügt .footer-link:focus:not(:focus-visible) {
    29 29 outline: 0;
    30 30 box-shadow: none;
    31 31 }

    Resources/app/storefront/src/scss/base.scss Änderungen an Resources/app/storefront/src/scss/base.scss: 8 hinzugefügt, 0 entfernt

    Änderungen an Resources/app/storefront/src/scss/base.scss: 8 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Hinzugefügt
    1712 Hinzugefügt /* validiscan:focus-visible-styles */
    1713 Hinzugefügt /* Visible keyboard focus (WCAG 2.4.7): brand colour sw-color-brand-primary, 8.9:1 on #ffffff. */
    1714 Hinzugefügt :focus-visible {
    1715 Hinzugefügt outline: 2px solid #6b3f1d;
    1716 Hinzugefügt outline-offset: 2px;
    1717 Hinzugefügt }
    1718 Hinzugefügt /* /validiscan:focus-visible-styles */
  • Eine Tab-Reihenfolge, die der Seite folgt remove-positive-tabindex Ein tabindex größer als 0 wird entfernt oder auf 0 gesetzt, sodass die Tab-Taste der Reihenfolge der Seite folgt, außer der Code des Templates schreibt ihn.

    Ein tabindex größer als 0 zieht ein Element an den Anfang der Tastaturreihenfolge. Das Rezept entfernt ihn bei Links, Schaltflächen, Formularfeldern und anderen Elementen, die ohnehin den Fokus erhalten können, und setzt ihn bei anderen Elementen auf 0, sodass sie erreichbar bleiben, während die Tab-Taste der Reihenfolge der Seite folgt. Ein tabindex, den der Code des Templates schreibt, bleibt einem Menschen überlassen. Ein Scan meldet eine solche erzwungene Reihenfolge zweimal: über die axe-core-Regel tabindex, deren Befunde diese Änderung erhalten, und über die Prüfung vs-focus-order von Validiscan, deren Stufe MANUAL ist, sodass ihre Befunde mit einer Anleitung kommen.

    Abgedeckte Regeln
    • tabindex AUTO
    • vs-focus-order MANUAL
    Geänderte Dateitypen
    .html.twig
    removes tabindex="1" from the buy button; vs-focus-order and axe tabindex stop firing Beispieldaten

    Resources/views/storefront/page/product-detail/buy-widget.html.twig Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    82 82 </div>
    83 83 <div class="buy-widget-submit">
    84 84 <button class="btn btn-primary btn-buy"
    85 Entfernt title="{{ 'detail.addProduct'|trans }}" tabindex="1">
    85 Hinzugefügt title="{{ 'detail.addProduct'|trans }}">
    86 86 {{ 'detail.addProduct'|trans }}
    87 87 </button>
    88 88 </div>

Zoom und Touch-Ziele

  • Zoomen auf dem Smartphone erlaubt meta-viewport-zoom user-scalable=no und jedes maximum-scale unter 2 verschwinden aus dem Viewport-Tag, sodass Besucherinnen und Besucher den Text auf dem Smartphone auf mindestens 200 % vergrößern können.

    Das Rezept entfernt user-scalable=no und jedes maximum-scale unter 2 aus dem Viewport-Tag der Storefront-Templates, meist in layout/meta.html.twig, sodass Besucherinnen und Besucher den Text auf dem Smartphone auf mindestens 200 % vergrößern können. Bei der Regel zur guten Praxis meta-viewport-large liegt die Grenze bei 5. Die übrigen Viewport-Einstellungen bleiben, wie sie waren. Diente die Sperre dazu, iOS am Hineinzoomen in Formularfelder zu hindern, erfüllt eine Schriftgröße von mindestens 16 Pixeln für die Felder denselben Zweck, ohne das Zoomen zu sperren.

    Abgedeckte Regeln
    • meta-viewport AUTO
    • meta-viewport-large AUTO
    Geänderte Dateitypen
    .html.twig
    removes the zoom lock from the fixture theme and the rule stops firing Beispieldaten

    Resources/views/storefront/layout/meta.html.twig Änderungen an Resources/views/storefront/layout/meta.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/meta.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    9 Hinzugefügt <meta name="viewport" content="width=device-width, initial-scale=1">
    10 10 {% endblock %}
    11 11
    12 12 {% block layout_head_meta_tags_general %}
  • Größere Touch-Ziele target-size-padding Schaltflächen und Links, die der Scan kleiner als 24 × 24 Pixel gefunden hat, erhalten diese Mindestgröße, sofern das Rezept sie anhand ihrer Klasse bestimmen kann.

    Schaltflächen und Links, die der Scan kleiner als 24 × 24 Pixel gefunden hat, etwa Icons im Kopfbereich, Mengenschaltflächen oder Farbmuster, erhalten eine Mindestgröße von 24 × 24 Pixel. Die Mindestgröße wird in der eigenen Style-Regel des Themes ergänzt, die das Bedienelement klein gemacht hat, neben der Deklaration, die es verkleinert hat, sodass die Reihenfolge der Styles des Themes gleich bleibt und die Icons darin ihre Größe behalten. Ein Bedienelement, das ohne eine solche Regel klein ist, erhält im Haupt-Stylesheet eine Mindestgröße für seine Klasse. Ein Bedienelement, das das Rezept nicht anhand seiner Klasse bestimmen kann, bleibt einem Menschen überlassen.

    Abgedeckte Regeln
    • vs-target-size AUTO
    Geänderte Dateitypen
    .scss
    gives the header icons, steppers and swatches a 24px minimum; no page reports small targets Beispieldaten

    Resources/app/storefront/src/scss/_variant-broken.scss Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 6 hinzugefügt, 2 entfernt

    Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 6 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung Code
    10 10
    11 11 .header-actions-btn {
    12 12 width: 18px;
    13 Hinzugefügt min-width: 24px;
    13 14 height: 18px;
    14 Entfernt min-height: 0;
    15 Hinzugefügt 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 Hinzugefügt min-width: 24px;
    99 101 height: 18px;
    102 Hinzugefügt 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 Hinzugefügt min-width: 24px;
    106 110 height: 20px;
    107 Entfernt min-height: 0;
    111 Hinzugefügt min-height: 24px;
    108 112 padding: 0;
    109 113 }
    110 114

Bewegung, Slideshows und Ton

  • Weniger Bewegung auf Wunsch der Besucherinnen und Besucher reduced-motion-guard Ist das Gerät auf reduzierte Bewegung eingestellt, enden die Animationen und Übergänge, die Stylesheets definieren, sofort, und sanftes Scrollen wird abgeschaltet.

    Ist das Gerät von Besucherinnen und Besuchern auf reduzierte Bewegung eingestellt, lässt ein Block im Haupt-Stylesheet Animationen und Übergänge sofort enden und schaltet sanftes Scrollen ab. Der Block gilt für jedes Element der Seite und erfasst deshalb auch Animationen, die andere Stylesheets definieren, darunter die von Plugins. Startet das Storefront-Skript des Themes einen Timer, etwa das automatische Abspielen einer Slideshow, nur dann, wenn eine Option für das automatische Abspielen gesetzt ist, prüft diese Bedingung nun auch die Einstellung der Besucherinnen und Besucher. Andere Bewegung, die ein Skript startet, wird für die prüfende Person aufgeführt, nicht geraten.

    Abgedeckte Regeln
    • vs-motion AUTO
    Geänderte Dateitypen
    .js.scss
    guards the pulsing badge and the slider autoplay; the animation stops under reduce Beispieldaten

    Resources/app/storefront/src/main.js Änderungen an Resources/app/storefront/src/main.js: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/app/storefront/src/main.js: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    139 139 });
    140 140 });
    141 141
    142 Entfernt if (options.autoplay === true) {
    142 Hinzugefügt 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 Änderungen an Resources/app/storefront/src/scss/base.scss: 14 hinzugefügt, 0 entfernt

    Änderungen an Resources/app/storefront/src/scss/base.scss: 14 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Hinzugefügt
    1712 Hinzugefügt /* validiscan:reduced-motion-guard */
    1713 Hinzugefügt /* Honour the visitor's reduced-motion setting: animations and transitions end at once, no smooth scrolling. */
    1714 Hinzugefügt @media (prefers-reduced-motion: reduce) {
    1715 Hinzugefügt *,
    1716 Hinzugefügt *::before,
    1717 Hinzugefügt *::after {
    1718 Hinzugefügt animation-duration: 0.01ms !important;
    1719 Hinzugefügt animation-iteration-count: 1 !important;
    1720 Hinzugefügt transition-duration: 0.01ms !important;
    1721 Hinzugefügt scroll-behavior: auto !important;
    1722 Hinzugefügt }
    1723 Hinzugefügt }
    1724 Hinzugefügt /* /validiscan:reduced-motion-guard */
  • Eine Pause-Schaltfläche für bewegte Slideshows slider-pause-control Der Basis-Slider von Shopware, der Slider hinter dem Bilderslider des CMS, erhält eine sichtbare, übersetzte Schaltfläche zum Anhalten und Fortsetzen.

    Der Basis-Slider von Shopware, der Slider hinter dem Bilderslider des CMS, erhält neben seinen Pfeilen für Zurück und Weiter eine sichtbare, übersetzte Schaltfläche zum Anhalten und Fortsetzen. Ein Codeblock im Storefront-Skript des Themes verbindet die Schaltfläche über das eigene Slider-Plugin von Shopware mit dem automatischen Abspielen des Sliders. Steuert das Theme das automatische Abspielen mit einem eigenen Timer, erhält dieser Timer eine Zeile, die einen Schritt überspringt, solange der Slider angehalten ist. Die Schaltfläche bleibt verborgen, bis das Skript sie verbunden hat, und erscheint deshalb nie ohne ein automatisches Abspielen, das sie anhalten könnte. Für Besucherinnen und Besucher, deren Gerät reduzierte Bewegung verlangt, startet der Slider angehalten. Slider, die mit anderen Bibliotheken gebaut sind, bleiben einem Menschen überlassen.

    Abgedeckte Regeln
    • vs-carousel AUTO
    Geänderte Dateitypen
    .html.twig.js.json
    adds a wired pause button to the hero slider; the carousel is no longer unstoppable Beispieldaten

    Resources/app/storefront/src/main.js Änderungen an Resources/app/storefront/src/main.js: 52 hinzugefügt, 0 entfernt

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

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 6 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 6 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "slider": {
    199 Hinzugefügt "pause": "Diashow anhalten",
    200 Hinzugefügt "play": "Diashow abspielen"
    201 Hinzugefügt }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 6 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 6 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "slider": {
    199 Hinzugefügt "pause": "Pause slideshow",
    200 Hinzugefügt "play": "Play slideshow"
    201 Hinzugefügt }
    196 202 }
    197 203 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 1 hinzugefügt, 0 entfernt

    Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 1 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt <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>
  • Kein Ton, der von selbst startet mute-autoplay Ein Video, das von selbst abspielt, wird stummgeschaltet, und Audio startet nicht mehr von selbst, außer bei automatischem Abspielen aus Template-Code oder einem Skript und bei Playern in Frames; auf Shopware wird das Rezept für Befunde von vs-media nicht verwendet.

    Ein Video, das von selbst abspielt, erhält muted und playsinline, sodass es wie ein Hintergrundvideo stumm weiterläuft und in Safari auf dem Smartphone innerhalb der Seite abgespielt wird, statt den Vollbildplayer zu öffnen. Audio, das von selbst startet, tut das danach nicht mehr; Besucherinnen und Besucher starten es selbst. Automatisches Abspielen, das der Code eines Templates oder ein Skript festlegt, und in Frames eingebettete Player bleiben einem Menschen überlassen. In einem Scan meldet die Medienprüfung vs-media von Validiscan Ton, der von selbst startet; sie tritt an die Stelle der axe-core-Regel no-autoplay-audio. vs-media, die auch Untertitel und Transkripte abdeckt, hat die Stufe MANUAL, sodass ein solcher Befund auf Shopware mit einer Anleitung kommt statt mit dieser Änderung.

    Abgedeckte Regeln
    • no-autoplay-audio AUTO geprüft durch vs-media
    • vs-media MANUAL
    Geänderte Dateitypen
    .html.twig
    mutes a seeded autoplaying background video; vs-media no longer reports autoplay Beispieldaten

    Resources/views/storefront/block/cms-block-video.html.twig Änderungen an Resources/views/storefront/block/cms-block-video.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/block/cms-block-video.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <video class="cms-video" autoplay loop preload="none" width="1280" height="720" poster="{{ block.posterUrl }}">
    12 Hinzugefügt <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>

Formulare, Warenkorb und eingebettete Inhalte

  • Beschriftungen, die mit ihren Feldern verbunden sind label-association Ein Feld, dessen Beschriftung schon auf der Seite steht, direkt vor dem Feld, wird mit diesem Text verbunden.

    Ein Formularfeld, dessen Beschriftung schon auf der Seite steht, direkt vor dem Feld, wird mit diesem Text verbunden. Ein <label> ohne for erhält ein for, das auf das Feld zeigt. Ein Textelement, das das Theme über seine Klasse als Titel oder Beschriftung auszeichnet, etwa der Name einer Variantengruppe auf einer Produktseite, wird zum <label> des Felds und behält seine Klassen und seine eigene Zeile. Screenreader lesen die Beschriftung dann vor, wenn das Feld den Fokus erhält. Ein Feld, das nur einen Platzhalter hat, bleibt einem Menschen überlassen, weil eine Beschriftung zu schreiben eine Inhaltsentscheidung ist.

    Abgedeckte Regeln
    • label AUTO geprüft durch vs-forms-ecommerce
    • select-name ASSISTED
    • vs-forms-ecommerce AUTO
    Geänderte Dateitypen
    .html.twig
    turns the variant group titles into labels of their selects; select-name no longer fires Beispieldaten

    Resources/views/storefront/page/product-detail/configurator.html.twig Änderungen an Resources/views/storefront/page/product-detail/configurator.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/page/product-detail/configurator.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    13 13 {% for group in product.configurator %}
    14 14 {% if group.display == 'select' %}
    15 15 <div class="product-detail-configurator-group">
    16 Entfernt <div class="product-detail-configurator-group-title">{{ group.name }}</div>
    16 Hinzugefügt <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) %}
  • Konto-, Adress- und Kontaktfelder, die Browser ausfüllen können autocomplete-tokens Felder, die nach den eigenen Daten der Kundinnen und Kunden fragen, etwa bei Registrierung, Adresse und Anmeldung, erhalten den autocomplete-Wert für das, wonach sie fragen, außer der Code eines Templates schreibt ihn.

    Felder, die nach den eigenen Daten der Kundinnen und Kunden fragen, etwa in den Formularen für Registrierung, Adresse, Anmeldung, Kontakt und Newsletter, erhalten den autocomplete-Wert für das, wonach sie fragen, etwa E-Mail-Adresse, Vor- und Nachname, Postleitzahl, Telefonnummer oder Passwort. Der Wert wird aus Typ, Name, ID, Beschriftung und Platzhalter des Felds abgeleitet. Passwörter erhalten in Registrierungsformularen new-password und sonst current-password. Felder für Suche, Gutschein, Menge, Captcha und Nachricht bleiben, wie sie sind, und ein Wert, den der Code eines Templates schreibt, bleibt einem Menschen überlassen. Browser können die Felder dann ausfüllen, und assistive Technologien erkennen, wofür jedes Feld gedacht ist.

    Abgedeckte Regeln
    • autocomplete-valid AUTO geprüft durch vs-forms-ecommerce
    • vs-forms-ecommerce AUTO
    Geänderte Dateitypen
    .html.twig
    adds tokens to the register, address, login, contact and newsletter fields; no autocomplete hits remain Beispieldaten

    Resources/views/storefront/component/account/login.html.twig Änderungen an Resources/views/storefront/component/account/login.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/component/account/login.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    19 19 name="username"
    20 20 placeholder="{{ 'account.loginMailPlaceholder'|trans }}"
    21 21 required
    22 Entfernt autocomplete="off">
    22 Hinzugefügt autocomplete="email">
    23 23 </div>
    24 24
    25 25 <div class="form-group">

    Resources/views/storefront/component/account/register.html.twig Änderungen an Resources/views/storefront/component/account/register.html.twig: 8 hinzugefügt, 4 entfernt

    Änderungen an Resources/views/storefront/component/account/register.html.twig: 8 hinzugefügt, 4 entfernt
    Zeile vorher Zeile nachher Änderung Code
    33 33 id="{{ formId }}FirstName"
    34 34 name="firstName"
    35 35 placeholder="{{ 'account.firstNamePlaceholder'|trans }}"
    36 Entfernt required>
    36 Hinzugefügt required
    37 Hinzugefügt 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 Entfernt required>
    45 Hinzugefügt required
    46 Hinzugefügt 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 Entfernt required>
    57 Hinzugefügt required
    58 Hinzugefügt 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 Entfernt required>
    68 Hinzugefügt required
    69 Hinzugefügt 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 Änderungen an Resources/views/storefront/component/address/address-form.html.twig: 8 hinzugefügt, 4 entfernt

    Änderungen an Resources/views/storefront/component/address/address-form.html.twig: 8 hinzugefügt, 4 entfernt
    Zeile vorher Zeile nachher Änderung Code
    14 14 id="{{ prefix }}AddressStreet"
    15 15 name="{{ prefix }}[street]"
    16 16 placeholder="{{ 'address.streetPlaceholder'|trans }}"
    17 Entfernt required>
    17 Hinzugefügt required
    18 Hinzugefügt 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 Entfernt required>
    30 Hinzugefügt required
    31 Hinzugefügt 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 Entfernt required>
    42 Hinzugefügt required
    43 Hinzugefügt 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 Entfernt required>
    53 Hinzugefügt required
    54 Hinzugefügt 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 Änderungen an Resources/views/storefront/element/cms-element-form.html.twig: 4 hinzugefügt, 4 entfernt

    Änderungen an Resources/views/storefront/element/cms-element-form.html.twig: 4 hinzugefügt, 4 entfernt
    Zeile vorher Zeile nachher Änderung Code
    18 18 </select>
    19 19 </div>
    20 20 <div class="form-group col-md-4">
    21 Entfernt <input type="text" class="form-control" id="contactFormFirstName" name="firstName" placeholder="{{ 'account.firstNamePlaceholder'|trans }}" required>
    21 Hinzugefügt <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 Entfernt <input type="text" class="form-control" id="contactFormLastName" name="lastName" placeholder="{{ 'account.lastNamePlaceholder'|trans }}" required>
    24 Hinzugefügt <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 Entfernt <input type="email" class="form-control" id="contactFormMail" name="email" placeholder="{{ 'account.emailPlaceholder'|trans }}" required>
    31 Hinzugefügt <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 Entfernt <input type="tel" class="form-control" id="contactFormPhone" name="phone">
    35 Hinzugefügt <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 Änderungen an Resources/views/storefront/layout/footer/footer.html.twig: 2 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/footer/footer.html.twig: 2 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    62 62 id="footerNewsletterMail"
    63 63 class="form-control"
    64 64 placeholder="{{ 'footer.newsletterPlaceholder'|trans }}"
    65 Entfernt required>
    65 Hinzugefügt required
    66 Hinzugefügt 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">
  • Änderungen bei Preis, Bestand und Warenkorb werden angesagt live-region-cart-variant Preis, Verfügbarkeit oder Summen des Warenkorbs, die das Storefront-Skript an Ort und Stelle aktualisiert, werden zu Live-Regionen, die Screenreader vorlesen, ohne zu unterbrechen, außer ein Skript ersetzt das Element selbst.

    Die Teile einer Seite, die das Storefront-Skript an Ort und Stelle aktualisiert, etwa der Preis, nachdem Kundinnen und Kunden eine Variante gewählt haben, die Verfügbarkeit oder die Summen des Warenkorbs, werden zu Live-Regionen mit der Einstellung „polite“. Screenreader lesen den neuen Wert dann einmal vollständig vor, ohne zu unterbrechen. Ersetzt ein Skript das Element selbst statt seines Inhalts, würde eine Live-Region nicht funktionieren; deshalb überlässt das Rezept dieses Element einem Menschen.

    Abgedeckte Regeln
    • vs-live-updates AUTO
    Geänderte Dateitypen
    .html.twig
    makes the variant price and the cart totals polite live regions; the updates are announced Beispieldaten

    Resources/views/storefront/page/checkout/cart/index.html.twig Änderungen an Resources/views/storefront/page/checkout/cart/index.html.twig: 2 hinzugefügt, 2 entfernt

    Änderungen an Resources/views/storefront/page/checkout/cart/index.html.twig: 2 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <div class="line-item-total-price">
    62 Hinzugefügt <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 Entfernt <div class="checkout-aside-summary-totals" data-shipping-threshold="{{ shop.shippingThreshold }}" data-shipping-costs="{{ shop.shippingCosts }}">
    91 Hinzugefügt <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 Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    17 17 {% endblock %}
    18 18
    19 19 {% block buy_widget_price %}
    20 Entfernt <div class="product-detail-price-container">
    20 Hinzugefügt <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>
  • Titel für eingebettete Karten und Videos iframe-title Ein eingebetteter Frame ohne Titel erhält einen übersetzten Titel, der sagt, was er zeigt, sofern der Code es verrät.

    Ein eingebetteter Frame ohne Titel, etwa eine Karte oder ein YouTube- oder Vimeo-Video, erhält aus den Snippet-Dateien des Themes einen übersetzten Titel, der sagt, was er zeigt. Das Rezept liest aus der Adresse des Frames, seiner Klasse und seinem Namen oder aus dem umgebenden Template, was der Frame zeigt. Ein Frame, dessen Inhalt der Code nicht verrät, erhält vom Rezept keinen Titel, weil ein erfundener Titel schlechter ist als keiner.

    Abgedeckte Regeln
    • frame-title AUTO
    Geänderte Dateitypen
    .html.twig.json
    titles the contact map frame through the snippets; frame-title no longer fires Beispieldaten

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 5 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 5 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "iframe": {
    199 Hinzugefügt "map": "Karte"
    200 Hinzugefügt }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 5 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 5 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "iframe": {
    199 Hinzugefügt "map": "Map"
    200 Hinzugefügt }
    196 201 }
    197 202 }

    Resources/views/storefront/block/cms-block-map.html.twig Änderungen an Resources/views/storefront/block/cms-block-map.html.twig: 2 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/block/cms-block-map.html.twig: 2 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    11 11 <iframe class="cms-map-frame"
    12 12 src="{{ url('/widgets/cms/map') }}"
    13 13 width="1280"
    14 Entfernt height="360"></iframe>
    14 Hinzugefügt height="360"
    15 Hinzugefügt 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>

Kontrast, Bilder und Bedienelemente, die eine Einschätzung verlangen

Textkontrast, Textalternativen für Bilder und Namen für unbenannte Bedienelemente verlangen meist eine Einschätzung: welche Farbe zu Ihrer Marke passt, welche Worte ein Bild beschreiben. Die Regeln, die sie melden, haben die Stufe ASSISTED, wie die Listen unten zeigen. Für einen Befund einer ASSISTED-Regel entwirft KI nur dann eine Änderung, wenn Sie KI-Entwürfen zugestimmt haben, und Validiscan prüft jede Änderung, bevor sie ausgeliefert wird.

Die vier Rezepte unten sind für keine Regel die Standardkorrektur. Jedes erfasst nur die engen Fälle, in denen allein der Code des Themes belegt, wie die Änderung aussehen muss. Zwei davon, button-semantics und icon-accessible-name, decken auch die Slideshow-Prüfung vs-carousel ab, die AUTO ist: In einem Projekt probiert die Korrektur-Engine die beiden an den Befunden dieser Prüfung aus, wenn das Rezept für die Pause-Schaltfläche oben sie nicht beheben kann. Die anderen beiden decken nur Regeln der Stufe ASSISTED ab.

  • Textfarben mit ausreichendem Kontrast contrast-tokens Eine Textfarbe, die die Kontrastprüfung nicht besteht und an genau einer Stelle definiert ist, wechselt zur nächsten Abstufung desselben Farbtons mit dem erforderlichen Kontrast; in einem Projekt nicht angewendet.

    Besteht Text die Kontrastprüfung nicht und ist seine Farbe an genau einer Stelle im Theme definiert, wird diese Stelle auf die nächste Abstufung desselben Farbtons geändert, die den erforderlichen Kontrast erreicht, sodass das Design wiedererkennbar bleibt. Eine Stelle kann ein Farbfeld in der theme.json zusammen mit der SCSS-Variablen sein, die es wiederholt, eine SCSS-Variable oder CSS-Variable, die nur für Text verwendet wird, oder eine einzelne color-Deklaration. Eine Farbe, die an mehreren Stellen oder auch als Hintergrund oder Rand verwendet wird, und die grundlegenden Farbfelder von Shopware für Marke, Ränder, Hintergründe, Statusmeldungen und die Kaufen-Schaltfläche bleiben einem Menschen überlassen. Eine Farbe, die in den Theme-Einstellungen der Administration gespeichert ist, überschreibt die theme.json: Wurde sie dort geändert, muss sie auch dort geändert werden. Die Regel color-contrast hat die Stufe ASSISTED, deshalb geht ein Kontrastbefund in einem Projekt den Weg ASSISTED, und dieses Rezept wird darauf nicht angewendet.

    Abgedeckte Regeln
    • color-contrast ASSISTED
    Geänderte Dateitypen
    .scss
    darkens the unit price colour to 4.5:1 with the same hue; axe clears it on both pages Beispieldaten

    Resources/app/storefront/src/scss/_variant-broken.scss Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/app/storefront/src/scss/_variant-broken.scss: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    45 45 /* Light grey unit prices, 3.1:1 (color-contrast). */
    46 46 .product-price-unit,
    47 47 .product-detail-price-unit {
    48 Entfernt color: #929292;
    48 Hinzugefügt color: #757575;
    49 49 }
    50 50
    51 51 /* Fixed two-line product names (vs-text-spacing). */
  • Dekorative Bilder, vor Screenreadern verborgen decorative-image-alt Nur Bilder, bei denen das Template belegt, dass sie dekorativ sind, erhalten ein leeres alt oder aria-hidden; in einem Projekt nicht angewendet.

    Ein Bild wird nur dann als dekorativ markiert, wenn das Template belegt, dass es nichts beiträgt: Es steht in einem Link oder einer Schaltfläche, die auf andere Weise einen Namen hat, es ist ein Icon neben seiner eigenen Textbeschriftung, oder es ist ein Abstandshalter oder Trenner. Ein <img> erhält ein leeres alt, ein Bild, das mit dem Tag sw_thumbnails von Shopware ausgegeben wird, erhält in seinen Attributen ein leeres alt, und SVG-Icons erhalten aria-hidden="true", sodass Screenreader sie überspringen, statt einen Dateinamen vorzulesen. Jedes andere Bild ist Inhalt, etwa die Zahlungslogos in einer Fußzeile, und seine Textalternative braucht Worte, die ein Mensch wählt. Die Regeln, die dieses Rezept abdeckt, haben die Stufe ASSISTED; deshalb gehen ihre Befunde in einem Projekt den Weg ASSISTED, und dieses Rezept wird darauf nicht angewendet.

    Abgedeckte Regeln
    • image-alt ASSISTED
    • role-img-alt ASSISTED
    • svg-img-alt ASSISTED
    Geänderte Dateitypen
    .html.twig
    marks a seeded phone icon inside the hotline link decorative; image-alt stops firing there Beispieldaten

    Resources/views/storefront/layout/footer/footer.html.twig Änderungen an Resources/views/storefront/layout/footer/footer.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/footer/footer.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    9 9 <div class="footer-column-content">
    10 10 <p class="footer-contact-hotline">
    11 11 {{ 'footer.serviceHotlineText'|trans }}
    12 Entfernt <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 Hinzugefügt <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">
  • Bedienelemente nur für die Maus, als Schaltflächen umgesetzt button-semantics Bedienelemente in Slidern und Cookie-Bannern, die nur auf eine Maus reagieren, werden zu echten Schaltflächen und behalten Klassen, Data-Attribute und IDs; in einem Projekt wird das Rezept an Slideshow-Befunden von vs-carousel versucht.

    Anklickbare <div>-, <span>-, <i>- und <li>-Elemente in Slidern und Cookie-Bannern sowie Links ohne Adresse, die nur auf eine Maus reagieren, werden zu echten Schaltflächen. Klassen, Data-Attribute und IDs bleiben erhalten, sodass das Skript des Themes sie wie bisher findet, und die Eingabetaste und die Leertaste bewirken nun, was ein Klick bewirkt. Ein Listenelement behält seinen Platz in der Liste; die Schaltfläche wird darin eingesetzt. Ein Bedienelement ohne Text erhält aus seinem Zweck einen übersetzten Namen, auf Englisch etwa „Show slide 2“, und ein kleines Style-Reset bewahrt sein Aussehen. Ein Bedienelement ohne Text, dessen Zweck unklar ist oder dessen Gestaltung von seinem Tag oder seiner Position abhängt, bleibt einem Menschen überlassen. In einem Projekt wird dieses Rezept an Slideshow-Befunden von vs-carousel versucht; Befunde der Cookie-Banner-Prüfung vs-cookie-banner haben die Stufe ASSISTED und gehen den Weg ASSISTED.

    Abgedeckte Regeln
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Geänderte Dateitypen
    .html.twig.json.scss
    turns the mouse-only slider dots into named buttons in their list items; vs-carousel stops reporting them Beispieldaten

    Resources/app/storefront/src/scss/base.scss Änderungen an Resources/app/storefront/src/scss/base.scss: 17 hinzugefügt, 0 entfernt

    Änderungen an Resources/app/storefront/src/scss/base.scss: 17 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung Code
    1708 1708 margin: 8px 0 0;
    1709 1709 font-size: 0.875rem;
    1710 1710 }
    1711 Hinzugefügt
    1712 Hinzugefügt /* validiscan:button-semantics */
    1713 Hinzugefügt /* Former <div>/<span>/<li> controls, now <button>: drop the browser button styles, keep the theme's (no specificity). */
    1714 Hinzugefügt :where(button.base-slider-dot) {
    1715 Hinzugefügt display: block;
    1716 Hinzugefügt margin: 0;
    1717 Hinzugefügt padding: 0;
    1718 Hinzugefügt border: 0;
    1719 Hinzugefügt background: none;
    1720 Hinzugefügt color: inherit;
    1721 Hinzugefügt font: inherit;
    1722 Hinzugefügt line-height: inherit;
    1723 Hinzugefügt text-align: inherit;
    1724 Hinzugefügt cursor: pointer;
    1725 Hinzugefügt appearance: none;
    1726 Hinzugefügt }
    1727 Hinzugefügt /* /validiscan:button-semantics */

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 5 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 5 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "showSlide": "Folie %index% anzeigen"
    200 Hinzugefügt }
    196 201 }
    197 202 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 5 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 5 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "showSlide": "Show slide %index%"
    200 Hinzugefügt }
    196 201 }
    197 202 }

    Resources/views/storefront/element/cms-element-image-slider.html.twig Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    51 51 </button>
    52 52 <ol class="base-slider-dots">
    53 53 {% for slide in slides %}
    54 Entfernt <li class="base-slider-dot{% if loop.first %} is-active{% endif %}" data-slide-to="{{ loop.index0 }}"></li>
    54 Hinzugefügt <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>
  • Namen für Schaltflächen und Links, die nur ein Icon zeigen icon-accessible-name Schaltflächen und Links, die nur ein Icon zeigen, erhalten einen übersetzten Namen, der sagt, was sie tun, sofern der Code ihren Zweck verrät; in einem Projekt wird das Rezept an den Slideshow-Bedienelementen versucht, die vs-carousel meldet.

    Schaltflächen und Links, die nur ein Icon zeigen, etwa für Suche, Warenkorb, Konto, den Logo-Link, Pfeile eines Sliders, Mengenschaltflächen und Cookie-Auswahl, erhalten ein übersetztes aria-label, das sagt, was sie tun. Der Zweck wird aus den Klassen des Bedienelements, seinem Linkziel, dem Icon darin und der umgebenden Komponente abgelesen. Ein Bedienelement, dessen Zweck der Code nicht verrät, etwa ein Social-Media-Link, dessen Netzwerk als Datenwert hinterlegt ist, bleibt einem Menschen überlassen, weil ein erfundener Name schlechter ist als keiner. In einem Projekt wird dieses Rezept an den Slideshow-Bedienelementen versucht, die vs-carousel meldet; andere Bedienelemente mit nur einem Icon werden von Regeln der Stufe ASSISTED gemeldet und gehen den Weg ASSISTED.

    Abgedeckte Regeln
    • button-name ASSISTED
    • link-name ASSISTED
    • vs-carousel AUTO
    • vs-cookie-banner ASSISTED
    Geänderte Dateitypen
    .html.twig.json
    names every icon-only button of the home, product and cart pages; button-name no longer fires Beispieldaten

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 12 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 12 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "search": "Suchen",
    200 Hinzugefügt "previousSlide": "Vorherige Folie",
    201 Hinzugefügt "nextSlide": "Nächste Folie",
    202 Hinzugefügt "cookieSettings": "Cookie-Einstellungen",
    203 Hinzugefügt "acceptCookies": "Alle Cookies akzeptieren",
    204 Hinzugefügt "decreaseQuantity": "Menge verringern",
    205 Hinzugefügt "increaseQuantity": "Menge erhöhen",
    206 Hinzugefügt "removeFromCart": "Aus dem Warenkorb entfernen"
    207 Hinzugefügt }
    196 208 }
    197 209 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 12 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 12 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "search": "Search",
    200 Hinzugefügt "previousSlide": "Previous slide",
    201 Hinzugefügt "nextSlide": "Next slide",
    202 Hinzugefügt "cookieSettings": "Cookie settings",
    203 Hinzugefügt "acceptCookies": "Accept all cookies",
    204 Hinzugefügt "decreaseQuantity": "Decrease quantity",
    205 Hinzugefügt "increaseQuantity": "Increase quantity",
    206 Hinzugefügt "removeFromCart": "Remove from cart"
    207 Hinzugefügt }
    196 208 }
    197 209 }

    Resources/views/storefront/element/cms-element-image-gallery.html.twig Änderungen an Resources/views/storefront/element/cms-element-image-gallery.html.twig: 2 hinzugefügt, 2 entfernt

    Änderungen an Resources/views/storefront/element/cms-element-image-gallery.html.twig: 2 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung Code
    31 31
    32 32 {% if media|length > 1 %}
    33 33 <div class="gallery-slider-controls">
    34 Entfernt <button type="button" class="base-slider-controls-prev gallery-slider-controls-prev" data-gallery-prev>
    34 Hinzugefügt <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 Entfernt <button type="button" class="base-slider-controls-next gallery-slider-controls-next" data-gallery-next>
    37 Hinzugefügt <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 Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 2 hinzugefügt, 2 entfernt

    Änderungen an Resources/views/storefront/element/cms-element-image-slider.html.twig: 2 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung Code
    46 46
    47 47 {% block element_image_slider_controls %}
    48 48 <div class="image-slider-controls-container">
    49 Entfernt <button type="button" class="base-slider-controls-prev image-slider-controls-btn" data-slide-prev>
    49 Hinzugefügt <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 Entfernt <button type="button" class="base-slider-controls-next image-slider-controls-btn" data-slide-next>
    57 Hinzugefügt <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 Änderungen an Resources/views/storefront/layout/cookie/cookie-permission.html.twig: 2 hinzugefügt, 2 entfernt

    Änderungen an Resources/views/storefront/layout/cookie/cookie-permission.html.twig: 2 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung Code
    22 22 </p>
    23 23 </div>
    24 24 <div class="col-auto cookie-permission-actions">
    25 Entfernt <button type="button" class="btn cookie-permission-icon-btn js-cookie-configuration-toggle">{% sw_icon 'settings' %}</button>
    26 Entfernt <button type="button" class="btn btn-primary cookie-permission-icon-btn js-cookie-accept-all-button">{% sw_icon 'checkmark' %}</button>
    25 Hinzugefügt <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 Hinzugefügt <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 Änderungen an Resources/views/storefront/layout/header/search.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/header/search.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    13 13 autocomplete="off"
    14 14 autocapitalize="off"
    15 15 placeholder="{{ 'header.searchPlaceholder'|trans }}" value="{{ page.searchTerm }}">
    16 Entfernt <button type="submit" class="btn header-search-btn">
    16 Hinzugefügt <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 Änderungen an Resources/views/storefront/page/checkout/cart/index.html.twig: 3 hinzugefügt, 3 entfernt

    Änderungen an Resources/views/storefront/page/checkout/cart/index.html.twig: 3 hinzugefügt, 3 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    42 Hinzugefügt <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 Entfernt <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    53 Hinzugefügt <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 Entfernt <button type="button" class="btn btn-light btn-sm line-item-remove-button">
    67 Hinzugefügt <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 Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 2 hinzugefügt, 2 entfernt

    Änderungen an Resources/views/storefront/page/product-detail/buy-widget.html.twig: 2 hinzugefügt, 2 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <button type="button" class="btn btn-outline-light btn-minus js-btn-minus">
    67 Hinzugefügt <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 Entfernt <button type="button" class="btn btn-outline-light btn-plus js-btn-plus">
    78 Hinzugefügt <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 Beispieldaten

    Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 6 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/de_DE/kaffeekontor-nord.de-DE.json: 6 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "home": "Zur Startseite",
    200 Hinzugefügt "account": "Ihr Konto"
    201 Hinzugefügt }
    196 202 }
    197 203 }

    Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 6 hinzugefügt, 0 entfernt

    Änderungen an Resources/snippet/en_GB/kaffeekontor-nord.en-GB.json: 6 hinzugefügt, 0 entfernt
    Zeile vorher Zeile nachher Änderung 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 Hinzugefügt },
    197 Hinzugefügt "a11y": {
    198 Hinzugefügt "control": {
    199 Hinzugefügt "home": "Go to the homepage",
    200 Hinzugefügt "account": "Your account"
    201 Hinzugefügt }
    196 202 }
    197 203 }

    Resources/views/storefront/layout/header/actions.html.twig Änderungen an Resources/views/storefront/layout/header/actions.html.twig: 2 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/header/actions.html.twig: 2 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung Code
    3 3 {% block layout_header_actions_account %}
    4 4 <div class="header-account">
    5 5 <a href="{{ url('/account/login') }}"
    6 Entfernt class="btn account-menu-btn header-actions-btn">
    6 Hinzugefügt class="btn account-menu-btn header-actions-btn"
    7 Hinzugefügt 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 Änderungen an Resources/views/storefront/layout/header/logo.html.twig: 1 hinzugefügt, 1 entfernt

    Änderungen an Resources/views/storefront/layout/header/logo.html.twig: 1 hinzugefügt, 1 entfernt
    Zeile vorher Zeile nachher Änderung 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 Entfernt <a class="header-logo-main-link" href="{{ url('/') }}">
    5 Hinzugefügt <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') }}"
Details: die Stufen, das Testtheme, die Prüfungen jeder Änderung und Text in den Sprachen Ihres Shops

Ein Rezept ist eine deterministische Änderung für eine Art von Fehler: Derselbe Code erhält immer dieselbe Änderung. Validiscan hat 20 Rezepte für Shopware-Themes. Jedes ändert nur dort etwas, wo der Code des Themes zeigt, welche Änderung die richtige ist. Wo er es nicht zeigt, ändert das Rezept nichts. Es rät nicht.

Unter jedem Rezept finden Sie die Regeln, die es abdeckt, mit der Stufe jeder Regel im Regelkatalog von Validiscan für Shopware. AUTO bedeutet, dass ein Rezept die Standardkorrektur der Regel ist. ASSISTED bedeutet, dass die Korrektur eine Einschätzung verlangt; deshalb wird eine Änderung entworfen und dann geprüft. MANUAL bedeutet, dass die Regel die Entscheidung eines Menschen braucht und mit einer Anleitung statt mit einer Codeänderung kommt. In einem Projekt werden Rezepte auf Befunde von Regeln der Stufe AUTO angewendet. Führt ein Rezept zusätzlich eine Regel der Stufe ASSISTED oder MANUAL auf, geht ein Befund dieser Regel stattdessen den Weg ASSISTED oder MANUAL. Eine Regel, die durch eine andere Prüfung geprüft wird, läuft in einem Scan nicht für sich; die andere Prüfung meldet denselben Fehler.

Die Dateitypen und das Vorher und Nachher jedes Rezepts stammen aus den eigenen Tests von Validiscan. Sie laufen auf dem Shopware-Testtheme von Validiscan, einem Theme-Plugin auf der Standard-Storefront von Shopware 6, in das absichtlich Fehler der Barrierefreiheit eingebaut wurden. Jeder Test rendert die Templates des Themes vor und nach der Änderung zu Seiten und führt auf beiden dieselbe Regel aus: Der Fehler muss vorher gemeldet werden und nachher nicht mehr, und die Änderung darf keinen neuen Fehler dieser Regel verursachen. Die Seiten rendert ein Test-Renderer, keine vollständige Shopware-Installation, und die Dateien Ihres Themes sehen anders aus.

Bevor Validiscan eine Änderung prüft, wird jede geänderte Datei kontrolliert. Twig-Templates werden mit einem Parser gelesen, dem die eigenen Tags von Shopware wie sw_extends und sw_include bekannt sind; sie werden nie kompiliert oder ausgeführt. Stylesheets erhalten eine Strukturprüfung, Snippet-Dateien und die theme.json werden als JSON gelesen, und JavaScript erhält eine Syntaxprüfung.

Fügt ein Rezept sichtbaren Text oder einen Namen hinzu, etwa die Worte eines Skip-Links oder den Namen einer Icon-Schaltfläche, verwendet es dafür ein Snippet im Template. Der Text kommt in die englische Snippet-Datei des Themes, die das Rezept anlegt, falls das Theme keine hat, und in die deutsche Snippet-Datei, wo das Theme eine hat. Snippets, die schon existieren, behalten ihren Text. Text für die anderen Sprachen Ihres Shops wird nicht hinzugefügt.

Was bleibt auf Shopware MANUAL?

Fehler, die eine Entscheidung über Inhalt oder Verhalten verlangen, die eine feste Codeänderung nicht treffen kann, zum Beispiel Untertitel und Transkripte für Ihre Videos und Audioinhalte, bleiben auf Shopware MANUAL. Für einen Befund einer MANUAL-Regel gibt Validiscan eine Anleitung zur Behebung, keine Änderung an Ihrem Code.

Die unten aufgeführten Regeln werden in Scans ausgeführt und haben auf Shopware die Stufe MANUAL.

  • meta-refresh Zeitverzögerte Aktualisierung unter 20 Stunden darf nicht verwendet werden
  • nested-interactive Interaktive Bedienelemente dürfen nicht verschachtelt sein
  • scrollable-region-focusable Scrollbare Bereiche müssen per Tastatur erreichbar sein
  • server-side-image-map Serverseitige Imagemaps dürfen nicht verwendet werden
  • vs-keyboard-trap Der Tastaturfokus darf nie festhängen
  • vs-focus-order Die Fokusreihenfolge muss der visuellen Reihenfolge folgen
  • vs-dragging Reine Ziehbewegungen brauchen eine Alternative per Tippen oder Klicken
  • vs-media Videos brauchen Untertitel, Audio braucht ein Transkript, und nichts darf automatisch mit Ton starten
  • vs-documents Verlinkte Dokumente brauchen eine manuelle Prüfung der Barrierefreiheit
Details: was ein Befund der Stufe MANUAL erhält und was ein Projekt nicht umfasst

Für einen Befund einer MANUAL-Regel gibt Validiscan eine Anleitung zur Behebung, keine Änderung an Ihrem Code. Diese Fehler verlangen eine Entscheidung über Inhalt oder Verhalten, die eine feste Codeänderung nicht treffen kann, zum Beispiel Untertitel und Transkripte für Ihre Videos und Audioinhalte, einen Ausweg aus einem Widget, das den Tastaturfokus festhält, oder eine zweite Möglichkeit, ein Bedienelement zu benutzen, das nur durch Ziehen funktioniert.

Die Arbeit an Befunden der Stufe MANUAL, bei denen Sie selbst inhaltlich entscheiden müssen, ist nicht Teil eines Projekts, PDFs ebenso wenig. Was der Scan automatisch prüft und was von Hand geprüft wird

Was stellen Sie bereit?

Sie stellen bereit: Ihre Einwilligung in einen vollständigen Scan, einen Weg in Ihr Theme-Plugin, Ihre Antwort zu KI-Entwürfen für ASSISTED-Korrekturen, ein Entwicklungsteam, das die Änderungen prüft, das Theme baut und bereitstellt, einige Einstellungen in der Administration, neuen Text für Sprachen außer Englisch und Deutsch und Ihre Entscheidungen darüber, was MANUAL bleibt.

  1. Ihre Einwilligung in einen vollständigen Scan Ihres Shops, die Validiscan vor dem Scan erfasst. Ein vollständiger Scan legt ein Produkt in den Warenkorb und öffnet den ersten Schritt im Checkout, ohne einen Kauf abzuschließen oder Zahlungsdaten einzugeben.
  2. Einen Weg in Ihr Theme: den Zugriff auf das GitHub-Repository, in dem das Theme-Plugin liegt, oder ein ZIP des Plugin-Ordners unter custom/plugins, das im Kundenportal hochgeladen wird.
  3. Ihre Antwort darauf, ob KI Korrekturen der Stufe ASSISTED entwerfen darf. KI-Entwürfe sind ausgeschaltet, bis Sie zustimmen.
  4. Ein Entwicklungsteam, das die Änderungen prüft, den Pull Request zusammenführt oder das Patch-Paket einspielt, das Theme baut und bereitstellt.
  5. Die Einstellungen in der Administration von Shopware, die die Korrekturen nicht ändern: Meta-Titel für Seiten, die keinen haben, die Sprache von Inhalten, die in einer anderen Sprache als Ihr Shop verfasst sind, und eine in den Theme-Einstellungen gespeicherte Farbe, wenn eine Kontrastkorrektur diese Farbe ändert.
  6. Text für die Sprachen Ihres Shops außer Englisch und Deutsch, wo eine Korrektur neuen Text hinzufügt.
  7. Ihre Entscheidungen zu dem, was MANUAL bleibt, etwa Untertitel für Ihre Videos.
Details: wenn die Änderungen live sind, und was ein Projekt umfasst

Sobald die Änderungen live sind, prüft ein Verifizierungsscan die betroffenen Seiten erneut, und das Remediation Evidence Package hält das Ergebnis fest. Der Ablauf eines Projekts, Schritt für Schritt

Ein Projekt gilt für einen Shop: eine Domain und ein Theme. Es ist für eine Storefront gedacht, die auf einem Theme beruht; eine Headless-Storefront ist auf Anfrage möglich.

Quellen

2 Quellen, alle für diese Seite geprüft am 6. Oktober 2026.

Entwickler­dokumentation von Shopware 2
  1. Add SCSS Styling and JavaScript to a Theme · Shopware · geprüft am
  2. Add custom JavaScript · Shopware · geprüft am

Beginnen Sie mit einem kostenlosen Check Ihres Shops.

Senden Sie die Adresse Ihres Shops. Validiscan scannt ihn und antwortet mit jedem Fehler, den der Scan nachweisen kann, jeweils mit der Seite und dem Code.

Kostenlosen Check anfordern

Oder schreiben Sie an info@validiscan.com.

Was der kostenlose Check umfasst und was nicht
Sie senden
die Adresse Ihres Shops
Gescannte Seiten
bis zu 15
Sie erhalten
jeden Fehler, den der Scan nachweisen kann, mit Seite und Code
In Ihrem Shop
nur öffentliche Seiten; keine Anmeldung, nichts gekauft, kein Formular abgeschickt
Was er nicht ist
ein vollständiges Audit

Nachricht an Validiscan

Für einen kostenlosen Check nennen Sie die Adresse Ihres Shops und seine Plattform.

Validiscan verwendet, was Sie senden, um Ihnen zu antworten. Datenschutzerklärung