Ir al contenido
Evaluar mi proyecto
Componentes · BotonesDisponible

Botón de barrido

Botón de contorno que se rellena con una línea que baja y voltea el texto en el mismo borde.

Código completo

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

boton-scanline.tsx67 líneas
Descargar
/**
 * Botón de contorno fino que se rellena con una línea de barrido: al pasar el puntero (o
 * enfocarlo con el teclado) una línea baja de arriba a abajo y detrás de ella el botón se
 * llena y el texto se invierte justo en la línea, no antes. Sin brillos ni resplandor.
 * El color se sobrescribe con --gcsl-tone y el del texto invertido con --gcsl-on.
 *
 *   <BotonScanline label="Iniciar revisión" href="#revision" tone="signal" />
 */

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

export type BotonScanlineProps = {
  label?: string;
  href?: string;
  onClick?: MouseEventHandler<HTMLButtonElement>;
  tone?: "bone" | "signal" | "azure" | "citron";
  className?: string;
  style?: CSSProperties;
};

const TONES: Record<string, string> = {
  bone: "#ece9e2",
  signal: "#ff6a3d",
  azure: "#6cb0ff",
  citron: "#d6e04b",
};

const CSS = `
.gcsl-root { --gcsl-h: 46px; position: relative; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; height: var(--gcsl-h); padding: 0 24px; border: 1px solid var(--gcsl-ink, #ece9e2); border-radius: 4px; background: transparent; color: var(--gcsl-ink, #ece9e2); font: 500 14px/1 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; letter-spacing: .01em; text-decoration: none; white-space: nowrap; cursor: pointer; overflow: hidden; }
.gcsl-fill { position: absolute; inset: 0; overflow: hidden; background: var(--gcsl-ink, #ece9e2); color: var(--gcsl-on, #0a0a0b); transform: translateY(-100%); transition: transform .5s cubic-bezier(.65, 0, .35, 1); }
.gcsl-fill::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: color-mix(in srgb, var(--gcsl-ink, #ece9e2) 35%, #fff); }
.gcsl-fill > span { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; transform: translateY(100%); transition: transform .5s cubic-bezier(.65, 0, .35, 1); }
.gcsl-root:hover .gcsl-fill, .gcsl-root:focus-visible .gcsl-fill, .gcsl-root:hover .gcsl-fill > span, .gcsl-root:focus-visible .gcsl-fill > span { transform: none; }
.gcsl-root:active { transform: translateY(1px); }
.gcsl-root:focus-visible { outline: 2px solid var(--gcsl-ink, #ece9e2); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .gcsl-fill, .gcsl-fill > span { transition: none; } }
`;

export function BotonScanline({
  label = "Iniciar revisión",
  href,
  onClick,
  tone = "bone",
  className,
  style,
}: BotonScanlineProps) {
  const props = {
    className: ["gcsl-root", className].filter(Boolean).join(" "),
    style: { ...style, ["--gcsl-ink" as string]: `var(--gcsl-tone, ${TONES[tone] ?? TONES.bone})` },
  };
  const content = (
    <>
      <span>{label}</span>
      <span className="gcsl-fill" aria-hidden="true"><span>{label}</span></span>
    </>
  );

  return (
    <>
      <style>{CSS}</style>
      {href
        ? <a {...props} href={href}>{content}</a>
        : <button {...props} type="button" onClick={onClick}>{content}</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-scanline

Ejemplo de uso de Botón de barrido

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

ejemplo.tsx5 líneas
import { BotonScanline } from "./boton-scanline";

export default function Ejemplo() {
  return <BotonScanline tone="bone" label="Ejemplo" />;
}

Propiedades de BotonScanline

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

PropiedadTipoDescripción
label?stringOpcional.
href?stringOpcional.
onClick?MouseEventHandler<HTMLButtonElement>Opcional.
tone?"bone" | "signal" | "azure" | "citron"Opcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Variantes disponibles

  • tone: bone, signal, azure, citron

Personaliza Botón de barrido 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.

VariableValor por defecto
--gcsl-ink#ece9e2
--gcsl-on#0a0a0b
--gcsl-tone${TONES[tone] ?? TONES.bone}

Accesibilidad y rendimiento

El archivo tiene 67 líneas (3 KB) y no necesita @keyframes. Esto es lo que hace Botón de barrido 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).
  • Expone su estado a tecnologías de apoyo con atributos aria-*.
  • Calcula tonos con color-mix(), así que un solo color base alcanza.
Licencia
MIT
Dependencias
React >=18

Acerca del producto

Al pasar el puntero o enfocarlo, una línea baja y detrás de ella el botón se rellena; el texto invertido aparece exactamente en la línea. Sin brillos.

Incluye

Un solo archivo React + TypeScript con estilos dentro. Sin Tailwind ni dependencias externas.

  • Texto invertido recortado con el relleno
  • Funciona con foco de teclado
  • Cuatro tonos
  • Botón o enlace según las props
  • Respeta prefers-reduced-motion