Precios claymorfismo
Tres planes de plastilina con el recomendado más grande, interruptor blando mensual/anual y lista de beneficios.
Elige tu tamaño de maceta
MensualAnual−20%
Semilla
Para probar con calma.
$0/mes
- 1 proyecto
- Hasta 3 personas
- Soporte por correo
Brote
Para equipos que ya florecen.
$15/mes
- Proyectos ilimitados
- Hasta 15 personas
- Reportes semanales
- Soporte prioritario
Bosque
Para organizaciones completas.
$39/mes
- Todo lo de Brote
- Personas ilimitadas
- Roles y permisos
- Acuerdo de servicio
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useState, type CSSProperties } from "react";
/** Precios claymorfismo: tres planes de plastilina con el recomendado más grande, interruptor mensual/anual y lista de beneficios. Datos de muestra. React + TypeScript, sin dependencias. */
export type PreciosArcillaPlan = { name: string; monthly: number; yearly: number; blurb: string; features: string[]; color: string; featured?: boolean };
export type PreciosArcillaProps = {
title?: string;
plans?: PreciosArcillaPlan[];
currency?: string;
cta?: string;
onChoose?: (plan: string, billing: "mensual" | "anual") => void;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcpar-root { box-sizing: border-box; container-type: inline-size; width: 100%; padding: clamp(24px, 5cqi, 64px) clamp(16px, 4cqi, 56px); background: #fbeee4; color: #40284d; font: 700 15px/1.45 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, system-ui, sans-serif; }
.gcpar-root *, .gcpar-root *::before, .gcpar-root *::after { box-sizing: border-box; }
.gcpar-root button { font: inherit; -webkit-tap-highlight-color: transparent; }
.gcpar-root :focus-visible { outline: 3px solid #40284d; outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .gcpar-root *, .gcpar-root *::before, .gcpar-root *::after { transition-duration: .01ms !important; } }
.gcpar-head { display: grid; justify-items: center; gap: 18px; margin-bottom: 36px; text-align: center; }
.gcpar-title { margin: 0; font: 900 clamp(32px, 5cqi, 56px)/1.02 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, sans-serif; letter-spacing: -.035em; text-wrap: balance; }
.gcpar-switch { display: inline-flex; align-items: center; gap: 12px; }
.gcpar-sw { position: relative; width: 70px; height: 38px; padding: 0; border: 0; border-radius: 999px; cursor: pointer; background: #ffd0b8; box-shadow: inset 4px 5px 8px rgba(190, 90, 60, .35), inset -4px -5px 8px rgba(255, 255, 255, .8); }
.gcpar-ball { position: absolute; top: 4px; left: 4px; width: 30px; height: 30px; border-radius: 50%; background: #fff7ee; box-shadow: inset -3px -4px 6px rgba(190, 140, 90, .4), inset 3px 4px 6px #fff, 2px 4px 8px rgba(60, 30, 80, .3); transition: transform .4s cubic-bezier(.34, 1.56, .64, 1); }
.gcpar-sw[aria-checked="true"] .gcpar-ball { transform: translateX(32px); }
.gcpar-save { padding: 4px 10px; border-radius: 999px; background: #8fe3c0; color: #12483a; font-size: 13px; }
.gcpar-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; align-items: center; }
.gcpar-card { position: relative; display: grid; gap: 14px; padding: 28px 26px 30px; border-radius: 40px; background: var(--c); box-shadow: inset -9px -12px 22px color-mix(in srgb, var(--c) 66%, #4a1030), inset 9px 12px 22px color-mix(in srgb, var(--c) 35%, #fff), 16px 22px 34px -12px color-mix(in srgb, var(--c) 75%, #3a0a20); transition: transform .4s cubic-bezier(.34, 1.56, .64, 1); }
.gcpar-card:hover { transform: translateY(-8px) rotate(-.5deg); }
.gcpar-card[data-f="true"] { padding-block: 40px; }
.gcpar-badge { position: absolute; top: -14px; left: 50%; transform: translateX(-50%); padding: 6px 16px; border-radius: 999px; background: #40284d; color: #fff; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; box-shadow: 0 6px 10px -4px rgba(64, 40, 77, .6); }
.gcpar-name { margin: 0; font-size: 22px; font-weight: 900; }
.gcpar-blurb { margin: 0; font-weight: 600; color: color-mix(in srgb, #40284d 80%, var(--c)); }
.gcpar-price { display: flex; align-items: baseline; gap: 6px; }
.gcpar-price b { font: 900 54px/1 ui-rounded, "SF Pro Rounded", "Nunito", ui-sans-serif, sans-serif; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
.gcpar-price span { font-weight: 700; opacity: .75; }
.gcpar-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.gcpar-list li { display: flex; gap: 10px; align-items: flex-start; font-weight: 700; }
.gcpar-list svg { margin-top: 2px; padding: 3px; border-radius: 50%; background: #fff7ee; box-shadow: inset -1px -2px 3px rgba(0, 0, 0, .15), 1px 2px 3px rgba(60, 30, 80, .2); }
.gcpar-btn { min-height: 52px; margin-top: 6px; padding: 0 24px; border: 0; border-radius: 999px; background: #40284d; color: #fff; font-weight: 800; cursor: pointer; box-shadow: inset -4px -5px 8px rgba(0, 0, 0, .4), inset 4px 5px 8px rgba(255, 255, 255, .22), 4px 8px 12px -4px rgba(64, 40, 77, .55); transition: transform .3s cubic-bezier(.34, 1.56, .64, 1); }
.gcpar-btn:hover { transform: scale(1.04); }
.gcpar-btn:active { transform: scale(.95, .9); }
@container (max-width: 860px) { .gcpar-grid { grid-template-columns: minmax(0, 1fr); } .gcpar-card[data-f="true"] { padding-block: 28px; } }`;
const PLANS: PreciosArcillaPlan[] = [
{ name: "Semilla", monthly: 0, yearly: 0, blurb: "Para probar con calma.", features: ["1 proyecto", "Hasta 3 personas", "Soporte por correo"], color: "#ffd0b8" },
{ name: "Brote", monthly: 19, yearly: 15, blurb: "Para equipos que ya florecen.", features: ["Proyectos ilimitados", "Hasta 15 personas", "Reportes semanales", "Soporte prioritario"], color: "#c3b1ff", featured: true },
{ name: "Bosque", monthly: 49, yearly: 39, blurb: "Para organizaciones completas.", features: ["Todo lo de Brote", "Personas ilimitadas", "Roles y permisos", "Acuerdo de servicio"], color: "#8fe3c0" },
];
export function PreciosArcilla({ title = "Elige tu tamaño de maceta", plans = PLANS, currency = "$", cta = "Elegir plan", onChoose, className = "", style }: PreciosArcillaProps) {
const [yearly, setYearly] = useState(true);
return (
<section className={`gcpar-root ${className}`} style={style}>
<style>{CSS}</style>
<div className="gcpar-head">
<h2 className="gcpar-title">{title}</h2>
<div className="gcpar-switch">
<span>Mensual</span>
<button className="gcpar-sw" type="button" role="switch" aria-checked={yearly} aria-label="Facturación anual" onClick={() => setYearly((value) => !value)}>
<span className="gcpar-ball" />
</button>
<span>Anual</span>
<span className="gcpar-save">−20%</span>
</div>
</div>
<div className="gcpar-grid">
{plans.map((plan) => (
<article key={plan.name} className="gcpar-card" data-f={plan.featured ? "true" : undefined} style={{ "--c": plan.color } as CSSProperties}>
{plan.featured ? <span className="gcpar-badge">Más elegido</span> : null}
<h3 className="gcpar-name">{plan.name}</h3>
<p className="gcpar-blurb">{plan.blurb}</p>
<div className="gcpar-price" aria-live="polite">
<b>
{currency}
{yearly ? plan.yearly : plan.monthly}
</b>
<span>/mes</span>
</div>
<ul className="gcpar-list">
{plan.features.map((feature) => (
<li key={feature}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="m5 12.5 4.5 4.5L19 7.5" />
</svg>
{feature}
</li>
))}
</ul>
<button className="gcpar-btn" type="button" onClick={() => onChoose?.(plan.name, yearly ? "anual" : "mensual")}>
{cta}
</button>
</article>
))}
</div>
</section>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/precios-arcillaEjemplo de uso de Precios claymorfismo
Después de copiar precios-arcilla.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { PreciosArcilla } from "./precios-arcilla";
export default function Ejemplo() {
return <PreciosArcilla title="Ejemplo" />;
}Propiedades de PreciosArcilla
PreciosArcilla 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. |
plans? | PreciosArcillaPlan[] | Opcional. |
currency? | string | Opcional. |
cta? | string | Opcional. |
onChoose? | (plan: string, billing: "mensual" | "anual") => void | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 103 líneas (7 KB) y no necesita @keyframes. Esto es lo que hace Precios 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).
- Declara roles ARIA para lectores de pantalla: switch.
- 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".