Back to 0fee
0fee

Un sitio web de marketing inspirado en Stripe con SolidJS

Cómo construimos el sitio web de marketing de 0fee.dev inspirado en Stripe con ejemplos de código interactivos, calculadora de precios y mapa de países.

Thales & Claude | March 30, 2026 10 min 0fee
EN/ FR/ ES
marketingsolidjsdesignglassmorphismintersection-observer

El sitio web de Stripe es el estándar de oro para el marketing enfocado en desarrolladores. Tipografía limpia, ejemplos de código interactivos, animaciones suaves y densidad de información que respeta la inteligencia del lector. Cuando construimos el sitio web de marketing de 0fee.dev, estudiamos el enfoque de Stripe y lo adaptamos para un orquestador de pagos orientado a África.

Este artículo cubre las principales secciones del sitio web de marketing -- el hero con estadísticas en vivo, ejemplos de código interactivos en cinco lenguajes, la grilla de funcionalidades, el mapa interactivo de países, la calculadora de precios y las páginas de soporte. Todo está construido en SolidJS con TailwindCSS, usando Intersection Observer para animaciones activadas por scroll y glassmorphism para profundidad.

La estructura de la página de inicio

La página de inicio de marketing (Home.tsx) ensambla ocho secciones en orden:

tsxexport default function Home() {
  return (
    <div>
      <Hero />
      <Features />
      <Providers />
      <CodeExamples />
      <Countries />
      <Pricing />
      <Testimonials />
      <CTA />
    </div>
  );
}

Cada sección es un componente autocontenido con sus propios datos, animaciones y diseño responsivo.

Sección hero con estadísticas

El hero combina un título, subtítulo y estadísticas en tiempo real:

tsxfunction Hero() {
  const stats = [
    { value: "53+", label: "Payment Providers" },
    { value: "200+", label: "Countries Covered" },
    { value: "40+", label: "Currencies" },
    { value: "99.9%", label: "Uptime" },
  ];

  return (
    <section class="hero-gradient min-h-screen flex items-center">
      <div class="container mx-auto px-6 text-center">
        <div class="inline-block px-4 py-1 bg-emerald-500/10 rounded-full
                    text-emerald-400 text-sm mb-6">
          Payment Orchestration for Developers
        </div>

        <h1 class="text-5xl md:text-7xl font-bold text-white mb-6">
          One API.
          <span class="gradient-text"> Every Payment.</span>
          <br />Everywhere.
        </h1>

        <p class="text-xl text-gray-400 max-w-2xl mx-auto mb-10">
          Accept cards, mobile money, and wallets across 200+ countries
          with a single integration. Africa-first, globally ready.
        </p>

        <div class="flex gap-4 justify-center mb-16">
          <a href="/register" class="btn-primary">Start Building</a>
          <a href="/docs" class="btn-secondary">Read the Docs</a>
        </div>

        <div class="grid grid-cols-2 md:grid-cols-4 gap-8">
          <For each={stats}>
            {(stat) => (
              <div class="text-center">
                <div class="text-3xl font-bold text-white">{stat.value}</div>
                <div class="text-gray-400 text-sm">{stat.label}</div>
              </div>
            )}
          </For>
        </div>
      </div>
    </section>
  );
}

El hero usa un fondo de gradiente oscuro (hero-gradient) con orbes decorativos de gradiente que flotan con una animación CSS. La fila de estadísticas proporciona prueba social de inmediato.

Ejemplos de código interactivos

La sección de ejemplos de código es el componente más inspirado en Stripe. Muestra código de integración en cinco lenguajes de programación con resaltado de sintaxis, números de línea y un botón de copiar:

