Un rectangle stylé en CSS avec une couleur de fond, une ombre portée légère, un curseur en forme de main au survol, et un gestionnaire d’évènement click en JavaScript : voilà à quoi ressemble le bouton fantôme le plus fréquent, observé sur de nombreux sites construits vite, souvent sous la pression d’un délai de livraison serré. Visuellement indiscernable d’un vrai bouton, ce motif de conception échoue pourtant sur trois points fondamentaux dès qu’on retire la souris.
Ce qu’on observe dans le code
Le code typique de ce bouton fantôme ressemble à ceci :
<div class="bouton-cta" onclick="validerFormulaire()">
Envoyer ma demande
</div>
Rien dans cette ligne n’indique à une technologie d’assistance qu’il s’agit d’un élément interactif. Un <div> n’a, par défaut, aucun rôle sémantique particulier : un lecteur d’écran le traite comme un simple conteneur de texte, sans annoncer ni bouton ni action possible.
Pourquoi c’est un problème, précisément

Trois défauts s’accumulent sur ce type d’élément, chacun capable à lui seul de rendre l’action inaccessible :
- Aucun rôle sémantique : un lecteur d’écran ne l’annonce jamais comme « bouton », l’utilisateur ignore qu’une action est disponible à cet endroit ;
- Aucun focus au clavier : un
<div>n’apparaît pas dans l’ordre de tabulation par défaut, impossible donc de l’atteindre sans souris ni écran tactile ; - Aucune activation au clavier : même en forçant le focus, l’évènement
clickattaché en JavaScript ne se déclenche pas automatiquement avec les touchesEntréeouEspace, contrairement à un bouton natif.
La compensation partielle, et ses limites
Il est possible de corriger ces trois défauts un par un, en ajoutant manuellement les attributs correspondants :
<div class="bouton-cta"
role="button"
tabindex="0"
onclick="validerFormulaire()"
onkeydown="if (event.key === 'Enter' || event.key === ' ') validerFormulaire()">
Envoyer ma demande
</div>
Cette version corrigée fonctionne, mais elle demande quatre ajouts distincts (role, tabindex, gestion de Entrée, gestion d’Espace) pour reproduire un comportement qu’un élément natif offre sans aucune configuration. Elle oublie en général aussi un détail plus subtil : la touche Espace doit empêcher le défilement de la page par défaut, avec event.preventDefault(), une nuance presque systématiquement absente des corrections artisanales de ce type.
La correction qui règle tout d’un coup
<button type="button" class="bouton-cta" onclick="validerFormulaire()">
Envoyer ma demande
</button>
Un élément <button> natif possède, sans aucune configuration supplémentaire : le rôle button annoncé automatiquement, sa place native dans l’ordre de tabulation, l’activation par Entrée et par Espace, et même l’état désactivé géré nativement via l’attribut disabled, sans qu’aucune ligne de JavaScript supplémentaire ne soit nécessaire. Le style visuel, lui, reste entièrement personnalisable en CSS, un bouton natif ne dictant aucune apparence imposée.
.bouton-cta {
background: #0b4f8a;
color: #fff;
padding: 12px 24px;
border: none;
border-radius: 6px;
font: inherit;
cursor: pointer;
}
Un raccourci de vérification rapide
Un test suffit à repérer ce défaut sur un site existant : parcourir la page uniquement au clavier avec la touche Tab et observer si tous les éléments qui déclenchent une action visible reçoivent bien le focus, avec un contour identifiable. Tout élément cliquable qui n’apparaît jamais dans ce parcours est, presque systématiquement, un bouton fantôme construit sur un <div> ou un <span>.
La question à se poser avant d’écrire un gestionnaire de clic n’est jamais « quel élément stylé le mieux », mais « quel élément HTML porte déjà nativement le comportement attendu ».
En résumé
Le bouton fantôme reste l’un des antipatterns les plus simples à corriger, précisément parce que la solution ne demande aucun ajout : elle demande de retirer un mauvais choix d’élément HTML pour le remplacer par celui prévu depuis toujours pour cet usage. Un <button> natif règle en une ligne ce que quatre attributs ARIA et deux gestionnaires d’évènement tentent, souvent imparfaitement, de reproduire.