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.
"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.
npx shadcn@latest add https://www.gassierconsulting.com/r/boton-vidrioEjemplo 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.
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".
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.