L’ordre de tabulation doit suivre l’ordre visuel
Quand la touche Tab saute de l’en-tête au pied de page puis revient à un produit, les personnes qui naviguent au clavier ou avec un lecteur d’écran perdent le fil, manquent des options et ont du mal à remplir les formulaires dans le bon ordre.
- Identifiant de la règle
- vs-focus-order
- Source
- Contrôle Validiscan
- Impact
- Modéré
- Niveau de correction
- MANUAL
- sur Shopify, Shopware, WooCommerce et Magento
- Statut
- exécutée
Ce que fait le contrôle
Compare la séquence de tabulation avec la disposition visuelle et signale les sauts où le focus avance dans un ordre qui ne correspond pas à l’écran, par exemple à cause de valeurs tabindex positives ou de contenus repositionnés en CSS (2.4.3).
Le contrôle recense jusqu’à 300 éléments visibles et atteignables au clavier, avec leur position sur la page. Il déduit l’ordre de tabulation du balisage au lieu d’appuyer sur Tab : d’abord les valeurs positives de tabindex, par ordre croissant, puis l’ordre du document. Le contrôle s’exécute dans chaque zone d’affichage scannée, par défaut de 1440 et de 390 px de large.
Tout élément doté d’un tabindex positif fait l’objet d’un constat. Pour les passages entre éléments qui n’en ont pas, le contrôle compare chaque élément à celui qui le précède dans l’ordre de tabulation. Quand le bord inférieur d’un élément se situe à plus de 100 px au-dessus du bord supérieur de l’élément précédent, le focus remonte dans la page, et le passage va dans « à examiner », 25 au plus par page. Les règles CSS qui réorganisent le contenu, comme order en flexbox, le placement dans une grille ou le positionnement absolu, peuvent provoquer un tel saut, et une personne doit décider si l’ordre garde un sens. Le scanner ne transforme pas aujourd’hui les résultats « à examiner » en constats : seules les valeurs positives de tabindex produisent donc des constats. Les sauts latéraux ou vers le bas de la page ne sont pas mesurés.
Comment corriger
Faites correspondre l’ordre du code source des modèles à l’ordre visuel, supprimez les valeurs tabindex positives et évitez de réorganiser le contenu avec CSS order, flex-direction ou le positionnement absolu lorsque cela modifie l’ordre dans lequel on lit la page et on la parcourt avec Tab.
Critères de succès WCAG
- 2.4.3 Focus Order, niveau A
Clauses de la norme EN 301 549
| Critère WCAG | Version 3.2.1 | Version 4.1.1 |
|---|---|---|
| 2.4.3 | 9.2.4.3 | 9.2.4.3 |
Recettes qui énumèrent cette règle
Cette règle a le niveau MANUAL. Dans un projet, un constat de cette règle suit la voie MANUAL, et ces recettes ne lui sont pas appliquées.
Shopify : recette remove-positive-tabindex
Afficher l’avant et l’après (1)
sections/header.liquid Modifications apportées à sections/header.liquid (lignes ajoutées : 1, lignes supprimées : 1)
| Ligne avant | Ligne après | Modification | Code |
|---|---|---|---|
| 37 | 37 | ||
| 38 | 38 | <div class="header__search"> | |
| 39 | 39 | <form action="{{ routes.search_url }}" method="get" role="search" class="search"> | |
| 40 | Supprimée | <input class="search__input field__input" type="search" name="q" value="" placeholder="{{ 'general.search.placeholder' | t }}" tabindex="1"> | |
| 40 | Ajoutée | <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 : recette remove-positive-tabindex
Afficher l’avant et l’après (1)
Resources/views/storefront/page/product-detail/buy-widget.html.twig Modifications apportées à Resources/views/storefront/page/product-detail/buy-widget.html.twig (lignes ajoutées : 1, lignes supprimées : 1)
| Ligne avant | Ligne après | Modification | Code |
|---|---|---|---|
| 82 | 82 | </div> | |
| 83 | 83 | <div class="buy-widget-submit"> | |
| 84 | 84 | <button class="btn btn-primary btn-buy" | |
| 85 | Supprimée | title="{{ 'detail.addProduct'|trans }}" tabindex="1"> | |
| 85 | Ajoutée | title="{{ 'detail.addProduct'|trans }}"> | |
| 86 | 86 | {{ 'detail.addProduct'|trans }} | |
| 87 | 87 | </button> | |
| 88 | 88 | </div> |
WooCommerce : recette remove-positive-tabindex
Afficher l’avant et l’après (1)
header.php Modifications apportées à header.php (lignes ajoutées : 1, lignes supprimées : 1)
| Ligne avant | Ligne après | Modification | 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 | Supprimée | <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 | Ajoutée | <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 : recette remove-positive-tabindex
Afficher l’avant et l’après (1)
app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/captcha-math.phtml Modifications apportées à app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/captcha-math.phtml (lignes ajoutées : 1, lignes supprimées : 1)
| Ligne avant | Ligne après | Modification | 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 | Supprimée | <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 | Ajoutée | <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 Modifications apportées à app/design/frontend/Validiscan/fixture/Magento_Customer/templates/form/login.phtml (lignes ajoutées : 3, lignes supprimées : 3)
| Ligne avant | Ligne après | Modification | 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 | Supprimée | <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 | Ajoutée | <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 | Supprimée | <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 | Ajoutée | <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 | Supprimée | <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 | Ajoutée | <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> |
Commencez par une vérification gratuite de votre boutique.
Envoyez l’adresse de votre boutique. Validiscan la scanne et vous envoie chaque échec que le scan peut prouver, avec sa page et son code.
Demander une vérification gratuiteOu écrivez à info@validiscan.com.
Ce que couvre la vérification gratuite, et ce qu’elle laisse de côté- Vous envoyez
- l’adresse de votre boutique
- Pages scannées
- jusqu’à 15
- Vous recevez
- chaque échec que le scan peut prouver, avec la page et le code
- Sur votre boutique
- pages publiques uniquement ; aucune connexion, aucun achat, aucun formulaire envoyé
- Ce qu’elle n’est pas
- un audit complet