Footer índice editorial
Footer con índice de enlaces en filas grandes, correo configurable y retorno al inicio; composición editorial adaptable.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"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.
npx shadcn@latest add https://www.gassierconsulting.com/r/footer-indiceEjemplo de uso de Footer índice editorial
Después de copiar footer-indice.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
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.
| Propiedad | Tipo | Descripción |
|---|---|---|
brand? | string | Opcional. |
email? | string | Opcional. |
links? | { label: string; href: string }[] | Opcional. |
year? | number | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
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.
| Variable | Valor 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".