# Footer CTA

> Bloco de rodapé integrado com cartão de conversão de alto impacto posicionado antes dos links.

A shadcn/ui block from the [@lema-ds](https://ds.lema.ufpb.br) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/lema-ds/ds-footer-cta).

- Page, with a live preview: https://shadcn.exchange/r/lema-ds/ds-footer-cta
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/lema-ds/ds-footer-cta-light-9471bd2566237311.png
- Registry item: https://ds.lema.ufpb.br/r/ds-footer-cta.json
- Categories: footer
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ds.lema.ufpb.br/r/ds-footer-cta.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `class-variance-authority`, `lucide-react`
- Registry items: `@lema-ds/button`, `@lema-ds/ds-social-links`, `@lema-ds/ds-footer-bottom-bar`, `@lema-ds/ds-footer-menu`, `@lema-ds/ui-i18n`, `@lema-ds/ds-locale-provider`, `@lema-ds/tokens`

## Files

### `components/ds/ds-footer-cta.tsx`

```tsx
"use client"

import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { ArrowRightIcon } from "lucide-react"

import { cn } from "@/lib/utils"
import { type UILocale } from "@/lib/ui-i18n"
import { useUILocale } from "@/components/ui/ds-locale-provider"
import { Button } from "@/components/ui/button"
import { SocialLinks, type SocialLinkItem } from "./ds-social-links"
import { FooterBottomBar, type LegalLinkItem } from "./ds-footer-bottom-bar"
import { FooterMenu, type FooterGroupData } from "./ds-footer-menu"

// ── Types ──

export interface FooterCtaAction {
  label: string
  href?: string
  onClick?: () => void
}

export interface FooterCtaProps
  extends
    React.HTMLAttributes<HTMLElement>,
    VariantProps<typeof footerCtaVariants> {
  ctaTitle?: string
  ctaDescription?: string
  primaryAction?: FooterCtaAction
  secondaryAction?: FooterCtaAction
  columns?: FooterGroupData[]
  brand?: React.ReactNode
  socialLinks?: SocialLinkItem[]
  brandName?: string
  legalLinks?: LegalLinkItem[]
  locale?: UILocale
}

// ── Variants ──

export const footerCtaVariants = cva(
  "relative w-full border-t border-border bg-background pt-16 pb-12",
  {
    variants: {
      ctaTone: {
        card: "border-border bg-card",
        primary: "border-transparent bg-primary text-primary-foreground",
        glow: "border-primary/20 bg-card/80 shadow-lg shadow-primary/5",
      },
    },
    defaultVariants: {
      ctaTone: "glow",
    },
  }
)

// ── Component ──

export const FooterCta = React.forwardRef<HTMLElement, FooterCtaProps>(
  (
    {
      ctaTitle = "Ready to transform your digital experience?",
      ctaDescription = "Join researchers, developers, and students building modern web applications with the LEMA Design System.",
      primaryAction = {
        label: "Get started now",
        href: "#",
      },
      secondaryAction = {
        label: "Talk to the team",
        href: "#",
      },
      columns = [
        {
          title: "Product",
          options: [
            { name: "Components", url: "#" },
            { name: "Tokens", url: "#" },
            { name: "Templates", url: "#" },
          ],
        },
        {
          title: "Developers",
          options: [
            { name: "Documentation", url: "#" },
            { name: "GitHub", url: "#" },
            { name: "Registry", url: "#" },
          ],
        },
        {
          title: "Institutional",
          options: [
            { name: "About LEMA", url: "#" },
            { name: "UFPB", url: "#" },
            { name: "Contact", url: "#" },
          ],
        },
      ],
      brand,
      socialLinks = [
        { platform: "github", href: "https://github.com" },
        { platform: "x", href: "https://x.com" },
        { platform: "linkedin", href: "https://linkedin.com" },
      ],
      brandName = "LEMA - UFPB",
      legalLinks,
      ctaTone = "glow",
      locale: localeProp,
      className,
      ...props
    },
    ref
  ) => {
    const locale = useUILocale(localeProp)
    return (
      <footer
        ref={ref}
        className={cn(footerCtaVariants({ ctaTone }), className)}
        {...props}
      >
        <div className="mx-auto flex max-w-7xl flex-col gap-16 px-4 sm:px-6 lg:px-8">
          {/* CTA Card / Band */}
          <div
            className={cn(
              "relative overflow-hidden rounded-3xl border p-8 transition-all md:p-12",
              ctaTone === "primary"
                ? "border-transparent bg-primary text-primary-foreground"
                : ctaTone === "glow"
                  ? "border-primary/20 bg-card shadow-xl shadow-primary/5"
                  : "border-border bg-card"
            )}
          >
            {/* Ambient subtle glow background */}
            <div
              className="pointer-events-none absolute -top-16 -right-16 size-72 rounded-full bg-primary/10 blur-3xl"
              aria-hidden="true"
            />
            <div
              className="pointer-events-none absolute -bottom-16 -left-16 size-72 rounded-full bg-highlight-violet/10 blur-3xl"
              aria-hidden="true"
            />

            <div className="relative z-10 flex flex-col items-start justify-between gap-8 md:flex-row md:items-center">
              <div className="flex max-w-2xl flex-col gap-3">
                <h2 className="text-2xl font-bold tracking-tight md:text-3xl lg:text-4xl">
                  {ctaTitle}
                </h2>
                {ctaDescription && (
                  <p
                    className={cn(
                      "text-sm leading-relaxed md:text-base",
                      ctaTone === "primary"
                        ? "text-primary-foreground/80"
                        : "text-muted-foreground"
                    )}
                  >
                    {ctaDescription}
                  </p>
                )}
              </div>

              <div className="flex shrink-0 flex-wrap items-center gap-3">
                {primaryAction && (
                  <Button
                    asChild={Boolean(primaryAction.href)}
                    size="lg"
                    onClick={primaryAction.onClick}
                    variant={ctaTone === "primary" ? "secondary" : "default"}
                    className="gap-2 font-semibold"
                  >
                    {primaryAction.href ? (
                      <a href={primaryAction.href}>
                        <span>{primaryAction.label}</span>
                        <ArrowRightIcon className="size-4" aria-hidden="true" />
                      </a>
                    ) : (
                      <>
                        <span>{primaryAction.label}</span>
                        <ArrowRightIcon className="size-4" aria-hidden="true" />
                      </>
                    )}
                  </Button>
                )}

                {secondaryAction && (
                  <Button
                    asChild={Boolean(secondaryAction.href)}
                    size="lg"
                    variant={ctaTone === "primary" ? "ghost" : "outline"}
                    onClick={secondaryAction.onClick}
                    className="font-medium"
                  >
                    {secondaryAction.href ? (
                      <a href={secondaryAction.href}>{secondaryAction.label}</a>
                    ) : (
                      <span>{secondaryAction.label}</span>
                    )}
                  </Button>
                )}
              </div>
            </div>
          </div>

          {/* Navigation Links Grid */}
          <div className="grid grid-cols-1 gap-10 lg:grid-cols-12">
            <div className="flex flex-col gap-4 lg:col-span-4">
              {brand || (
                <div className="flex items-center gap-2 font-bold text-foreground">
                  <div className="flex size-7 items-center justify-center rounded-lg bg-primary text-xs font-bold text-primary-foreground">
                    L
                  </div>
                  <span>{brandName}</span>
                </div>
              )}

              {socialLinks.length > 0 && (
                <SocialLinks links={socialLinks} variant="ghost" size="sm" />
              )}
            </div>

            <div className="lg:col-span-8">
              <FooterMenu data={columns} locale={locale} size="sm" />
            </div>
          </div>

          {/* Bottom Bar */}
          <FooterBottomBar
            brandName={brandName}
            legalLinks={legalLinks}
            locale={locale}
          />
        </div>
      </footer>
    )
  }
)
FooterCta.displayName = "FooterCta"
```
