Ir al contenido
Evaluar mi proyecto
Componentes · FondosDisponible

Fondo de contornos reactivos

Contornos elípticos SVG que desplazan su perspectiva con el puntero; fondo ligero, sin loops ni canvas.

Una forma de ver.

El paisaje responde a tu perspectiva.

Código completo

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

fondo-contornos.tsx96 líneas
Descargar
"use client";

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

/** Fondo de contornos reactivos. React + TypeScript, sin dependencias. Personaliza con variables --gczc-*. */
export type FondoContornosProps = {
  children?: ReactNode;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gczc-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gczc-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gczc-root *, .gczc-root *::before, .gczc-root *::after { box-sizing: border-box; }
.gczc-root button, .gczc-root input, .gczc-root textarea, .gczc-root select { font: inherit; }
.gczc-root button, .gczc-root a { -webkit-tap-highlight-color: transparent; }
.gczc-root :focus-visible { outline: 2px solid var(--gczc-accent, #25724b); outline-offset: 4px; }
.gczc-root ::selection { background: var(--gczc-accent, #25724b); color: #fff; }
.gczc-root button:disabled { opacity: .5; cursor: not-allowed; }
.gczc-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gczc-root *, .gczc-root *::before, .gczc-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gczc-root { position:relative; isolation:isolate; width:100%; min-height:420px; overflow:hidden; display:flex; align-items:center; padding:44px clamp(24px,6vw,72px); background:var(--gczc-bg,#e8e5da); color:var(--gczc-fg,#333c31); }
.gczc-contours { position:absolute; inset:-5%; width:110%; height:110%; z-index:-1; stroke:var(--gczc-accent,#809174); stroke-width:.7; transform:translate(var(--gczc-x,0px),var(--gczc-y,0px)); transition:transform .5s cubic-bezier(.16,1,.3,1); opacity:.65; }
.gczc-content { max-width:22ch; }
.gczc-content h3 { font-size:clamp(32px,5vw,56px); line-height:1.05; font-weight:600; letter-spacing:-.035em; margin:0 0 20px; }
.gczc-content p { margin:0; font-size:14px; color:#515c47; max-width:25ch; }
@media(prefers-reduced-motion:reduce) { .gczc-contours { transform:none; } }`;

export function FondoContornos({
  children,
  className = "",
  style,
}: FondoContornosProps) {
  const ref = useRef<HTMLDivElement>(null);
  function move(event: PointerEvent<HTMLDivElement>) {
    if (
      event.pointerType !== "mouse" ||
      window.matchMedia("(prefers-reduced-motion: reduce)").matches
    )
      return;
    const rect = event.currentTarget.getBoundingClientRect();
    ref.current?.style.setProperty(
      "--gczc-x",
      `${((event.clientX - rect.left) / rect.width - 0.5) * 24}px`,
    );
    ref.current?.style.setProperty(
      "--gczc-y",
      `${((event.clientY - rect.top) / rect.height - 0.5) * 24}px`,
    );
  }
  return (
    <div
      ref={ref}
      className={`gczc-root ${className}`}
      style={style}
      onPointerMove={move}
      onPointerLeave={() => {
        ref.current?.style.removeProperty("--gczc-x");
        ref.current?.style.removeProperty("--gczc-y");
      }}
    >
      <style>{CSS}</style>
      <svg
        className="gczc-contours"
        viewBox="0 0 800 500"
        fill="none"
        aria-hidden="true"
      >
        {Array.from({ length: 22 }, (_, i) => (
          <ellipse
            key={i}
            cx="520"
            cy="230"
            rx={70 + i * 13}
            ry={35 + i * 9}
            transform={`rotate(${i * 3} 520 230)`}
          />
        ))}
      </svg>
      <div className="gczc-content">
        {children ?? (
          <>
            <h3>Una forma de ver.</h3>
            <p>El paisaje responde a tu perspectiva.</p>
          </>
        )}
      </div>
    </div>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

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

Ejemplo de uso de Fondo de contornos reactivos

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

ejemplo.tsx5 líneas
import { FondoContornos } from "./fondo-contornos";

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

Propiedades de FondoContornos

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

PropiedadTipoDescripción
children?ReactNodeOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Fondo de contornos reactivos 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
--gczc-fg#202421
--gczc-accent#25724b
--gczc-bg#e8e5da
--gczc-x0px
--gczc-y0px

Accesibilidad y rendimiento

El archivo tiene 96 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Fondo de contornos reactivos 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-*.
  • Reacciona al puntero con eventos pointer (ratón, lápiz y táctil).
  • 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)