tsxfunction CodeExamples() {
  const [activeTab, setActiveTab] = createSignal("typescript");

  const examples = {
    typescript: {
      label: "TypeScript",
      install: "npm install zerofee",
      code: `import { ZeroFee } from 'zerofee';

const zf = new ZeroFee({ apiKey: 'zf_live_...' });

const payment = await zf.payments.create({
  amount: 5000,
  currency: 'XOF',
  country: 'CI',
  method: 'PAYIN_ORANGE_CI',
  customer: { phone: '+2250700000000' },
  returnUrl: 'https://yourapp.com/thanks'
});

// Redirect customer to checkout
window.location.href = payment.checkoutUrl;`,
    },
    python: {
      label: "Python",
      install: "pip install zerofee",
      code: `from zerofee import ZeroFee

zf = ZeroFee(api_key="zf_live_...")

payment = zf.payments.create(
    amount=5000,
    currency="XOF",
    country="CI",
    method="PAYIN_ORANGE_CI",
    customer={"phone": "+2250700000000"},
    return_url="https://yourapp.com/thanks"
)

# Redirect customer to checkout
print(payment.checkout_url)`,
    },
    curl: {
      label: "cURL",
      install: "",
      code: `curl -X POST https://api.0fee.dev/v1/payments \\
  -H "Authorization: Bearer zf_live_..." \\
  -H "Content-Type: application/json" \\
  -d '{
    "amount": 5000,
    "currency": "XOF",
    "country": "CI",
    "payment_method": "PAYIN_ORANGE_CI",
    "customer": {"phone": "+2250700000000"},
    "return_url": "https://yourapp.com/thanks"
  }'`,
    },
    go: {
      label: "Go",
      install: "go get github.com/zerosuite/zerofee-go",
      code: `package main

import "github.com/zerosuite/zerofee-go"

func main() {
    zf := zerofee.New("zf_live_...")

    payment, _ := zf.Payments.Create(&zerofee.PaymentParams{
        Amount:   5000,
        Currency: "XOF",
        Country:  "CI",
        Method:   "PAYIN_ORANGE_CI",
        Customer: &zerofee.Customer{
            Phone: "+2250700000000",
        },
        ReturnURL: "https://yourapp.com/thanks",
    })

    fmt.Println(payment.CheckoutURL)
}`,
    },
    php: {
      label: "PHP",
      install: "composer require zerosuite/zerofee-php",
      code: `<?php
use ZeroFee\\ZeroFee;

$zf = new ZeroFee('zf_live_...');

$payment = $zf->payments->create([
    'amount' => 5000,
    'currency' => 'XOF',
    'country' => 'CI',
    'method' => 'PAYIN_ORANGE_CI',
    'customer' => ['phone' => '+2250700000000'],
    'return_url' => 'https://yourapp.com/thanks',
]);

header('Location: ' . $payment->checkout_url);`,
    },
  };

  return (
    <section class="py-24 bg-gray-950">
      <div class="container mx-auto px-6">
        <h2 class="text-3xl font-bold text-white text-center mb-4">
          Five Lines of Code
        </h2>
        <p class="text-gray-400 text-center mb-12 max-w-xl mx-auto">
          Accept payments worldwide with a single API call.
          Choose your language.
        </p>

        {/* Pestañas de lenguaje */}
        <div class="flex gap-2 justify-center mb-8">
          <For each={Object.entries(examples)}>
            {([key, lang]) => (
              <button
                class={`px-4 py-2 rounded-lg text-sm ${
                  activeTab() === key
                    ? "bg-emerald-500 text-white"
                    : "text-gray-400 hover:text-white"
                }`}
                onClick={() => setActiveTab(key)}
              >
                {lang.label}
              </button>
            )}
          </For>
        </div>

        {/* Bloque de código con resaltado de sintaxis */}
        <div class="max-w-3xl mx-auto">
          <SyntaxHighlighter
            code={examples[activeTab()].code}
            language={activeTab()}
          />
        </div>
      </div>
    </section>
  );
}

El cambio de pestañas es instantáneo -- los cinco bloques de código están pre-renderizados y se alternan con CSS. Sin cargas, sin llamadas API.

Grilla de funcionalidades

La sección de funcionalidades presenta ocho capacidades en una grilla responsiva:

tsxconst features = [
  {
    title: "Smart Routing",
    description: "Automatically route payments to the best provider based on country, method, and availability.",
    icon: RouteIcon,
  },
  {
    title: "Mobile Money First",
    description: "Orange Money, MTN, Wave, M-Pesa, and 40+ operators across Africa.",
    icon: PhoneIcon,
  },
  {
    title: "One Integration",
    description: "Single API, single SDK, single webhook format. Cover 200+ countries.",
    icon: CodeIcon,
  },
  {
    title: "Real-Time Dashboard",
    description: "Monitor transactions, manage providers, and track revenue in one place.",
    icon: ChartIcon,
  },
  // ... 4 funcionalidades más
];

