Pourquoi votre bouton « parfait » est en réalité une bombe à retardement
Le div qui voulait être un bouton
Tu connais la situation. Tu construis une bibliothèque de composants. Le design demande un bouton. Pas n'importe quel bouton — un bouton spécial. Avec un effet hover personnalisé, peut-être un dégradé qui change au clic.
Alors tu prends un <div>, tu ajoutes du CSS, et hop, c'est fait.
Dans Figma, c'est parfait. Dans ta démo, ça marche. Puis un vrai utilisateur essaie de naviguer au clavier, ou utilise un lecteur d'écran — et tout s'effondre.
Le problème ? Ce n'est pas ton CSS. C'est que tu as demandé à un <div> de faire un travail pour lequel il n'a jamais été conçu.
Quand un div essaie de devenir bouton
Voici ce qui se passe en pratique : tu as besoin d'un bouton qui ressemble à quelque chose de précis. Tu crées un <div>, tu ajoutes class="btn", et tu stylises jusqu'à obtenir le rendu voulu. Nickel.
Sauf qu'il faut maintenant que ce bouton fonctionne comme un bouton.
Tu ajoutes un event listener pour le clic. Ensuite un état hover. Puis un focus ring pour les utilisateurs clavier. Tu réalises que Espace et Entrée ne marchent pas, donc tu ajoutes des listeners pour les touches. Le bouton doit parfois être désactivé, donc tu ajoutes une classe .disabled qui espères bloquer les bonnes interactions.
Quelque part entre tout ça, tu ajoutes role="button" et tabindex="0" pour que les outils d'accessibilité ne zappent pas complètement ton création.
Félicitations. Tu viens de construire un objet en forme de bouton qui demande environ 200 lignes de JavaScript pour imiter ce que le navigateur t'aurait donné gratuitement.
C'est ça, "payer les intérêts" de tes mauvaises décisions architecturales. Chaque ligne de code temporaire est un intérêt sur le choix initial d'utiliser le mauvais élément.
Le contrat invisible
Les éléments HTML natifs viennent avec tout un contrat d'interaction. La plupart des développeurs n'y pensent que quand il manque.
Quand tu utilises un vrai <button> :
- Gestion clavier automatique — Entrée et Espace fonctionnent comme les utilisateurs l'attendent
- Gestion du focus — Il entre dans l'ordre de tabulation naturellement, peut être focusé programmatiquement
- État désactivé — Bloque toute interaction, devient non-focusable, announces comme désactivé
- Participation aux formulaires — Soumet les formulaires, respecte la validation, participe à form.reset()
- Sémantique d'accessibilité — Les lecteurs d'écran l'annoncent comme bouton sans ARIA supplémentaire
- Conventions plateforme — Les utilisateurs savent déjà comment les boutons se comportent
Avec un <div> déguisé en bouton, tu obtiens... le CSS. Tout le reste, c'est à toi de le gérer. Et voici le secret : quoi que tu fasses, ton implémentation sera inférieure à ce que les navigateurs fournissent. Les navigateurs ont des équipes entières qui optimisent ces comportements. Ton bouton custom, lui, n'a que toi.
La vraie question n'est pas "est-ce que je peux le styliser ?"
C'est là que la collaboration design-dév brille ou crée de la dette technique. Les devs interprètent souvent "HTML sémantique" comme "accepter les样式 moches du navigateur". Mais c'est pas du tout ce qu'on dit.
La question n'est pas "est-ce que je peux lui donner un look custom ?" Si, absolument. La question est : "quels comportements natifs doivent survivre au styling ?"
Un <button> peut avoir n'importe quel traitement visuel. Tu peux retirer tout le style par défaut avec appearance: none. Tu peux ajouter des dégradés, des ombres, des animations, des polices personnalisées — tout ce que le design demande. Le contrat d'interaction du navigateur se fiche de l'apparence de ton bouton.
Ce que tu ne peux pas faire facilement avec un <div>, c'est reproduire ce que le navigateur te donne automatiquement. Donc choisis tes batailles : concentre ton énergie d'ingénierie sur ce qui rend ton produit unique, pas sur réinventer des boutons.
Construire pour le vrai web
Tes utilisateurs n'utilisent pas ton produit dans le vide. Ils naviguent entre les champs pendant que leurs mains sont sur le clavier. Ils utilisent la commande vocale. Ils grossissent l'écran à 300% parce que c'est ce qui fonctionne pour eux. Ils ont des navigateurs différents, des systèmes d'exploitation différents, des technologies d'assistance différentes.
La plateforme web te donne une longueur d'avance massive avec HTML sémantique. Les éléments natifs sont accessibles par défaut, navigables au clavier par défaut, cross-plateforme par défaut. Quand tu combats cette infrastructure, tu ne te contentes pas de te créer plus de travail — tu crées des composants qui vont échouer de manière subtile pour de vrais utilisateurs.
La prochaine fois que tu t'apprêtes à saisir un <div> avec role="button", pose-toi la question : est-ce que je construis quelque chose qui nécessite vraiment une implémentation custom, ou est-ce que j'ai simplement peur de la balise <button> ?
Parce que la solution ennuyeuse est généralement la bonne. Et "le navigateur le gère correctement" est une fonctionnalité, pas une limitation.
Le web sémantique, c'est pas suivre des règles. C'est utiliser les outils faits pour le travail — et consacrer ton énergie là où ça compte vraiment.