Ir al contenido
Evaluar mi proyecto
Componentes · PreciosDisponible

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
Más elegido

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.

precios-arcilla.tsx103 líneas
Descargar
"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.

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

Ejemplo de uso de Precios claymorfismo

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

ejemplo.tsx5 líneas
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.

PropiedadTipoDescripción
title?stringOpcional.
plans?PreciosArcillaPlan[]Opcional.
currency?stringOpcional.
cta?stringOpcional.
onChoose?(plan: string, billing: "mensual" | "anual") => voidOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

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".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)