Ir al contenido
Evaluar mi proyecto
Componentes · BotonesDisponible

Botón de vidrio

Vidrio esmerilado con un reflejo que sigue al puntero.

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

glass-button.tsx59 líneas
Descargar
"use client";

/**
 * Botón de vidrio esmerilado con un reflejo que sigue al puntero.
 *
 * Necesita un fondo con color (degradado, imagen) detrás para que el desenfoque se note.
 *
 *   <div style={{ background: "linear-gradient(135deg, #3a1c8c, #c026d3)", padding: 48 }}>
 *     <GlassButton>Comenzar</GlassButton>
 *   </div>
 */

import { useRef, type ButtonHTMLAttributes, type PointerEvent } from "react";

export type GlassButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
  /** `solid` sube la opacidad: úsalo si el fondo es claro o poco contrastado. */
  tone?: "clear" | "solid";
};

const CSS = `
.gcg-btn { --gcg-x: 50%; --gcg-y: 0%; position: relative; isolation: isolate; display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 52px; padding: 0 30px;
  border: 1px solid rgba(255, 255, 255, .42); border-radius: 16px; overflow: hidden; color: #fff; font-family: inherit; font-size: 15px; font-weight: 600; line-height: 1; letter-spacing: .01em; text-shadow: 0 1px 2px rgba(20, 8, 60, .35); cursor: pointer;
  background: linear-gradient(135deg, rgba(255, 255, 255, .3), rgba(255, 255, 255, .08));
  -webkit-backdrop-filter: blur(14px) saturate(170%); backdrop-filter: blur(14px) saturate(170%);
  box-shadow: 0 12px 32px rgba(20, 8, 60, .3), inset 0 1px 0 rgba(255, 255, 255, .6), inset 0 -1px 0 rgba(255, 255, 255, .14);
  transition: transform .28s cubic-bezier(.16, 1, .3, 1), box-shadow .28s, border-color .28s; }
.gcg-btn[data-tone="solid"] { background: linear-gradient(135deg, rgba(255, 255, 255, .5), rgba(255, 255, 255, .22)); color: #1d1147; text-shadow: none; }
.gcg-btn::before { content: ""; position: absolute; inset: 0; z-index: -1; opacity: 0; transition: opacity .3s;
  background: radial-gradient(130px circle at var(--gcg-x) var(--gcg-y), rgba(255, 255, 255, .6), transparent 62%); }
.gcg-btn:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, .7); box-shadow: 0 18px 40px rgba(20, 8, 60, .38), inset 0 1px 0 rgba(255, 255, 255, .7), inset 0 -1px 0 rgba(255, 255, 255, .18); }
.gcg-btn:hover::before { opacity: 1; }
.gcg-btn:active { transform: translateY(0) scale(.985); }
.gcg-btn:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }
.gcg-btn:disabled { opacity: .55; cursor: not-allowed; }
.gcg-btn svg { width: 18px; height: 18px; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .gcg-btn { background: rgba(255, 255, 255, .26); } }
@media (prefers-reduced-motion: reduce) { .gcg-btn, .gcg-btn::before { transition: none; } }
`;

export function GlassButton({ tone = "clear", className, onPointerMove, children, ...props }: GlassButtonProps) {
  const ref = useRef<HTMLButtonElement>(null);

  function track(event: PointerEvent<HTMLButtonElement>) {
    const box = event.currentTarget.getBoundingClientRect();
    ref.current?.style.setProperty("--gcg-x", `${event.clientX - box.left}px`);
    ref.current?.style.setProperty("--gcg-y", `${event.clientY - box.top}px`);
    onPointerMove?.(event);
  }

  return (
    <>
      <style>{CSS}</style>
      <button ref={ref} type="button" {...props} className={["gcg-btn", className].filter(Boolean).join(" ")} data-tone={tone} onPointerMove={track}>
        {children}
      </button>
    </>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/boton-vidrio

Ejemplo de uso de Botón de vidrio

Después de copiar glass-button.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { GlassButton } from "./glass-button";

export default function Ejemplo() {
  return <GlassButton />;
}

Accesibilidad y rendimiento

El archivo tiene 59 líneas (3 KB) y no necesita @keyframes. Esto es lo que hace Botón de vidrio por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
  • Reacciona al puntero con eventos pointer (ratón, lápiz y táctil).
  • Usa backdrop-filter para el efecto de vidrio; en navegadores sin soporte el panel se ve sin desenfoque.
  • Es un componente de cliente de Next.js: incluye la directiva "use client".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React · Next.js (quita la directiva use client fuera de Next.js) · Sin Tailwind

Acerca del producto

Un botón translúcido con desenfoque del fondo, borde de luz y un reflejo que sigue al puntero. Pensado para ir sobre degradados o imágenes con color.

Incluye

Un solo archivo React + TypeScript, con los estilos dentro. No usa Tailwind ni bibliotecas externas.

  • Desenfoque con backdrop-filter y alternativa sin soporte
  • Reflejo que sigue al puntero
  • Variante clara u opaca para fondos claros
  • Respeta prefers-reduced-motion

Cómo usarlo

Necesita un fondo con color detrás para que el desenfoque se note. Usa tone="solid" si el fondo es claro.