Ir al contenido
Evaluar mi proyecto
Componentes · HerosDisponible

Hero de producto SaaS

Texto, formulario de correo y una ventana de aplicación dibujada con CSS.

NuevoNovedad: tableros en tiempo real

Todo tu equipo, en un solo tablero.

Planifica, asigna y entrega sin saltar entre cinco herramientas. Empieza en minutos.

Sin tarjeta de crédito. Cancela cuando quieras.

Código completo

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

product-hero.tsx121 líneas
Descargar
/**
 * Hero de producto SaaS: texto y formulario de correo a la izquierda y, a la derecha, una ventana de aplicación dibujada solo con CSS
 * (tablero, tarjetas y avisos flotantes). No usa imágenes. Los avisos flotan y se animan en bucle.
 *
 *   <ProductHero
 *     badge="Novedad: tableros en tiempo real"
 *     title="Todo tu equipo, en un solo tablero."
 *     description="Planifica, asigna y entrega sin saltar entre cinco herramientas."
 *     formAction="/api/lista-de-espera"
 *   />
 */

import type { CSSProperties } from "react";

export type ProductHeroProps = {
  badge?: string;
  title?: string;
  description?: string;
  /** URL a la que se envía el formulario (método POST). */
  formAction?: string;
  buttonLabel?: string;
  /** Color de acento (botón y detalles). */
  accent?: string;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gchp-root { --gchp-accent: #c8ff4d; position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); align-items: center; gap: clamp(28px, 5vw, 80px); box-sizing: border-box; min-height: 640px; overflow: hidden; padding: clamp(28px, 5vw, 72px); background: radial-gradient(70% 90% at 90% 10%, #14503f 0%, transparent 60%), #0a2a22; color: #effae8; font-family: inherit; }
.gchp-badge { display: inline-flex; align-items: center; gap: 10px; padding: 7px 14px 7px 8px; border: 1px solid rgba(239, 250, 232, .2); border-radius: 999px; color: #cfe6c6; font-size: 14px; }
.gchp-badge b { padding: 3px 9px; border-radius: 999px; background: var(--gchp-accent); color: #10200a; font-size: 12px; font-weight: 700; }
.gchp-title { margin: 22px 0 0; max-width: 12ch; font-size: clamp(40px, 5.6vw, 76px); font-weight: 700; line-height: 1.02; letter-spacing: -.04em; text-wrap: balance; }
.gchp-desc { margin: 20px 0 0; max-width: 44ch; color: #b8d1b0; font-size: clamp(16px, 1.4vw, 19px); line-height: 1.6; }
.gchp-form { display: flex; flex-wrap: wrap; gap: 10px; margin: 30px 0 0; max-width: 460px; padding: 6px; border: 1px solid rgba(239, 250, 232, .22); border-radius: 16px; background: rgba(255, 255, 255, .05); }
.gchp-form:focus-within { border-color: var(--gchp-accent); box-shadow: 0 0 0 4px rgba(200, 255, 77, .14); }
.gchp-form input { flex: 1 1 180px; min-width: 0; min-height: 46px; padding: 0 14px; border: 0; outline: none; background: transparent; color: #effae8; font-family: inherit; font-size: 16px; }
.gchp-form input::placeholder { color: #86a37d; }
.gchp-form button { min-height: 46px; padding: 0 22px; border: 0; border-radius: 11px; background: var(--gchp-accent); color: #10200a; font-family: inherit; font-size: 15px; font-weight: 700; cursor: pointer; transition: transform .25s cubic-bezier(.16, 1, .3, 1), filter .25s; }
.gchp-form button:hover { transform: translateY(-1px); filter: brightness(1.08); }
.gchp-form button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.gchp-note { margin: 14px 0 0; color: #86a37d; font-size: 13px; }
.gchp-stage { position: relative; padding: 24px 0 36px; }
.gchp-window { position: relative; display: grid; grid-template-columns: 56px 1fr; overflow: hidden; border-radius: 18px; background: #f4f7f1; color: #17261f; box-shadow: 0 50px 90px -30px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 255, 255, .1); transform: perspective(1400px) rotateY(-9deg) rotateX(3deg); animation: gchp-float 7s ease-in-out infinite; }
.gchp-side { display: grid; align-content: start; justify-items: center; gap: 12px; padding: 16px 0; background: #0f3a2e; }
.gchp-side i { width: 22px; height: 22px; border-radius: 7px; background: rgba(255, 255, 255, .18); }
.gchp-side i:first-child { background: var(--gchp-accent); }
.gchp-body { padding: 18px 20px 22px; }
.gchp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; font-size: 15px; font-weight: 700; }
.gchp-avatars { display: flex; } .gchp-avatars i { width: 26px; height: 26px; margin-left: -7px; border: 2px solid #f4f7f1; border-radius: 50%; background: #7bd389; } .gchp-avatars i:nth-child(2) { background: #f2b84b; } .gchp-avatars i:nth-child(3) { background: #7aa7ff; }
.gchp-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.gchp-col { padding: 10px; border-radius: 12px; background: #e7ede2; }
.gchp-col h4 { margin: 0 0 10px; color: #4b6556; font-size: 12px; font-weight: 700; }
.gchp-card { margin-bottom: 8px; padding: 10px; border-radius: 9px; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .08); }
.gchp-card u { display: block; height: 7px; margin-bottom: 6px; border-radius: 4px; background: #d3dccf; text-decoration: none; } .gchp-card u:nth-child(2) { width: 62%; }
.gchp-card em { display: inline-block; padding: 2px 8px; border-radius: 999px; background: #d9f7a3; color: #34520e; font-size: 10px; font-style: normal; font-weight: 700; }
.gchp-card em.b { background: #d3e3ff; color: #1d3e8a; } .gchp-card em.o { background: #ffe0b8; color: #7a4308; }
.gchp-toast { position: absolute; top: 4px; right: -10px; display: flex; align-items: center; gap: 9px; padding: 10px 14px; border-radius: 12px; background: #fff; color: #17261f; font-size: 13px; font-weight: 600; box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .5); animation: gchp-pop 6s cubic-bezier(.16, 1, .3, 1) infinite; }
.gchp-toast svg { width: 22px; height: 22px; padding: 4px; box-sizing: border-box; border-radius: 50%; background: #7bd389; color: #0f3a2e; }
.gchp-chart { position: absolute; left: -14px; bottom: 0; display: flex; align-items: end; gap: 5px; height: 78px; padding: 12px 14px; border-radius: 14px; background: #fff; box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .5); animation: gchp-float 6s ease-in-out -2s infinite; }
.gchp-chart i { width: 9px; border-radius: 3px 3px 0 0; background: #17261f; } .gchp-chart i:last-child { background: #7bd389; }
@keyframes gchp-float { 50% { translate: 0 -9px; } }
@keyframes gchp-pop { 0%, 8% { opacity: 0; transform: translateY(14px) scale(.9); } 14%, 70% { opacity: 1; transform: none; } 80%, 100% { opacity: 0; transform: translateY(-10px); } }
@media (max-width: 900px) { .gchp-root { grid-template-columns: 1fr; } .gchp-window { transform: none; } .gchp-toast { right: 0; } .gchp-chart { left: 0; } }
@media (prefers-reduced-motion: reduce) { .gchp-window, .gchp-chart, .gchp-toast { animation: none; } .gchp-toast { opacity: 1; } }
`;

const cards: [string, string][][] = [
  [["", ""], ["b", "Diseño"]],
  [["", ""], ["o", "Revisión"]],
  [["", ""], ["", "Listo"]],
];

export function ProductHero({
  badge = "Novedad: tableros en tiempo real",
  title = "Todo tu equipo, en un solo tablero.",
  description = "Planifica, asigna y entrega sin saltar entre cinco herramientas. Empieza en minutos.",
  formAction = "#",
  buttonLabel = "Probar gratis",
  accent,
  className,
  style,
}: ProductHeroProps) {
  return (
    <>
      <style>{CSS}</style>
      <section className={["gchp-root", className].filter(Boolean).join(" ")} style={{ ["--gchp-accent" as string]: accent, ...style } as CSSProperties}>
        <div>
          <span className="gchp-badge"><b>Nuevo</b>{badge}</span>
          <h1 className="gchp-title">{title}</h1>
          <p className="gchp-desc">{description}</p>
          <form className="gchp-form" action={formAction} method="post">
            <input type="email" name="email" required placeholder="tu@empresa.com" aria-label="Correo electrónico" autoComplete="email" />
            <button type="submit">{buttonLabel}</button>
          </form>
          <p className="gchp-note">Sin tarjeta de crédito. Cancela cuando quieras.</p>
        </div>
        <div className="gchp-stage" aria-hidden="true">
          <div className="gchp-window">
            <div className="gchp-side"><i /><i /><i /><i /></div>
            <div className="gchp-body">
              <div className="gchp-head"><span>Sprint 14</span><span className="gchp-avatars"><i /><i /><i /></span></div>
              <div className="gchp-cols">
                {["Por hacer", "En curso", "Listo"].map((name, column) => (
                  <div className="gchp-col" key={name}>
                    <h4>{name}</h4>
                    {cards[column].map(([tone, label], index) => (
                      <div className="gchp-card" key={index}><u /><u />{label ? <em className={tone}>{label}</em> : null}</div>
                    ))}
                  </div>
                ))}
              </div>
            </div>
          </div>
          <div className="gchp-toast"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.4" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg>Tarea completada</div>
          <div className="gchp-chart"><i style={{ height: "35%" }} /><i style={{ height: "55%" }} /><i style={{ height: "45%" }} /><i style={{ height: "75%" }} /><i style={{ height: "100%" }} /></div>
        </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/hero-producto

Ejemplo de uso de Hero de producto SaaS

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

ejemplo.tsx5 líneas
import { ProductHero } from "./product-hero";

export default function Ejemplo() {
  return <ProductHero badge="Ejemplo" />;
}

Propiedades de ProductHero

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

PropiedadTipoDescripción
badge?stringOpcional.
title?stringOpcional.
description?stringOpcional.
formAction?stringURL a la que se envía el formulario (método POST).
buttonLabel?stringOpcional.
accent?stringColor de acento (botón y detalles).
className?stringOpcional.
style?CSSPropertiesOpcional.

Accesibilidad y rendimiento

El archivo tiene 121 líneas (9 KB) y define 2 animaciones con @keyframes. Esto es lo que hace Hero de producto SaaS 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-*.
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React · Next.js · Sin Tailwind

Acerca del producto

Un hero para presentar un producto: insignia de novedad, titular, formulario de correo y, al lado, una ventana de aplicación dibujada solo con CSS (barra lateral, tablero con tarjetas) con un aviso y una gráfica flotando. No usa imágenes.

Incluye

Una sección completa en un solo archivo React + TypeScript, con los estilos dentro. No usa Tailwind ni bibliotecas externas.

  • Ventana de aplicación 100% CSS
  • Formulario nativo con action configurable
  • Avisos flotantes animados
  • Color de acento configurable
  • Sin JavaScript

Cómo usarlo

Pasa badge, title, description y formAction (URL que recibe el correo por POST).