Retour au catalogue

CTA Dark

CTA sur fond sombre avec contraste fort. Accent lumineux sur fond dark pour impact maximal.

ctasimple Dark Responsive a11y
darkboldagencysaascentered
Theme

Cómo construir una sección CTA oscura con glow de acento en React

Una sección CTA oscura en React centra un bloque de texto sobre un fondo profundo, usa un div radial difuminado al 6% de opacidad para emitir un glow de acento suave y anima todo el bloque al entrar en el viewport con una transición whileInView de Framer Motion. Una palabra resaltada en el título se colorea mediante una custom property de CSS para mantener la coherencia con el tema del design system.

  • Stack: React + Framer Motion + Lucide React + Tailwind v4, ~110 líneas, cero dependencias adicionales.
  • Animación: un único bloque whileInView (opacity 0→1, y 24→0, duración 0,6s, ease custom [0.16, 1, 0.3, 1]).
  • Theming: totalmente basado en tokens, los colores de fondo, acento y borde provienen de custom properties de CSS, no de valores codificados en duro.
  • Accesible: etiquetas <h2> y <a> semánticas, aria-hidden en el div glow decorativo.
  • Responsive: escala tipográfica fluida mediante los breakpoints de Tailwind (text-3xl / md:text-4xl / lg:text-5xl), funciona en todos los tamaños de pantalla.

CtaDark es una sección call-to-action centrada pensada para landing pages de tema oscuro. Combina un fondo profundo con un glow de acento radial sutil, lo bastante visible para atraer la mirada sin distraer. Una única animación de entrada de Framer Motion da al bloque un aire intencional en lugar de estático. Es la sección que colocas al final de la página cuando necesitas una acción única y de alto contraste.

Anatomía

La sección tiene tres capas apiladas dentro de un contenedor relative. En el fondo se sitúa el color de fondo (--color-background-dark). La capa intermedia es un div centrado en absoluto de 600x400px, difuminado a 120px, coloreado con --color-accent al 6% de opacidad. Es aria-hidden y pointer-events:none. Encima, un motion.div centrado en max-w-2xl contiene el título, un párrafo y una fila de botones. El botón principal es una píldora rellena con fondo --color-accent; un botón secundario opcional es una píldora en outline con --color-border-dark.

Cómo funciona

La animación usa un único motion.div de Framer Motion con whileInView, de modo que el bloque se anima solo la primera vez que entra en el viewport (viewport: { once: true }). El estado inicial es opacity:0 e y:24; el estado animado es opacity:1 e y:0. La transición dura 0,6s con un cubic-bezier custom [0.16, 1, 0.3, 1], un ease-out fuerte que desacelera rápido al final, para un efecto pop-up contundente pero fluido. La palabra de acento del título se calcula con un simple split sobre titleAccent, y luego se envuelve en un span coloreado con var(--color-accent). Sin canvas, sin SVG, sin hooks adicionales.

