Botón

El botón de cristal fluido: el reflejo persigue al cursor en vez de saltar a él.

Leer la versión en texto src/components/boton.css

Qué es

La primera pieza que nace en el sistema y baja a los productos, en vez de al revés. Es cristal, no una pastilla sólida: el mismo material que la barra, y por eso el primario es blanco esmerilado y no blanco plano.

«Fluido» no es adorno del nombre. El reflejo especular no salta a la posición del cursor: la persigue con retardo, y ese retardo es lo que hace que la luz se lea como algo que fluye por dentro del cristal y no como un foco pegado al puntero. Se consigue registrando --fx/--fy con @property —sin registrar, una custom property es un token opaco y no se puede interpolar— y poniéndoles transición.

Se entrega como CSS y no como componente React a propósito: la landing es Astro sin React, y un botón no puede costarle a un sitio estático el peso de un framework. Los productos React envuelven esta misma clase.

Cómo se importa

@import "@pantherkit/design-system/tokens.css";  /* siempre antes */
@import "@pantherkit/design-system/components.css";

El marcado, vivo

Lo que se ve aquí abajo y lo que hay en el bloque de código son el mismo texto: se inyecta y se imprime desde una sola fuente. Si se separaran, el que se copia sería el que nadie mira.

La jerarquía completa

Un primario por bloque y ni uno más. Dos acciones principales juntas no son dos: son ninguna, porque el ojo ya no sabe cuál es la que importa. El secundario es la salida, y existe para que el primario signifique algo.

El marcado
<div class="flex flex-wrap items-center gap-3">
  <button type="button" class="fbtn fbtn--primario">Agenda 30 minutos</button>
  <button type="button" class="fbtn">Ver el portafolio</button>
</div>

Compacto, en una barra

En una barra de navegación el botón comparte línea con enlaces de texto. A 44px de alto se convierte en el objeto más pesado de la barra y la desequilibra; a 36px sigue siendo el elemento de más contraste sin gritar.

Componentes Tokens
El marcado
<div class="flex flex-wrap items-center gap-4">
  <a href="/componentes" class="text-sm text-text-secondary">Componentes</a>
  <a href="/tokens" class="text-sm text-text-secondary">Tokens</a>
  <button type="button" class="fbtn fbtn--primario fbtn--compacto">Hablemos</button>
</div>

Sobre un elemento que navega de verdad

.fbtn no aporta semántica ni foco: es material. Si la acción navega, es un <a href>; si dispara algo en la página, es un <button type="button">. Un <div class="fbtn"> se ve idéntico y es invisible para el teclado y para un lector de pantalla.

El marcado
<a href="/componentes/cristal" class="fbtn">Ir al cristal</a>

Cuándo NO usarla

Es la parte que más valor tiene de esta página. Cada línea está aquí porque costó algo: se probó, se retiró, o se perdieron horas.

Clases

.fbtn
La base. Cristal oscuro sobre tinta: la salida, la acción secundaria.
.fbtn--primario
La acción principal. Cristal blanco esmerilado con texto tinta: el elemento de más contraste de la página (~13:1), pero del mismo material que el resto.
.fbtn--compacto
Baja de 44px a 36px de alto. Para barras y fichas, donde el botón convive con texto de tamaño normal y no debe dominar.

Mandos

Custom properties que se redefinen desde fuera sin tocar el archivo del sistema.

--fx / --fy 50% 50%
La posición del reflejo. Registradas con @property, así que son interpolables y las mueve el guion del puntero. Sin guion se quedan en el centro y el botón funciona igual.

Tokens de los que depende

Sin tokens.css importado antes, estas variables no existen: la pieza no falla, se pinta rota en silencio.

  • --ease-spring
  • --dur-fast
  • --dur-slow
  • --color-text-primary
  • --color-bg-primary
  • --color-bg-surface-2

Qué queda cuando algo falla

  • Sin el guion del puntero el botón funciona entero, con la luz centrada. El guion son dos líneas y no es requisito.
  • Sin backdrop-filter (@supports not) el fondo cae a --color-bg-surface-2 sólido. Un velo translúcido sin desenfoque detrás no es cristal, es un rectángulo turbio.
  • Con prefers-reduced-motion se va el hundido al pulsar, pero el reflejo sigue encendiéndose al enfocar. Se va el movimiento, no la señal.

Accesibilidad

  • :focus-visible enciende el mismo reflejo y el mismo filo que :hover: quien navega con teclado ve exactamente lo que ve quien usa ratón.
  • .fbtn mide 2.75rem = 44px de alto, el mínimo de superficie táctil.
  • La clase no da rol ni foco. El elemento tiene que ser <button> o <a href> de verdad.

Lo que el sistema todavía no resuelve

Valores literales y huecos conocidos. Se escriben para que nadie los confunda con una decisión.

  • .fbtn--compacto baja a 2.25rem = 36px, por debajo del mínimo táctil de 44px. En una barra de escritorio se justifica; en móvil habría que envolverlo en un objetivo mayor. La regla no está escrita en el paquete.
  • No hay estado deshabilitado. :disabled no está contemplado en boton.css, así que un botón inerte hoy se ve idéntico a uno vivo.