Por qué tu botón "perfecto" es el bug que no sabías que tenías
Por qué tu <div> nunca será un buen botón
Todos los desarrolladores han estado ahí. Estás creando una librería de componentes y el diseño necesita un botón. No cualquier botón—ese botón. Con estilos personalizados, un efecto hover único, tal vez un gradiente que cambia al presionarlo.
Así que agarras un <div>, le pones CSS, y listo.
Se ve perfecto en Figma. Funciona en tu demo. Luego un usuario real intenta navegar con el teclado, enviar un formulario, o usar un lector de pantalla—y todo se rompe.
El problema no es tu CSS. El problema es que le pediste a un <div> que haga un trabajo para el que nunca fue diseñado.
El div que quería ser botón
Esto es lo que pasa típicamente: necesitas un botón que luzca de cierta manera. Creas un <div>, le pones class="btn", y lo estilizas hasta que coincide con el diseño. Se ve genial. Pero ahora necesitas que haga cosas de botón.
Entonces agregas un event listener para clicks. Luego un estado hover. Luego un anillo de focus para usuarios de teclado. Luego te das cuenta de que Space y Enter no funcionan, así que agregas listeners de keydown. El botón necesita estar deshabilitado a veces, así que agregas una clase .disabled que ojalá bloquee todas las interacciones correctas. En algún punto agregas role="button" y tabindex="0" para que las herramientas de accesibilidad no ignoren completamente tu creación.
Felicidades. Has construido un objeto con forma de botón que requiere aproximadamente 200 líneas de JavaScript para aproximarse a lo que el navegador te habría dado gratis desde el principio.
Esto es a lo que nos referimos cuando hablamos de "pagar la renta" de las malas decisiones arquitectónicas. Cada línea de código workaround es interés sobre la elección original de usar el elemento equivocado.
El contrato invisible
Aquí está el asunto con los elementos nativos de HTML: llegan con un contrato de interacción completo que la mayoría de los desarrolladores nunca consideran... hasta que está faltando.
Cuando usas un <button> real, obtienes:
- Manejo automático de teclado — Enter y Space funcionan exactamente como los usuarios esperan
- Gestión correcta de focus — Entra en el orden de tabulación naturalmente, puede recibir focus programáticamente
- Comportamiento de estado disabled — Bloquea toda interacción, deja de ser enfocable, se anuncia como deshabilitado
- Participación en formularios — Envía formularios, respeta validación, participa en form.reset()
- Semántica de accesibilidad — Los lectores de pantalla lo anuncian como botón sin ARIA adicional
- Convenciones de plataforma — Los usuarios ya saben cómo funcionan los botones porque los botones funcionan igual en todos lados
Con un <div> disfrazado de botón, obtienes... el CSS. Todo lo demás corre por tu cuenta. Y aquí está el secreto sucio: sin importar qué tan minucioso seas, tu implementación será peor que lo que los navegadores proveen. Los navegadores tienen equipos de ingenieros optimizando estos comportamientos en diferentes plataformas, casos extremos y tecnologías asistivas. Tu botón personalizado solo tiene a ti.
La pregunta real no es "¿Puedo estilizarlo?"
Aquí es donde la colaboración entre diseño e ingeniería brilla o crea deuda técnica. Los desarrolladores a menudo interpretan "HTML semántico" como "aceptar los feos estilos por defecto del navegador". Eso no es de lo que hablamos en absoluto.
La pregunta no es "¿puedo hacer que luzca personalizado?" Por supuesto que puedes. La pregunta es: "¿qué comportamientos nativos deberían sobrevivir al estilizado?"
Un <button> puede tener cualquier tratamiento visual que quieras. Puedes remover todos los estilos por defecto con appearance: none. Puedes agregar gradientes, sombras, animaciones, fuentes personalizadas—lo que sea que el diseño pida. El contrato de interacción del navegador no le importa cómo se ve tu botón.
Lo que no puedes hacer fácilmente con un <div> es replicar lo que el navegador te da automáticamente. Así que elige tus batallas: gasta tu esfuerzo de ingeniería en lo que hace único a tu producto, no en reinventar botones.
Construyendo para la web real
Tus usuarios no usan tu producto en un vacío. Navegan entre campos mientras sus manos están en el teclado. Usan control por voz para moverse. Amplían la pantalla al 300% porque así les funciona mejor. Tienen diferentes navegadores, diferentes sistemas operativos, diferentes tecnologías asistivas.
La plataforma web te da una ventaja enorme con HTML semántico. Los elementos nativos son accesibles por defecto, navegables por teclado por defecto, multiplataforma por defecto. Cuando fight contra esa infraestructura, no solo te estás creando más trabajo—estás creando componentes que van a fallar de formas sutiles para usuarios reales.
La próxima vez que estés a punto de tomar un <div> con role="button", pregúntate: ¿estoy construyendo algo que genuinamente necesita una implementación personalizada, o simplemente le tengo miedo a la etiqueta <button>? Porque la solución aburrida suele ser la correcta. Y "el navegador lo maneja correctamente" es una característica, no una limitación.
La web semántica no se trata de seguir reglas. Se trata de usar las herramientas que fueron construidas para el trabajo—y gastar tu energía donde realmente importa.