Footer aurora
Footer oscuro con una aurora verde y azul muy lenta detrás del contenido.
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
/**
* Footer sobre una aurora boreal: dos cortinas de luz verde y azul que se mecen muy despacio
* detrás del contenido, sin bordes duros. Marca y frase a la izquierda, dos columnas de
* enlaces, suscripción por correo con campo subrayado y fila legal. Con movimiento reducido
* la aurora se queda quieta. Los colores se sobrescriben con --gcfa-bg y --gcfa-accent.
*
* <FooterAurora
* brand="Gassier Consulting"
* links={[["Servicios", [["Sistemas a la medida", "/servicios/sistemas-criticos"]]]]}
* onSubscribe={async (email) => { await save(email); }}
* />
*/
"use client";
import { useId, useState, type CSSProperties, type FormEvent } from "react";
export type FooterAuroraProps = {
brand?: string;
tagline?: string;
links?: [string, [string, string][]][];
/** Si se omite, el formulario avisa que la vista previa no está conectada. */
onSubscribe?: (email: string) => void | Promise<void>;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcfa-root { position: relative; isolation: isolate; overflow: hidden; box-sizing: border-box; padding: clamp(56px, 8vw, 104px) clamp(20px, 5vw, 64px) 24px; background: var(--gcfa-bg, #050809); color: #c9ccd1; font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
.gcfa-aurora { position: absolute; inset: 0 0 auto 0; z-index: -1; height: 90%; pointer-events: none; -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 100%); mask-image: linear-gradient(180deg, #000 0%, transparent 100%); }
.gcfa-aurora i { position: absolute; top: -30%; width: 55%; height: 120%; filter: blur(70px); mix-blend-mode: screen; will-change: transform; }
.gcfa-aurora i:nth-child(1) { left: -8%; background: radial-gradient(50% 50% at 50% 50%, rgba(52, 232, 160, .42), transparent 70%); animation: gcfa-sway-a 19s ease-in-out infinite alternate; }
.gcfa-aurora i:nth-child(2) { left: 28%; background: radial-gradient(50% 50% at 50% 50%, rgba(46, 150, 230, .34), transparent 70%); animation: gcfa-sway-b 23s ease-in-out infinite alternate; }
.gcfa-aurora i:nth-child(3) { left: 56%; background: radial-gradient(50% 50% at 50% 50%, rgba(80, 240, 200, .26), transparent 70%); animation: gcfa-sway-a 27s ease-in-out infinite alternate-reverse; }
.gcfa-top { display: grid; grid-template-columns: 1.3fr 1.5fr 1.1fr; gap: clamp(32px, 4vw, 72px); padding-bottom: clamp(48px, 7vw, 88px); }
.gcfa-mark { display: block; color: #f4f6f7; font-size: clamp(26px, 3vw, 38px); font-weight: 600; letter-spacing: -.035em; line-height: 1.05; }
.gcfa-tag { max-width: 34ch; margin: 14px 0 0; color: #98a0a8; font-size: 15px; line-height: 1.55; }
.gcfa-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px; }
.gcfa-cols h4 { margin: 0 0 14px; color: #f4f6f7; font-size: 14px; font-weight: 600; }
.gcfa-cols ul { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.gcfa-cols a, .gcfa-legal a { color: #aab1b8; text-decoration: none; transition: color .2s; }
.gcfa-cols a:hover, .gcfa-legal a:hover { color: #fff; }
.gcfa-cols a:focus-visible, .gcfa-legal a:focus-visible { outline: 2px solid var(--gcfa-accent, #5ee6b0); outline-offset: 4px; border-radius: 2px; }
.gcfa-form label { display: block; margin-bottom: 10px; color: #f4f6f7; font-size: 14px; font-weight: 600; }
.gcfa-field { display: flex; align-items: center; gap: 10px; border-bottom: 1px solid #4a5258; transition: border-color .2s; }
.gcfa-field:focus-within { border-color: var(--gcfa-accent, #5ee6b0); }
.gcfa-field input { flex: 1; min-width: 0; padding: 10px 0; border: 0; background: transparent; color: #fff; font: inherit; outline: none; }
.gcfa-field input::placeholder { color: #8a929a; }
.gcfa-field button { padding: 8px 0 8px 8px; border: 0; background: transparent; color: var(--gcfa-accent, #5ee6b0); font: 600 14px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer; }
.gcfa-field button:hover { text-decoration: underline; text-underline-offset: 4px; }
.gcfa-field button:focus-visible { outline: 2px solid var(--gcfa-accent, #5ee6b0); outline-offset: 2px; border-radius: 2px; }
.gcfa-field button:disabled { cursor: wait; opacity: .6; }
.gcfa-feedback { min-height: 20px; margin: 10px 0 0; color: #aab1b8; font-size: 13px; }
.gcfa-legal { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px 24px; padding-top: 22px; border-top: 1px solid rgba(255, 255, 255, .1); color: #8a929a; font-size: 13px; }
.gcfa-legal span { display: inline-flex; gap: 18px; }
@keyframes gcfa-sway-a { to { transform: translate(14%, 10%) scale(1.12); } }
@keyframes gcfa-sway-b { to { transform: translate(-16%, 6%) scale(.92); } }
@media (max-width: 860px) { .gcfa-top { grid-template-columns: 1fr; gap: 40px; } }
@media (prefers-reduced-motion: reduce) { .gcfa-aurora i { animation: none; } }
`;
export function FooterAurora({
brand = "Gassier Consulting",
tagline = "Software a la medida que se adapta a tu operación, no al revés.",
links = [
["Servicios", [["Sistemas a la medida", "/servicios/sistemas-criticos"], ["Aplicaciones web", "/servicios/aplicaciones-web"], ["Datos e IA aplicada", "/servicios/datos-ia"]]],
["Empresa", [["Cómo trabajamos", "/proceso"], ["Preguntas frecuentes", "/faq"], ["Evaluar un proyecto", "/cotizar"]]],
],
onSubscribe,
className,
style,
}: FooterAuroraProps) {
const emailId = useId();
const [feedback, setFeedback] = useState("");
const [submitting, setSubmitting] = useState(false);
const subscribe = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const form = event.currentTarget;
const email = new FormData(form).get("email");
if (typeof email !== "string" || !email) return;
if (!onSubscribe) {
setFeedback("Esta vista previa no está conectada a una lista de correo.");
return;
}
setSubmitting(true);
setFeedback("");
try {
await onSubscribe(email);
setFeedback("Listo. Te escribiremos solo cuando haya algo útil.");
form.reset();
} catch {
setFeedback("No se pudo completar el registro. Inténtalo de nuevo.");
} finally {
setSubmitting(false);
}
};
return (
<>
<style>{CSS}</style>
<footer className={["gcfa-root", className].filter(Boolean).join(" ")} style={style}>
<div className="gcfa-aurora" aria-hidden="true"><i /><i /><i /></div>
<div className="gcfa-top">
<div>
<span className="gcfa-mark">{brand}</span>
<p className="gcfa-tag">{tagline}</p>
</div>
<nav className="gcfa-cols" aria-label="Pie de página">
{links.map(([title, items]) => (
<div key={title}>
<h4>{title}</h4>
<ul>
{items.map(([label, href]) => (
<li key={label}><a href={href}>{label}</a></li>
))}
</ul>
</div>
))}
</nav>
<form className="gcfa-form" onSubmit={subscribe}>
<label htmlFor={emailId}>Recibe novedades por correo</label>
<div className="gcfa-field">
<input id={emailId} type="email" name="email" placeholder="tu@correo.com" autoComplete="email" required />
<button type="submit" disabled={submitting}>{submitting ? "Enviando…" : "Suscribirme"}</button>
</div>
<p className="gcfa-feedback" role="status" aria-live="polite">{feedback}</p>
</form>
</div>
<div className="gcfa-legal">
<span>© 2026 {brand}. Todos los derechos reservados.</span>
<span>
<a href="/privacidad">Privacidad</a>
<a href="/terminos">Términos</a>
<a href="/cookies">Cookies</a>
</span>
</div>
</footer>
</>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/footer-auroraEjemplo de uso de Footer aurora
Después de copiar footer-aurora.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { FooterAurora } from "./footer-aurora";
export default function Ejemplo() {
return <FooterAurora brand="Ejemplo" />;
}Propiedades de FooterAurora
FooterAurora 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. |
tagline? | string | Opcional. |
links? | [string, [string, string][]][] | Opcional. |
onSubscribe? | (email: string) => void | Promise<void> | Si se omite, el formulario avisa que la vista previa no está conectada. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Footer aurora 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 |
|---|---|
--gcfa-bg | #050809 |
--gcfa-accent | #5ee6b0 |
Accesibilidad y rendimiento
El archivo tiene 142 líneas (8 KB) y define 2 animaciones con @keyframes. Esto es lo que hace Footer aurora 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).
- Declara roles ARIA para lectores de pantalla: status.
- Es un componente de cliente de Next.js: incluye la directiva "use client".
Acerca del producto
Marca y frase, dos columnas de enlaces y una suscripción por correo con campo subrayado. La aurora son tres manchas difuminadas que se mecen; con movimiento reducido se queda quieta.
Incluye
Un solo archivo React + TypeScript con estilos dentro. Sin Tailwind ni dependencias externas.
- Aurora de tres capas con máscara
- Suscripción con estados de envío y error
- Navegación con aria-label
- Fila legal con derechos
- Respeta prefers-reduced-motion