Die Fokusreihenfolge muss der visuellen Reihenfolge folgen
Springt die Tab-Taste vom Header zum Footer und zurück zu einem Produkt, verlieren Menschen, die mit Tastatur oder Screenreader navigieren, den Überblick, übersehen Optionen und haben Mühe, Formulare in der richtigen Reihenfolge auszufüllen.
- Regel-ID
- vs-focus-order
- Quelle
- Validiscan-Prüfung
- Auswirkung
- Mittel
- Korrekturstufe
- MANUAL
- auf Shopify, Shopware, WooCommerce und Magento
- Status
- wird ausgeführt
Was geprüft wird
Vergleicht die Tab-Reihenfolge mit dem visuellen Layout und markiert Sprünge, bei denen der Fokus in einer Reihenfolge wandert, die nicht zur Darstellung passt, zum Beispiel wegen positiver tabindex-Werte oder per CSS verschobener Inhalte (2.4.3).
Die Prüfung sammelt bis zu 300 sichtbare, per Tastatur erreichbare Elemente und ihre Position auf der Seite. Die Tab-Reihenfolge ermittelt sie aus dem Markup, ohne die Tab-Taste zu drücken: zuerst positive tabindex-Werte in aufsteigender Folge, dann die Reihenfolge im Dokument. Sie läuft in jedem gescannten Viewport, standardmäßig 1440 und 390 px breit.
Jedes Element mit positivem tabindex ist ein Befund. Bei Schritten zwischen Elementen ohne einen solchen Wert vergleicht die Prüfung jedes Element mit seinem Vorgänger in der Tab-Reihenfolge. Liegt seine Unterkante mehr als 100 px über der Oberkante des vorherigen Elements, springt der Fokus auf der Seite zurück nach oben, und der Schritt landet bei „zu prüfen“ (höchstens 25 pro Seite). CSS, das Inhalte umsortiert, etwa order in Flexbox, die Platzierung im Grid oder absolute Positionierung, kann einen solchen Sprung verursachen. Ob die Reihenfolge dann noch sinnvoll ist, muss ein Mensch entscheiden. Aus Ergebnissen „zu prüfen“ macht der Scanner derzeit keine Befunde, deshalb führen nur positive tabindex-Werte zu Befunden. Sprünge zur Seite oder nach unten werden nicht gemessen.
So wird es behoben
Bringen Sie die Quellreihenfolge der Templates in Übereinstimmung mit der visuellen Reihenfolge, entfernen Sie positive tabindex-Werte, und vermeiden Sie es, Inhalte mit CSS order, flex-direction oder absoluter Positionierung umzusortieren, wo das die Reihenfolge ändert, in der Menschen lesen und mit Tab navigieren.
WCAG-Erfolgskriterien
- 2.4.3 Focus Order, Stufe A
Abschnitte der EN 301 549
| WCAG-Kriterium | Version 3.2.1 | Version 4.1.1 |
|---|---|---|
| 2.4.3 | 9.2.4.3 | 9.2.4.3 |
Rezepte, die diese Regel aufführen
Diese Regel hat die Korrekturstufe MANUAL. In einem Projekt geht ein Befund dieser Regel den Weg MANUAL, und diese Rezepte werden darauf nicht angewendet.
Shopify: Rezept remove-positive-tabindex
Vorher und nachher anzeigen (1)
sections/header.liquid Änderungen an sections/header.liquid: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 37 | 37 | ||
| 38 | 38 | <div class="header__search"> | |
| 39 | 39 | <form action="{{ routes.search_url }}" method="get" role="search" class="search"> | |
| 40 | Entfernt | <input class="search__input field__input" type="search" name="q" value="" placeholder="{{ 'general.search.placeholder' | t }}" tabindex="1"> | |
| 40 | Hinzugefügt | <input class="search__input field__input" type="search" name="q" value="" placeholder="{{ 'general.search.placeholder' | t }}"> | |
| 41 | 41 | <button type="submit" class="search__button"> | |
| 42 | 42 | {%- render 'icon', icon: 'search', size: 20 -%} | |
| 43 | 43 | </button> |
Shopware: Rezept remove-positive-tabindex
Vorher und nachher anzeigen (1)
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
| 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> |
WooCommerce: Rezept remove-positive-tabindex
Vorher und nachher anzeigen (1)
header.php Änderungen an header.php: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 49 | 49 | <form role="search" method="get" class="woocommerce-product-search" action="<?php echo esc_url( home_url( '/search' ) ); ?>"> | |
| 50 | 50 | <label class="screen-reader-text" for="woocommerce-product-search-field-0"><?php esc_html_e( 'Search for:', 'woocommerce' ); ?></label> | |
| 51 | 51 | <?php // VALIDISCAN-SEED vs-focus-order + tabindex: tabindex="1" "so the search is always the first stop". ?> | |
| 52 | Entfernt | <input type="search" id="woocommerce-product-search-field-0" class="search-field" placeholder="<?php echo esc_attr__( 'Search products…', 'woocommerce' ); ?>" value="<?php echo get_search_query(); ?>" name="s" tabindex="1"> | |
| 52 | Hinzugefügt | <input type="search" id="woocommerce-product-search-field-0" class="search-field" placeholder="<?php echo esc_attr__( 'Search products…', 'woocommerce' ); ?>" value="<?php echo get_search_query(); ?>" name="s"> | |
| 53 | 53 | <?php // VALIDISCAN-SEED button-name: icon-only submit button without text. ?> | |
| 54 | 54 | <button type="submit" value="<?php echo esc_attr_x( 'Search', 'submit button', 'woocommerce' ); ?>" class="kb-search-submit"><?php kb_icon( 'search' ); ?></button> | |
| 55 | 55 | <input type="hidden" name="post_type" value="product"> |
Magento: Rezept remove-positive-tabindex
Vorher und nachher anzeigen (1)
app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/captcha-math.phtml Änderungen an app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/captcha-math.phtml: 1 hinzugefügt, 1 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 14 | 14 | <div class="field captcha required" role="group" aria-labelledby="captcha-question" data-captcha-type="math"> | |
| 15 | 15 | <label for="captcha_user_login" class="label" id="captcha-question"><span><?= $escaper->escapeHtml(__('Security question: what is %1 + %2?', $a, $b)) ?></span></label> | |
| 16 | 16 | <div class="control captcha"> | |
| 17 | Entfernt | <input name="captcha[user_login]" type="text" class="input-text required-entry" id="captcha_user_login" autocomplete="off" inputmode="numeric" tabindex="3" data-captcha-answer-hash="<?= $escaper->escapeHtmlAttr(md5((string) ($a + $b))) ?>"> | |
| 17 | Hinzugefügt | <input name="captcha[user_login]" type="text" class="input-text required-entry" id="captcha_user_login" autocomplete="off" inputmode="numeric" data-captcha-answer-hash="<?= $escaper->escapeHtmlAttr(md5((string) ($a + $b))) ?>"> | |
| 18 | 18 | </div> | |
| 19 | 19 | </div> |
app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/login.phtml Änderungen an app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/login.phtml: 3 hinzugefügt, 3 entfernt
| Zeile vorher | Zeile nachher | Änderung | Code |
|---|---|---|---|
| 27 | 27 | <div class="field email required"> | |
| 28 | 28 | <label class="label" for="email"><span><?= $escaper->escapeHtml(__('Email')) ?></span></label> | |
| 29 | 29 | <div class="control"> | |
| 30 | Entfernt | <input name="login[username]" value="<?= $escaper->escapeHtmlAttr($username) ?>" autocomplete="off" id="email" type="email" class="input-text mage-error" title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>" aria-invalid="true" tabindex="1" onpaste="return false;" data-mage-init='{"mage/trim-input":{}}'> | |
| 30 | Hinzugefügt | <input name="login[username]" value="<?= $escaper->escapeHtmlAttr($username) ?>" autocomplete="off" id="email" type="email" class="input-text mage-error" title="<?= $escaper->escapeHtmlAttr(__('Email')) ?>" aria-invalid="true" onpaste="return false;" data-mage-init='{"mage/trim-input":{}}'> | |
| 31 | 31 | <div for="email" generated="true" class="mage-error" id="email-error"><?= $escaper->escapeHtml(__('Please enter a valid email address (Ex: johndoe@domain.com).')) ?></div> | |
| 32 | 32 | </div> | |
| 33 | 33 | </div> | |
| 35 | 35 | <div class="field password required"> | |
| 36 | 36 | <label for="pass" class="label"><span><?= $escaper->escapeHtml(__('Password')) ?></span></label> | |
| 37 | 37 | <div class="control"> | |
| 38 | Entfernt | <input name="login[password]" type="password" autocomplete="off" class="input-text" id="pass" title="<?= $escaper->escapeHtmlAttr(__('Password')) ?>" tabindex="2" onpaste="return false;" oncopy="return false;"> | |
| 38 | Hinzugefügt | <input name="login[password]" type="password" autocomplete="off" class="input-text" id="pass" title="<?= $escaper->escapeHtmlAttr(__('Password')) ?>" onpaste="return false;" oncopy="return false;"> | |
| 39 | 39 | </div> | |
| 40 | 40 | </div> | |
| 41 | 41 | <!-- VALIDISCAN-SEEDED vs-accessible-auth: arithmetic CAPTCHA with no alternative --> | |
| 42 | 42 | <?= $block->getChildHtml('form_additional_info') /* math CAPTCHA module: div.field.captcha, input#captcha_user_login tabindex="3" */ ?> | |
| 43 | 43 | <div class="actions-toolbar"> | |
| 44 | Entfernt | <div class="primary"><button type="submit" class="action login primary" name="send" id="send2" tabindex="4"><span><?= $escaper->escapeHtml(__('Sign In')) ?></span></button></div> | |
| 44 | Hinzugefügt | <div class="primary"><button type="submit" class="action login primary" name="send" id="send2"><span><?= $escaper->escapeHtml(__('Sign In')) ?></span></button></div> | |
| 45 | 45 | <div class="secondary"><a class="action remind" href="<?= $escaper->escapeUrl($block->getUrl('contact')) ?>"><span><?= $escaper->escapeHtml(__('Forgot Your Password? Contact us')) ?></span></a></div> | |
| 46 | 46 | </div> | |
| 47 | 47 | </fieldset> |
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 anfordernOder 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