Footer neobrutalista
Bloques de color con borde grueso y sombra dura, enlaces gigantes que se desplazan y una cinta de texto corrido.
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 neobrutalista: bloques de color con borde grueso y sombra dura, enlaces gigantes y una cinta con texto corrido. React + TypeScript, sin dependencias. */
export type FooterNeobrutalProps = {
brand?: string;
cta?: string;
links?: { label: string; href?: string; color?: string }[];
legal?: string;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcfnb-root { box-sizing: border-box; container-type: inline-size; width: 100%; padding: clamp(16px, 4cqi, 44px); background: #ffe14d; color: #111; font: 800 16px/1.3 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gcfnb-root *, .gcfnb-root *::before, .gcfnb-root *::after { box-sizing: border-box; }
.gcfnb-root :focus-visible { outline: 4px solid #2f5bff; outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .gcfnb-root *, .gcfnb-root *::before, .gcfnb-root *::after { animation: none !important; transition-duration: .01ms !important; } }
.gcfnb-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 18px; }
.gcfnb-box { padding: 22px; border: 4px solid #111; background: #fff; box-shadow: 8px 8px 0 #111; }
.gcfnb-brand { margin: 0 0 10px; font: 900 clamp(34px, 6cqi, 72px)/.9 ui-sans-serif, system-ui, sans-serif; letter-spacing: -.05em; text-transform: uppercase; }
.gcfnb-cta { display: inline-flex; align-items: center; min-height: 50px; padding: 0 22px; border: 4px solid #111; background: #ff90e8; color: #111; font-weight: 900; text-transform: uppercase; text-decoration: none; box-shadow: 5px 5px 0 #111; transition: transform .1s, box-shadow .1s; }
.gcfnb-cta:hover { transform: translate(2px, 2px); box-shadow: 3px 3px 0 #111; }
.gcfnb-cta:active { transform: translate(5px, 5px); box-shadow: 0 0 0 #111; }
.gcfnb-links { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.gcfnb-links a { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border: 4px solid #111; background: var(--c, #fff); color: #111; font: 900 22px/1 ui-sans-serif, system-ui, sans-serif; text-transform: uppercase; text-decoration: none; transition: transform .1s; }
.gcfnb-links a:hover { transform: translateX(8px); }
.gcfnb-tape { grid-column: 1 / -1; display: flex; overflow: hidden; padding: 0; background: #111; color: #ffe14d; }
.gcfnb-run { display: flex; flex-shrink: 0; gap: 32px; padding: 12px 16px; white-space: nowrap; text-transform: uppercase; letter-spacing: .06em; animation: gcfnb-run 16s linear infinite; }
.gcfnb-legal { margin: 18px 0 0; font-size: 14px; }
@container (max-width: 640px) { .gcfnb-grid { grid-template-columns: 1fr; } }
@keyframes gcfnb-run { to { transform: translateX(-100%); } }`;
const DEFAULT_LINKS = [{ label: "Proyectos", color: "#7cf5c3" }, { label: "Servicios", color: "#8bc8ff" }, { label: "Contacto", color: "#ff9a8a" }];
export function FooterNeobrutal({ brand = "Hagamos ruido", cta = "Escríbenos", links = DEFAULT_LINKS, legal = "© 2026 Hagamos ruido. Todos los derechos reservados, casi todos.", className = "", style }: FooterNeobrutalProps) {
const phrase = "Sin letra pequeña ✺ Sin sorpresas ✺ Sin relleno ✺ ";
return (
<footer className={`gcfnb-root ${className}`} style={style}>
<style>{CSS}</style>
<div className="gcfnb-grid">
<div className="gcfnb-box">
<p className="gcfnb-brand">{brand}</p>
<a className="gcfnb-cta" href="#" onClick={(event) => event.preventDefault()}>
{cta}
</a>
</div>
<nav className="gcfnb-box" aria-label="Pie de página">
<ul className="gcfnb-links">
{links.map((link) => (
<li key={link.label}>
<a href={link.href ?? "#"} style={{ "--c": link.color } as CSSProperties} onClick={(event) => !link.href && event.preventDefault()}>
{link.label} <span aria-hidden="true">→</span>
</a>
</li>
))}
</ul>
</nav>
<div className="gcfnb-box gcfnb-tape" aria-hidden="true">
{[0, 1].map((n) => (
<span key={n} className="gcfnb-run">
{phrase.repeat(3)}
</span>
))}
</div>
</div>
<p className="gcfnb-legal">{legal}</p>
</footer>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/footer-neobrutalEjemplo de uso de Footer neobrutalista
Después de copiar footer-neobrutal.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { FooterNeobrutal } from "./footer-neobrutal";
export default function Ejemplo() {
return <FooterNeobrutal brand="Ejemplo" />;
}Propiedades de FooterNeobrutal
FooterNeobrutal 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. |
cta? | string | Opcional. |
links? | { label: string; href?: string; color?: string }[] | Opcional. |
legal? | string | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Personaliza Footer neobrutalista 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 |
|---|---|
--c | #fff |
Accesibilidad y rendimiento
El archivo tiene 72 líneas (4 KB) y define 1 animación con @keyframes. Esto es lo que hace Footer neobrutalista 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".