Ir al contenido
Evaluar mi proyecto
Componentes · Cursor y efectosDisponible

Tarjeta con inclinación 3D y brillo

Inclinación según el puntero, brillo especular y capas en profundidad (anillo, esfera y texto) que se separan al inclinar.

Profundidad real

Inclina la tarjeta con el puntero.

Código completo

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

efecto-inclinacion.tsx61 líneas
Descargar
"use client";

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

/** Tarjeta con inclinación 3D, brillo especular que sigue al puntero y capas en profundidad (fondo, forma y texto se separan al inclinar). Vuelve a su sitio con un resorte. React + TypeScript, sin dependencias. */
export type EfectoInclinacionProps = {
  title?: string;
  text?: string;
  max?: number;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gceic-root { box-sizing: border-box; display: inline-block; perspective: 900px; font: 500 15px/1.4 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gceic-root *, .gceic-root *::before, .gceic-root *::after { box-sizing: border-box; }
@media (prefers-reduced-motion: reduce) { .gceic-card { transform: none !important; } }
.gceic-card { --rx: 0deg; --ry: 0deg; --mx: 50%; --my: 50%; position: relative; width: 300px; max-width: 100%; aspect-ratio: 3 / 4; padding: 22px; border-radius: 26px; background: linear-gradient(150deg, #5b7cff, #8a4dff 55%, #ff7ac6); color: #fff; transform-style: preserve-3d; transform: rotateX(var(--rx)) rotateY(var(--ry)); transition: transform .5s cubic-bezier(.16, 1, .3, 1); box-shadow: 0 40px 60px -26px rgba(60, 30, 160, .7); touch-action: pan-y; }
.gceic-card[data-live="true"] { transition-duration: .06s; }
.gceic-glare { position: absolute; inset: 0; border-radius: inherit; background: radial-gradient(240px 240px at var(--mx) var(--my), rgba(255, 255, 255, .5), transparent 60%); mix-blend-mode: soft-light; pointer-events: none; }
.gceic-shape { position: absolute; left: 50%; top: 20%; width: 150px; height: 150px; margin-left: -75px; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #fff6c9, #ffb23f 60%, #ff7a3d); transform: translateZ(60px); box-shadow: 0 24px 30px -12px rgba(120, 40, 0, .5); }
.gceic-ring { position: absolute; left: 50%; top: 20%; width: 230px; height: 230px; margin: -40px 0 0 -115px; border: 3px solid rgba(255, 255, 255, .55); border-radius: 50%; transform: translateZ(25px) rotateX(70deg); }
.gceic-txt { position: absolute; left: 22px; right: 22px; bottom: 22px; transform: translateZ(40px); }
.gceic-txt h3 { margin: 0 0 4px; font: 700 26px/1.05 ui-sans-serif, system-ui, sans-serif; letter-spacing: -.03em; }
.gceic-txt p { margin: 0; opacity: .88; font-size: 14px; }`;

export function EfectoInclinacion({ title = "Profundidad real", text = "Inclina la tarjeta con el puntero.", max = 16, className = "", style }: EfectoInclinacionProps) {
  const ref = useRef<HTMLDivElement>(null);
  function move(event: PointerEvent<HTMLDivElement>) {
    if (event.pointerType === "touch") return;
    const el = ref.current!;
    const r = el.getBoundingClientRect();
    const x = (event.clientX - r.left) / r.width, y = (event.clientY - r.top) / r.height;
    el.style.setProperty("--ry", `${(x - 0.5) * max * 2}deg`);
    el.style.setProperty("--rx", `${(0.5 - y) * max * 2}deg`);
    el.style.setProperty("--mx", `${x * 100}%`);
    el.style.setProperty("--my", `${y * 100}%`);
    el.dataset.live = "true";
  }
  function leave() {
    const el = ref.current!;
    el.style.setProperty("--rx", "0deg");
    el.style.setProperty("--ry", "0deg");
    delete el.dataset.live;
  }
  return (
    <div className={`gceic-root ${className}`} style={style}>
      <style>{CSS}</style>
      <div ref={ref} className="gceic-card" onPointerMove={move} onPointerLeave={leave}>
        <div className="gceic-ring" aria-hidden="true" />
        <div className="gceic-shape" aria-hidden="true" />
        <div className="gceic-txt">
          <h3>{title}</h3>
          <p>{text}</p>
        </div>
        <div className="gceic-glare" aria-hidden="true" />
      </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/efecto-inclinacion

Ejemplo de uso de Tarjeta con inclinación 3D y brillo

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

ejemplo.tsx5 líneas
import { EfectoInclinacion } from "./efecto-inclinacion";

export default function Ejemplo() {
  return <EfectoInclinacion title="Ejemplo" />;
}

Propiedades de EfectoInclinacion

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

PropiedadTipoDescripción
title?stringOpcional.
text?stringOpcional.
max?numberOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Accesibilidad y rendimiento

El archivo tiene 61 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Tarjeta con inclinación 3D y brillo por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • 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)