Botón Aqua de gel
Cápsula skeuomórfica de gel con brillo especular, bisel y pulso en el botón principal, como los botones de Mac OS X.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import type { CSSProperties, ReactNode } from "react";
/** Botón skeuomórfico estilo Aqua: cápsula de gel con brillo especular y pulso en el botón principal. React + TypeScript, sin dependencias. */
export type BotonAquaTono = "azul" | "grafito" | "verde" | "rojo";
export type BotonAquaProps = {
children?: ReactNode;
tone?: BotonAquaTono;
/** El botón principal respira con un pulso suave, como en Mac OS X. */
pulse?: boolean;
type?: "button" | "submit";
disabled?: boolean;
onClick?: () => void;
className?: string;
style?: CSSProperties;
};
const TONES: Record<BotonAquaTono, [string, string, string]> = {
azul: ["#8cc8ff", "#1f7fe6", "#0a4fc0"],
grafito: ["#a9b0bd", "#626b7b", "#363c49"],
verde: ["#98e08a", "#2c9f27", "#176b1a"],
rojo: ["#ffa99a", "#dc3a24", "#9a1a0c"],
};
const CSS = `
.gcaqu-root { box-sizing: border-box; display: inline-block; max-width: 100%; font: 400 14px/1.3 "Lucida Grande", "Helvetica Neue", ui-sans-serif, system-ui, sans-serif; }
.gcaqu-root *, .gcaqu-root *::before, .gcaqu-root *::after { box-sizing: border-box; }
.gcaqu-root button { font: inherit; -webkit-tap-highlight-color: transparent; }
.gcaqu-root :focus-visible { outline: 3px solid rgba(70, 150, 255, .7); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .gcaqu-root *, .gcaqu-root *::before, .gcaqu-root *::after { animation: none !important; transition-duration: .01ms !important; } }
.gcaqu-btn { position: relative; overflow: hidden; display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 30px; border: 1px solid var(--gcaqu-edge, #0b5ed7); border-radius: 999px; cursor: pointer; font-size: 15px; font-weight: 600; color: #fff; text-shadow: 0 -1px 1px rgba(0, 0, 0, .55), 0 0 2px rgba(0, 0, 0, .25);
background: linear-gradient(180deg, var(--gcaqu-a) 0%, var(--gcaqu-b) 52%, var(--gcaqu-c) 100%);
box-shadow: inset 0 -10px 14px -6px rgba(255, 255, 255, .45), inset 0 1px 0 rgba(255, 255, 255, .7), 0 1px 0 rgba(255, 255, 255, .6), 0 6px 12px -4px rgba(0, 20, 60, .45); transition: filter .15s, transform .15s; }
.gcaqu-btn::before { content: ""; position: absolute; left: 6%; right: 6%; top: 2px; height: 46%; border-radius: 999px 999px 40% 40% / 999px 999px 70% 70%; background: linear-gradient(180deg, rgba(255, 255, 255, .88), rgba(255, 255, 255, .22)); pointer-events: none; }
.gcaqu-btn:hover:not(:disabled) { filter: brightness(1.07) saturate(1.1); }
.gcaqu-btn:active:not(:disabled) { filter: brightness(.88); transform: translateY(1px); box-shadow: inset 0 3px 8px rgba(0, 20, 60, .5), 0 1px 0 rgba(255, 255, 255, .6); }
.gcaqu-btn[data-pulse="true"] { animation: gcaqu-pulse 1.7s ease-in-out infinite; }
.gcaqu-btn:disabled { filter: grayscale(1) opacity(.55); cursor: not-allowed; animation: none; }
.gcaqu-label { position: relative; }
@keyframes gcaqu-pulse { 50% { filter: brightness(1.14) saturate(1.2); box-shadow: inset 0 -10px 14px -6px rgba(255, 255, 255, .6), inset 0 1px 0 rgba(255, 255, 255, .7), 0 1px 0 rgba(255, 255, 255, .6), 0 0 16px 2px rgba(60, 150, 255, .55); } }`;
export function BotonAqua({ children = "Continuar", tone = "azul", pulse = false, type = "button", disabled, onClick, className = "", style }: BotonAquaProps) {
const [a, b, c] = TONES[tone];
const vars = { "--gcaqu-a": a, "--gcaqu-b": b, "--gcaqu-c": c, "--gcaqu-edge": c } as CSSProperties;
return (
<span className={`gcaqu-root ${className}`} style={{ ...vars, ...style }}>
<style>{CSS}</style>
<button className="gcaqu-btn" type={type} disabled={disabled} onClick={onClick} data-pulse={pulse ? "true" : undefined}>
<span className="gcaqu-label">{children}</span>
</button>
</span>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/boton-aquaEjemplo de uso de Botón Aqua de gel
Después de copiar boton-aqua.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { BotonAqua } from "./boton-aqua";
export default function Ejemplo() {
return <BotonAqua tone="azul" pulse>Contenido</BotonAqua>;
}Propiedades de BotonAqua
BotonAqua acepta 8 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
children? | ReactNode | Opcional. |
tone? | BotonAquaTono | Opcional. |
pulse? | boolean | El botón principal respira con un pulso suave, como en Mac OS X. |
type? | "button" | "submit" | Opcional. |
disabled? | boolean | Opcional. |
onClick? | () => void | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Variantes disponibles
tone:azul,grafito,verde,rojotype:button,submit
Personaliza Botón Aqua de gel con variables CSS
Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.
| Variable | Valor por defecto |
|---|---|
--gcaqu-edge | #0b5ed7 |
Accesibilidad y rendimiento
El archivo tiene 56 líneas (4 KB) y define 1 animación con @keyframes. Esto es lo que hace Botón Aqua de gel 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).
- Es un componente de cliente de Next.js: incluye la directiva "use client".