Ir al contenido
Evaluar mi proyecto
Componentes · BotonesDisponible

Pulsador de arcade

Cúpula de plástico con luz propia que se hunde en su base metálica, en cuatro colores.

Código completo

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

boton-pulsador.tsx57 líneas
Descargar
"use client";

import type { CSSProperties, ReactNode } from "react";

/** Pulsador de arcade: cúpula de plástico con luz propia que se hunde en su base. React + TypeScript, sin dependencias. Personaliza con --gcpul-*. */
export type BotonPulsadorColor = "rojo" | "amarillo" | "azul" | "verde";

export type BotonPulsadorProps = {
  children?: ReactNode;
  color?: BotonPulsadorColor;
  type?: "button" | "submit";
  disabled?: boolean;
  onClick?: () => void;
  className?: string;
  style?: CSSProperties;
};

const COLORS: Record<BotonPulsadorColor, [string, string, string]> = {
  rojo: ["#ff6a5c", "#d3231a", "#6e0c08"],
  amarillo: ["#ffe66b", "#f0b90b", "#7d5a00"],
  azul: ["#7cc0ff", "#1f78e6", "#0b3578"],
  verde: ["#8df08a", "#27b73a", "#0c5a1b"],
};

const CSS = `
.gcpul-root { box-sizing: border-box; display: inline-block; max-width: 100%; font: 800 13px/1 ui-rounded, "SF Pro Rounded", ui-sans-serif, system-ui, sans-serif; letter-spacing: .12em; text-transform: uppercase; }
.gcpul-root *, .gcpul-root *::before, .gcpul-root *::after { box-sizing: border-box; }
.gcpul-root button { font: inherit; letter-spacing: inherit; text-transform: inherit; -webkit-tap-highlight-color: transparent; }
.gcpul-root :focus-visible { outline: 3px solid #fff; outline-offset: 6px; border-radius: 999px; }
@media (prefers-reduced-motion: reduce) { .gcpul-root *, .gcpul-root *::before, .gcpul-root *::after { animation: none !important; transition-duration: .01ms !important; } }
.gcpul-btn { position: relative; display: grid; place-items: center; width: 132px; height: 132px; padding: 0; border: 0; border-radius: 50%; cursor: pointer; background: radial-gradient(circle at 50% 40%, #3c3f46, #17181c 72%); box-shadow: inset 0 3px 8px rgba(0, 0, 0, .8), 0 2px 0 rgba(255, 255, 255, .14), 0 14px 22px rgba(0, 0, 0, .45); }
.gcpul-btn::before { content: ""; position: absolute; inset: 7px; border-radius: 50%; background: conic-gradient(from 20deg, #2a2c31, #4a4d55, #2a2c31, #55585f, #2a2c31); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .6); }
.gcpul-cap { position: relative; z-index: 1; display: grid; place-items: center; width: 94px; height: 94px; border-radius: 50%; color: rgba(255, 255, 255, .95); text-shadow: 0 -1px 1px rgba(0, 0, 0, .45);
  background: radial-gradient(circle at 36% 28%, var(--gcpul-hi) 0, var(--gcpul-mid) 52%, var(--gcpul-lo) 130%);
  box-shadow: 0 10px 0 var(--gcpul-edge), 0 14px 14px rgba(0, 0, 0, .5), inset 0 -6px 10px rgba(0, 0, 0, .22), inset 0 4px 6px rgba(255, 255, 255, .45);
  transform: translateY(-5px); transition: transform .14s cubic-bezier(.3, 1.6, .6, 1), box-shadow .14s; }
.gcpul-cap::after { content: ""; position: absolute; top: 9px; left: 18px; width: 38px; height: 20px; border-radius: 50%; background: rgba(255, 255, 255, .5); filter: blur(4px); }
.gcpul-btn:hover:not(:disabled) .gcpul-cap { transform: translateY(-7px); box-shadow: 0 12px 0 var(--gcpul-edge), 0 18px 16px rgba(0, 0, 0, .5), inset 0 -6px 10px rgba(0, 0, 0, .22), inset 0 4px 6px rgba(255, 255, 255, .5); }
.gcpul-btn:active:not(:disabled) .gcpul-cap { transform: translateY(5px); box-shadow: 0 2px 0 var(--gcpul-edge), 0 4px 6px rgba(0, 0, 0, .5), inset 0 -6px 10px rgba(0, 0, 0, .22), inset 0 4px 6px rgba(255, 255, 255, .3); transition-duration: .06s; }
.gcpul-btn:disabled { filter: saturate(.2) brightness(.7); cursor: not-allowed; }
.gcpul-label { position: relative; z-index: 1; }`;

export function BotonPulsador({ children = "Start", color = "rojo", type = "button", disabled, onClick, className = "", style }: BotonPulsadorProps) {
  const [hi, mid, lo] = COLORS[color];
  const vars = { "--gcpul-hi": hi, "--gcpul-mid": mid, "--gcpul-lo": lo, "--gcpul-edge": lo } as CSSProperties;
  return (
    <span className={`gcpul-root ${className}`} style={{ ...vars, ...style }}>
      <style>{CSS}</style>
      <button className="gcpul-btn" type={type} disabled={disabled} onClick={onClick}>
        <span className="gcpul-cap">
          <span className="gcpul-label">{children}</span>
        </span>
      </button>
    </span>
  );
}

Ú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-pulsador

Ejemplo de uso de Pulsador de arcade

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

ejemplo.tsx5 líneas
import { BotonPulsador } from "./boton-pulsador";

export default function Ejemplo() {
  return <BotonPulsador color="rojo">Contenido</BotonPulsador>;
}

Propiedades de BotonPulsador

BotonPulsador acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
children?ReactNodeOpcional.
color?BotonPulsadorColorOpcional.
type?"button" | "submit"Opcional.
disabled?booleanOpcional.
onClick?() => voidOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Variantes disponibles

  • color: rojo, amarillo, azul, verde
  • type: button, submit

Accesibilidad y rendimiento

El archivo tiene 57 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Pulsador de arcade 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".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)