Cómo crearlo en React

  1. Prepara el shell de la sección oscura

    Crea una sección relative/overflow-hidden con background-color:var(--color-background-dark) y un padding vertical generoso. Es el lienzo sobre el que se apoya todo lo demás. Usar un token CSS en lugar de un hex codificado en duro hace que los siete temas de la biblioteca funcionen sin retoques.

    <section
      className="relative overflow-hidden"
      style={{
        backgroundColor: "var(--color-background-dark)",
        paddingTop: "var(--section-padding-y-lg)",
        paddingBottom: "var(--section-padding-y-lg)",
      }}
    >
  2. Añade el glow de acento radial

    Coloca un div centrado en absoluto de 600x400px, difuminado a 120px con blur-[120px], y fija su backgroundColor en var(--color-accent) con opacidad 0,06. Mantenlo aria-hidden y pointer-events-none para que sea invisible para las tecnologías de asistencia y no pueda interceptar los clics.

    <div
      aria-hidden
      className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
                 w-[600px] h-[400px] rounded-full blur-[120px] pointer-events-none"
      style={{ backgroundColor: "var(--color-accent)", opacity: 0.06 }}
    />
  3. Anima el contenido con whileInView

    Envuelve el título, la descripción y los botones en un único motion.div con initial={{ opacity:0, y:24 }} y whileInView={{ opacity:1, y:0 }}, viewport={{ once:true }}. Pasa una transición de 0,6s con un ease custom para un reveal al hacer scroll contundente que se dispara una vez y permanece.

    <motion.div
      initial={{ opacity: 0, y: 24 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
      className="text-center max-w-2xl mx-auto"
    >
  4. Resalta la palabra de acento en el título

    Divide la cadena del título por la palabra titleAccent, y luego renderiza las partes con un span entre ellas coloreado por var(--color-accent). Así el acento queda ligado al token del design system, y cambiar de tema actualiza el resaltado automáticamente sin tocar el componente.

    const titleParts = titleAccent ? title.split(titleAccent) : [title];
    
    // In JSX:
    {titleAccent ? (
      <>
        {titleParts[0]}
        <span style={{ color: "var(--color-accent)" }}>{titleAccent}</span>
        {titleParts[1]}
      </>
    ) : title}

Cuándo usarlo

Usa CtaDark al final de una página SaaS o de agencia con tema oscuro, después de los testimonios o una sección de características, cuando quieras una acción de alto contraste antes del footer. La guía de composición en la meta lo solicita explícitamente en posición 'late'. Evítalo a media página o justo después de otro CTA; el fondo oscuro necesita espacio para leerse como algo intencional. En páginas con tema claro, prefiere una variante clara con la misma estructura en lugar de luchar contra el contraste.

Usado por

  • Stripe, Usa secciones CTA sobre fondo oscuro al final de sus páginas de producto con una única acción principal de alto contraste.
  • Vercel, Bloques CTA oscuros con glow en degradado radial aparecen al final de las páginas de características y de casos de estudio.
  • Linear, Paneles CTA oscuros centrados con resaltados del título en color de acento cierran la landing page principal.

FAQ

¿Cómo cambio el color del glow de acento?

El color del glow proviene de var(--color-accent), así que sigue el preset de tema activo en la página. Cambia el atributo data-theme del elemento padre a uno de los siete presets de la biblioteca y el glow se actualiza automáticamente.

¿Puedo añadir un segundo botón?

Pasa un valor a la prop ctaSecondaryLabel y el botón secundario en outline aparece automáticamente junto al principal. Ambos comparten la prop ctaUrl; si necesitas URLs separadas, haz un fork del componente y añade una prop ctaSecondaryUrl.

¿La animación al hacer scroll se reproduce en cada visita?

No. viewport: { once: true } en la config de whileInView significa que la animación se dispara una vez por carga de página cuando la sección entra en el viewport, y luego permanece en su estado animado. Quita ese flag si quieres que se reinicie cada vez que la sección sale y vuelve a entrar.

¿Qué pasa si titleAccent no se encuentra en la cadena del título?

El split devuelve un array con el título completo como único elemento, y la lógica de titleParts recurre a renderizar el título plano sin ningún span resaltado. No se lanza ningún error; el título simplemente se muestra sin coloración de acento.

"use client";

import { motion } from "framer-motion";
import { ArrowRight } from "lucide-react";

interface CtaDarkProps {
  title?: string;
  titleAccent?: string;
  description?: string;
  ctaLabel?: string;
  ctaUrl?: string;
  ctaSecondaryLabel?: string;
}

export default function CtaDark({
  title = "Lancez votre projet",
  titleAccent = "projet",
  description = "Rejoignez les entreprises qui nous font confiance.",
  ctaLabel = "Commencer maintenant",
  ctaUrl = "#contact",
  ctaSecondaryLabel,
}: CtaDarkProps) {

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reseñas

Sección CTA oscura en React con efecto glow, código +