Hero claymorfismo
Formas de plastilina que flotan y se aplastan al apuntarlas, titular redondeado y botones blandos con relieve interior.
Hecho a mano, también en pantalla
Interfaces blandas, redondas y amables para productos que quieren caer bien desde el primer clic.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import type { CSSProperties } from "react";
/** Hero claymorfismo: formas de plastilina que flotan y se aplastan al apuntarlas, con titular redondeado y botones blandos. React + TypeScript, sin dependencias. Personaliza con --gchar-*. */
export type HeroArcillaProps = {
title?: string;
description?: string;
primary?: string;
secondary?: string;
href?: string;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gchar-root { box-sizing: border-box; position: relative; container-type: inline-size; width: 100%; min-height: 620px; overflow: hidden; color: #40284d; background: var(--gchar-bg, #fff0e6); font: 700 16px/1.5 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, system-ui, sans-serif; }
.gchar-root *, .gchar-root *::before, .gchar-root *::after { box-sizing: border-box; }
.gchar-root a { -webkit-tap-highlight-color: transparent; }
.gchar-root :focus-visible { outline: 3px solid #40284d; outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .gchar-root *, .gchar-root *::before, .gchar-root *::after { animation: none !important; transition-duration: .01ms !important; } }
.gchar-copy { position: relative; z-index: 2; display: grid; gap: 22px; align-content: center; justify-items: start; min-height: 620px; padding: 72px clamp(24px, 7cqi, 90px); max-width: 640px; }
.gchar-title { margin: 0; font: 900 clamp(46px, 8.6cqi, 96px)/.98 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, system-ui, sans-serif; letter-spacing: -.035em; text-wrap: balance; }
.gchar-desc { margin: 0; max-width: 40ch; font-size: clamp(16px, 1.9cqi, 20px); color: #6a4a78; font-weight: 600; text-wrap: pretty; }
.gchar-actions { display: flex; flex-wrap: wrap; gap: 14px; }
.gchar-btn { display: inline-flex; align-items: center; min-height: 58px; padding: 0 32px; border-radius: 999px; color: #40284d; background: var(--c); text-decoration: none; font-weight: 800; font-size: 17px; box-shadow: inset -7px -9px 14px color-mix(in srgb, var(--c) 62%, #3a1100), inset 7px 9px 14px color-mix(in srgb, var(--c) 40%, #fff), 0 14px 22px -8px color-mix(in srgb, var(--c) 70%, #3a1100); transition: transform .35s cubic-bezier(.34, 1.56, .64, 1); }
.gchar-btn:hover { transform: translateY(-3px) scale(1.04); }
.gchar-btn:active { transform: scale(.95, .9); }
.gchar-shapes { position: absolute; inset: 0; }
.gchar-s { position: absolute; background: var(--c); box-shadow: inset -14px -18px 28px color-mix(in srgb, var(--c) 62%, #3a0a30), inset 12px 14px 24px color-mix(in srgb, var(--c) 35%, #fff), 24px 34px 44px -14px color-mix(in srgb, var(--c) 70%, #3a0a30); animation: gchar-float var(--t) ease-in-out var(--dl) infinite; transition: transform .5s cubic-bezier(.34, 1.56, .64, 1); }
.gchar-s:hover { transform: scale(1.12, .9) !important; }
.gchar-sphere { width: var(--z); height: var(--z); border-radius: 50%; }
.gchar-pillow { width: var(--z); height: var(--z); border-radius: 38%; }
.gchar-ring { width: var(--z); height: var(--z); border-radius: 50%; background: transparent; box-shadow: none; border: calc(var(--z) * .24) solid var(--c); filter: drop-shadow(14px 20px 14px color-mix(in srgb, var(--c) 60%, #3a0a30)); }
.gchar-ring::after { content: ""; position: absolute; inset: calc(var(--z) * -.24); border-radius: 50%; box-shadow: inset -10px -12px 18px color-mix(in srgb, var(--c) 60%, #3a0a30), inset 10px 12px 18px color-mix(in srgb, var(--c) 35%, #fff); -webkit-mask: radial-gradient(circle, transparent calc(50% - var(--z) * .24 + 1px), #000 calc(50% - var(--z) * .24 + 2px)); mask: radial-gradient(circle, transparent calc(50% - var(--z) * .24 + 1px), #000 calc(50% - var(--z) * .24 + 2px)); pointer-events: none; }
@container (max-width: 820px) { .gchar-shapes { opacity: .55; } .gchar-copy { max-width: none; } }
@keyframes gchar-float { 50% { transform: translateY(-22px) rotate(4deg) scale(1.02, .98); } }`;
type Shape = { kind: "sphere" | "pillow" | "ring"; c: string; z: number; x: string; y: string; t: number; dl: number };
const SHAPES: Shape[] = [
{ kind: "sphere", c: "#ff9b85", z: 190, x: "62%", y: "10%", t: 6, dl: 0 },
{ kind: "pillow", c: "#8fd3ff", z: 150, x: "80%", y: "46%", t: 7, dl: -2 },
{ kind: "ring", c: "#ffd45e", z: 140, x: "58%", y: "58%", t: 8, dl: -4 },
{ kind: "sphere", c: "#b79bff", z: 96, x: "86%", y: "12%", t: 5.5, dl: -1 },
{ kind: "pillow", c: "#8be3b5", z: 84, x: "70%", y: "84%", t: 6.5, dl: -3 },
];
export function HeroArcilla({ title = "Hecho a mano, también en pantalla", description = "Interfaces blandas, redondas y amables para productos que quieren caer bien desde el primer clic.", primary = "Probar gratis", secondary = "Ver galería", href = "#", className = "", style }: HeroArcillaProps) {
return (
<section className={`gchar-root ${className}`} style={style}>
<style>{CSS}</style>
<div className="gchar-shapes" aria-hidden="true">
{SHAPES.map((shape, index) => (
<span key={index} className={`gchar-s gchar-${shape.kind}`} style={{ "--c": shape.c, "--z": `${shape.z}px`, "--t": `${shape.t}s`, "--dl": `${shape.dl}s`, left: shape.x, top: shape.y } as CSSProperties} />
))}
</div>
<div className="gchar-copy">
<h1 className="gchar-title">{title}</h1>
<p className="gchar-desc">{description}</p>
<div className="gchar-actions">
<a className="gchar-btn" href={href} style={{ "--c": "#ffb08a" } as CSSProperties}>
{primary}
</a>
<a className="gchar-btn" href={href} style={{ "--c": "#c3b1ff" } as CSSProperties}>
{secondary}
</a>
</div>
</div>
</section>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/hero-arcillaEjemplo de uso de Hero claymorfismo
Después de copiar hero-arcilla.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { HeroArcilla } from "./hero-arcilla";
export default function Ejemplo() {
return <HeroArcilla title="Ejemplo" />;
}Propiedades de HeroArcilla
HeroArcilla acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
title? | string | Opcional. |
description? | string | Opcional. |
primary? | string | Opcional. |
secondary? | string | Opcional. |
href? | string | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Hero claymorfismo 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 |
|---|---|
--gchar-bg | #fff0e6 |
Accesibilidad y rendimiento
El archivo tiene 73 líneas (6 KB) y define 1 animación con @keyframes. Esto es lo que hace Hero claymorfismo 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.
- Es un componente de cliente de Next.js: incluye la directiva "use client".