Ir al contenido
Evaluar mi proyecto
Componentes · FootersDisponible

Footer índice editorial

Footer con índice de enlaces en filas grandes, correo configurable y retorno al inicio; composición editorial adaptable.

La conversación
sigue abierta.

hola@example.com
Forma© 2026 · Sitio de muestraVolver arriba

Código completo

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

footer-indice.tsx89 líneas
Descargar
"use client";

import { type CSSProperties } from "react";

/** Footer índice editorial. React + TypeScript, sin dependencias. Personaliza con variables --gcyi-*. */
export type FooterIndiceProps = {
  brand?: string;
  email?: string;
  links?: { label: string; href: string }[];
  year?: number;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcyi-root { box-sizing: border-box; min-width: 0; max-width: 100%; color: var(--gcyi-fg, #202421); font: 400 15px/1.5 ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
.gcyi-root *, .gcyi-root *::before, .gcyi-root *::after { box-sizing: border-box; }
.gcyi-root button, .gcyi-root input, .gcyi-root textarea, .gcyi-root select { font: inherit; }
.gcyi-root button, .gcyi-root a { -webkit-tap-highlight-color: transparent; }
.gcyi-root :focus-visible { outline: 2px solid var(--gcyi-accent, #25724b); outline-offset: 4px; }
.gcyi-root ::selection { background: var(--gcyi-accent, #25724b); color: #fff; }
.gcyi-root button:disabled { opacity: .5; cursor: not-allowed; }
.gcyi-root svg { flex-shrink: 0; }
@media (prefers-reduced-motion: reduce) { .gcyi-root *, .gcyi-root *::before, .gcyi-root *::after { animation: none !important; transition-duration: .01ms !important; scroll-behavior: auto !important; } }
.gcyi-root { width:100%; padding:48px clamp(24px,5vw,64px) 24px; background:var(--gcyi-bg,#e8e3d7); color:var(--gcyi-fg,#343b30); container-type:inline-size; }
.gcyi-top { display:flex; justify-content:space-between; align-items:flex-end; gap:28px; margin-bottom:40px; }
.gcyi-root h2 { font-size:clamp(36px,4.5vw,58px); line-height:1.05; font-weight:600; letter-spacing:-.03em; margin:0; }
.gcyi-email { color:#4b5940; font-size:14px; text-underline-offset:5px; overflow-wrap:anywhere; }
.gcyi-root nav { display:flex; flex-direction:column; }
.gcyi-root nav a { display:flex; align-items:center; justify-content:space-between; min-height:64px; padding:16px 0; border-top:1px solid #b8c0aa; color:#343b30; text-decoration:none; font-size:24px; letter-spacing:-.02em; }
.gcyi-root nav a svg { transition:transform .3s; }
.gcyi-root nav a:hover svg { transform:translateX(5px); }
.gcyi-bottom { display:flex; justify-content:space-between; align-items:center; gap:20px; margin-top:40px; font-size:11px; flex-wrap:wrap; color:#56604b; }
.gcyi-bottom strong { font-size:24px; font-weight:600; letter-spacing:-.03em; color:#343b30; }
.gcyi-bottom a { color:inherit; padding:10px 0; text-underline-offset:4px; }
@container(max-width:560px) { .gcyi-top { flex-direction:column; align-items:flex-start; } }`;

export function FooterIndice({
  brand = "Forma",
  email = "hola@example.com",
  links = [
    { label: "Proyectos", href: "#proyectos" },
    { label: "Estudio", href: "#estudio" },
    { label: "Contacto", href: "#contacto" },
  ],
  year = 2026,
  className = "",
  style,
}: FooterIndiceProps) {
  return (
    <footer className={`gcyi-root ${className}`} style={style}>
      <style>{CSS}</style>
      <div className="gcyi-top">
        <h2>
          La conversación
          <br />
          sigue abierta.
        </h2>
        <a className="gcyi-email" href={`mailto:${email}`}>
          {email}
        </a>
      </div>
      <nav aria-label="Índice del pie de página">
        {links.map((link) => (
          <a key={link.href} href={link.href}>
            {link.label}
            <svg
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              strokeWidth="1.4"
              aria-hidden="true"
            >
              <path d="M5 12h14m-6-6 6 6-6 6" />
            </svg>
          </a>
        ))}
      </nav>
      <div className="gcyi-bottom">
        <strong>{brand}</strong>
        <span>© {year} · Sitio de muestra</span>
        <a href="#top">Volver arriba</a>
      </div>
    </footer>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

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

Ejemplo de uso de Footer índice editorial

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

ejemplo.tsx5 líneas
import { FooterIndice } from "./footer-indice";

export default function Ejemplo() {
  return <FooterIndice brand="Ejemplo" />;
}

Propiedades de FooterIndice

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

PropiedadTipoDescripción
brand?stringOpcional.
email?stringOpcional.
links?{ label: string; href: string }[]Opcional.
year?numberOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Footer índice editorial con variables CSS

Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.

VariableValor por defecto
--gcyi-fg#202421
--gcyi-accent#25724b
--gcyi-bg#e8e3d7

Accesibilidad y rendimiento

El archivo tiene 89 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Footer índice editorial 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-*.
  • 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)