Cada tarjeta de funcionalidad usa Intersection Observer para animaciones de aparición activadas por scroll:

tsxfunction FeatureCard(props) {
  let ref;

  onMount(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          ref.classList.add("animate-fade-in");
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );
    observer.observe(ref);
  });

  return (
    <div ref={ref} class="opacity-0 p-6 rounded-xl bg-gray-900 border border-gray-800">
      <props.icon class="w-8 h-8 text-emerald-400 mb-4" />
      <h3 class="text-lg font-semibold text-white mb-2">{props.title}</h3>
      <p class="text-gray-400 text-sm">{props.description}</p>
    </div>
  );
}

Mapa interactivo de países

El componente de países muestra más de 30 países con filtrado por región:

tsxfunction Countries() {
  const [activeRegion, setActiveRegion] = createSignal("all");

  const regions = [
    { id: "all", label: "All" },
    { id: "west", label: "West Africa" },
    { id: "east", label: "East Africa" },
    { id: "central", label: "Central" },
    { id: "south", label: "Southern" },
    { id: "global", label: "Global" },
  ];

  const countries = [
    { code: "CI", name: "Ivory Coast", region: "west",
      methods: ["Orange", "MTN", "Wave", "Moov"] },
    { code: "KE", name: "Kenya", region: "east",
      methods: ["M-Pesa", "Airtel"] },
    { code: "NG", name: "Nigeria", region: "west",
      methods: ["MTN", "Airtel", "Cards"] },
    // ... más de 27 países
  ];

  const filtered = () =>
    activeRegion() === "all"
      ? countries
      : countries.filter(c => c.region === activeRegion());

  return (
    <section class="py-24">
      <div class="container mx-auto px-6">
        <h2 class="text-3xl font-bold text-center mb-12">
          Coverage Across 200+ Countries
        </h2>

        {/* Pestañas de región */}
        <div class="flex gap-2 justify-center mb-8">
          <For each={regions}>
            {(region) => (
              <button
                class={`px-4 py-2 rounded-full text-sm ${
                  activeRegion() === region.id
                    ? "bg-emerald-500 text-white"
                    : "bg-gray-800 text-gray-400"
                }`}
                onClick={() => setActiveRegion(region.id)}
              >
                {region.label}
              </button>
            )}
          </For>
        </div>

        {/* Grilla de países */}
        <div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
          <For each={filtered()}>
            {(country) => (
              <div class="p-4 rounded-xl bg-gray-900 border border-gray-800
                          hover:border-emerald-500 transition-colors group">
                <div class="text-2xl mb-2">{country.flag}</div>
                <div class="text-white font-medium">{country.name}</div>
                <div class="text-xs text-gray-500 mt-1">
                  {country.methods.join(", ")}
                </div>
              </div>
            )}
          </For>
        </div>
      </div>
    </section>
  );
}

Al pasar el cursor sobre una tarjeta de país se revelan los métodos de pago disponibles -- Orange Money, MTN, Wave, etc.

Precios con calculadora

La sección de precios presenta una calculadora interactiva de comisiones:

