Footer con marca gigante que sube
Barra de enlaces discreta y la marca en letras enormes que suben una a una al llegar al final de la página.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useEffect, useRef, useState, type CSSProperties } from "react";
/** Footer editorial con la marca en letras gigantes que suben una a una al llegar al final de la página, y una barra de enlaces discreta. React + TypeScript, sin dependencias. */
export type FooterCortinaProps = {
word?: string;
links?: { label: string; href?: string }[];
email?: string;
legal?: string;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcfco-root { box-sizing: border-box; container-type: inline-size; position: relative; width: 100%; overflow: hidden; padding: clamp(28px, 5cqi, 64px) clamp(16px, 4cqi, 56px) 0; background: #111; color: #f1ede4; font: 500 15px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gcfco-root *, .gcfco-root *::before, .gcfco-root *::after { box-sizing: border-box; }
.gcfco-root :focus-visible { outline: 2px solid #ff6b3d; outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .gcfco-root *, .gcfco-root *::before, .gcfco-root *::after { transition: none !important; transform: none !important; } }
.gcfco-bar { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 18px 36px; padding-bottom: 28px; border-bottom: 1px solid #333; }
.gcfco-mail { color: inherit; font: 500 clamp(20px, 3cqi, 30px)/1.1 "Newsreader", Georgia, ui-serif, serif; text-decoration: none; border-bottom: 1px solid currentColor; padding-bottom: 2px; }
.gcfco-mail:hover { color: #ff6b3d; }
.gcfco-links { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 0; padding: 0; list-style: none; align-items: center; }
.gcfco-links a { color: #bdb7a8; text-decoration: none; }
.gcfco-links a:hover { color: #fff; }
.gcfco-word { display: flex; justify-content: center; margin: 0; padding: .05em 0 0; overflow: hidden; font: 900 clamp(64px, 24cqi, 360px)/.82 "Arial Black", "Impact", ui-sans-serif, system-ui, sans-serif; letter-spacing: -.05em; text-transform: uppercase; color: #f1ede4; user-select: none; }
.gcfco-l { display: inline-block; transform: translateY(105%); transition: transform 1s cubic-bezier(.16, 1, .3, 1); transition-delay: calc(var(--i) * 70ms); }
.gcfco-root[data-in="true"] .gcfco-l { transform: none; }
.gcfco-l:hover { color: #ff6b3d; }
.gcfco-legal { position: absolute; right: clamp(16px, 4cqi, 56px); bottom: 10px; font-size: 12px; color: #6f6a5f; mix-blend-mode: normal; }
@media (scripting: none) { .gcfco-l { transform: none; } }`;
export function FooterCortina({ word = "Hasta pronto", links = [{ label: "Proyectos" }, { label: "Servicios" }, { label: "Contacto" }, { label: "Aviso de privacidad" }], email = "hola@ejemplo.com", legal = "© 2026", className = "", style }: FooterCortinaProps) {
const ref = useRef<HTMLElement>(null);
const [seen, setSeen] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(([entry]) => entry.isIntersecting && (setSeen(true), io.disconnect()), { threshold: 0.25 });
io.observe(el);
return () => io.disconnect();
}, []);
return (
<footer ref={ref} className={`gcfco-root ${className}`} style={style} data-in={seen}>
<style>{CSS}</style>
<div className="gcfco-bar">
<a className="gcfco-mail" href={`mailto:${email}`}>
{email}
</a>
<ul className="gcfco-links">
{links.map((link) => (
<li key={link.label}>
<a href={link.href ?? "#"} onClick={(event) => !link.href && event.preventDefault()}>
{link.label}
</a>
</li>
))}
</ul>
</div>
<p className="gcfco-word" aria-label={word}>
{Array.from(word).map((char, index) => (
<span key={index} className="gcfco-l" aria-hidden="true" style={{ "--i": index } as CSSProperties}>
{char === " " ? " " : char}
</span>
))}
</p>
<span className="gcfco-legal">{legal}</span>
</footer>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/footer-cortinaEjemplo de uso de Footer con marca gigante que sube
Después de copiar footer-cortina.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { FooterCortina } from "./footer-cortina";
export default function Ejemplo() {
return <FooterCortina word="Ejemplo" />;
}Propiedades de FooterCortina
FooterCortina acepta 6 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
word? | string | Opcional. |
links? | { label: string; href?: string }[] | Opcional. |
email? | string | Opcional. |
legal? | string | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 71 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Footer con marca gigante que sube 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-*.
- Se pausa cuando sale de la pantalla para no gastar batería ni CPU.
- Es un componente de cliente de Next.js: incluye la directiva "use client".