tsxfunction PricingCalculator() {
  const [volume, setVolume] = createSignal(1000);
  const [avgAmount, setAvgAmount] = createSignal(50);

  const monthlyRevenue = () => volume() * avgAmount();
  const zerofeeTotal = () => monthlyRevenue() * 0.0099; // 0.99%

  return (
    <div class="bg-gray-900 rounded-xl p-8">
      <h3 class="text-xl font-bold text-white mb-6">Fee Calculator</h3>

      <div class="space-y-6">
        <div>
          <label class="text-gray-400 text-sm">
            Monthly Transactions: {volume().toLocaleString()}
          </label>
          <input
            type="range"
            min="100" max="100000" step="100"
            value={volume()}
            onInput={(e) => setVolume(parseInt(e.target.value))}
            class="w-full"
          />
        </div>

        <div>
          <label class="text-gray-400 text-sm">
            Average Amount: ${avgAmount()}
          </label>
          <input
            type="range"
            min="1" max="500" step="1"
            value={avgAmount()}
            onInput={(e) => setAvgAmount(parseInt(e.target.value))}
            class="w-full"
          />
        </div>

        <div class="border-t border-gray-800 pt-4">
          <div class="flex justify-between text-gray-400">
            <span>Monthly Volume</span>
            <span>${monthlyRevenue().toLocaleString()}</span>
          </div>
          <div class="flex justify-between text-white text-lg font-bold mt-2">
            <span>0fee Cost (0.99%)</span>
            <span>${zerofeeTotal().toLocaleString()}</span>
          </div>
        </div>
      </div>
    </div>
  );
}

La calculadora usa señales de SolidJS para reactividad instantánea -- mover el deslizador actualiza la estimación de comisión sin re-renderizar el DOM circundante.

Efectos glassmorphism

En todo el sitio de marketing, usamos glassmorphism para profundidad visual:

css.glass {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.glass-hover:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.15);
}

Estos efectos se usan en tarjetas, modales y elementos decorativos. Crean una sensación de capas sin fondos sólidos.

Páginas adicionales

Además de la página de inicio, el sitio de marketing incluye:

PáginaRutaContenido
Products/productsDescripción general de productos (Payments, Checkout, Routing)
Pricing/pricingPrecios detallados con acordeón FAQ
About/aboutEquipo, misión, ecosistema ZeroSuite
Contact/contactFormulario de contacto con información de oficina
Docs/docsGuía de inicio con ejemplos de SDK
Status/statusIndicadores de estado de salud de proveedores
How It Works/how-it-worksExplicación paso a paso del flujo de pago
Coverage/coverageCobertura detallada de países y métodos
Providers/providersLos más de 53 proveedores de pago

Animaciones con Intersection Observer

Cada sección usa Intersection Observer para animaciones de entrada activadas por scroll:

typescriptfunction useScrollAnimation() {
  return (el: HTMLElement) => {
    el.classList.add("opacity-0", "translate-y-4");

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          el.classList.remove("opacity-0", "translate-y-4");
          el.classList.add("opacity-100", "translate-y-0", "transition-all", "duration-700");
          observer.disconnect();
        }
      },
      { threshold: 0.1 }
    );

    observer.observe(el);
  };
}

Los elementos comienzan invisibles y desplazados 16px hacia abajo. Cuando entran en la vista al hacer scroll, aparecen con un efecto de fundido y se deslizan hacia arriba. El observador se desconecta después de activarse para evitar procesamiento innecesario.

Lo que aprendimos

Construir el sitio web de marketing nos enseñó tres cosas:

  1. Los elementos interactivos convierten mejor que el texto estático. Los ejemplos de código con cambio de pestañas, la calculadora de precios con deslizadores y el mapa de países con filtros de región invitan a la interacción. Cada interacción refuerza las capacidades del producto.
  1. Los temas oscuros funcionan para productos enfocados en desarrolladores. Los fondos de gradiente oscuro con acentos esmeralda crean una sensación premium mientras reducen la fatiga visual para desarrolladores que pasan horas leyendo documentación.
  1. El rendimiento importa para las primeras impresiones. El sitio de marketing carga en menos de 2 segundos. Cada sección es visible sin esperar cargas de datos (todo el contenido es estático). Las animaciones están basadas en CSS, no en JavaScript, así que funcionan a 60fps incluso en dispositivos móviles.

El sitio web de marketing fue construido en la sesión 003 con un rediseño completo en la sesión 014. Se inspira directamente en el enfoque de Stripe -- denso en información, enfocado en desarrolladores y visualmente pulido -- mientras adapta el contenido para un orquestador de pagos panafricano.


Este artículo es parte de la serie "Cómo construimos 0fee.dev". 0fee.dev es un orquestador de pagos que cubre más de 53 proveedores en más de 200 países, construido por Juste A. GNIMAVO y Claude desde Abiyán sin ingenieros humanos. Sigue la serie para conocer la historia completa de la construcción.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles