Skip to content

Footer Sitemap

Dense sitemap footer: boxed newsletter strip with success state, five ruled link columns with pink badges (New, hiring count), then brand, legal links and a language select.

brutalist/section/footer-sitemap
Open ↗

Source

"use client";

import { useId, useState, type FormEvent } from "react";
import { cn } from "@/lib/utils";

export interface FooterSitemapProps {
  brand?: string;
  columns?: { title: string; links: { label: string; href: string; badge?: string }[] }[];
  newsletter?: { title: string; button: string; onSubscribe?: (email: string) => void | Promise<void> };
  languages?: string[];
  legal?: { label: string; href: string }[];
  copyright?: string;
  className?: string;
}

const DEFAULT_COLUMNS: FooterSitemapProps["columns"] = [
  { title: "Product", links: [{ label: "Changelog pages", href: "#" }, { label: "In-app widget", href: "#" }, { label: "AI summaries", href: "#", badge: "New" }, { label: "Digests", href: "#" }, { label: "Pricing", href: "#" }] },
  { title: "Integrations", links: [{ label: "GitHub", href: "#" }, { label: "GitLab", href: "#" }, { label: "Linear", href: "#" }, { label: "Slack", href: "#" }, { label: "Zapier", href: "#" }] },
  { title: "Developers", links: [{ label: "Docs", href: "#" }, { label: "API reference", href: "#" }, { label: "Webhooks", href: "#" }, { label: "Status", href: "#" }] },
  { title: "Resources", links: [{ label: "Blog", href: "#" }, { label: "Templates", href: "#" }, { label: "Customers", href: "#" }, { label: "Guides", href: "#" }] },
  { title: "Company", links: [{ label: "About", href: "#" }, { label: "Careers", href: "#", badge: "3" }, { label: "Press", href: "#" }, { label: "Contact", href: "#" }] },
];

export function FooterSitemap({
  brand = "Shipyard",
  columns = DEFAULT_COLUMNS,
  newsletter = { title: "One email per month. Only what shipped.", button: "Subscribe" },
  languages = ["English", "Français", "Deutsch", "日本語"],
  legal = [
    { label: "Privacy", href: "#" },
    { label: "Terms", href: "#" },
    { label: "DPA", href: "#" },
    { label: "Security", href: "#" },
  ],
  copyright = `© ${new Date().getFullYear()} Shipyard Labs, Inc.`,
  className,
}: FooterSitemapProps) {
  const emailId = useId();
  const langId = useId();
  const [done, setDone] = useState(false);

  async function subscribe(e: FormEvent<HTMLFormElement>) {
    e.preventDefault();
    const email = new FormData(e.currentTarget).get("email");
    if (typeof email !== "string") return;
    await newsletter.onSubscribe?.(email);
    setDone(true);
  }

  return (
    <footer className={cn("da-stroke-t bg-da-surface-2 text-da-fg", className)}>
      <div className="da-container py-14">
        <div className="da-stroke grid bg-da-surface text-da-surface-fg shadow-da-md md:grid-cols-[1fr_1.2fr]">
          <p className="p-6 font-da-display text-2xl leading-tight tracking-da-display uppercase md:da-stroke-r">{newsletter.title}</p>
          {done ? (
            <p role="status" className="flex items-center bg-da-success p-6 font-bold text-da-success-fg">
              ✓ You’re in. First issue lands next month.
            </p>
          ) : (
            <form onSubmit={subscribe} className="flex flex-col sm:flex-row">
              <label htmlFor={emailId} className="sr-only">
                Email
              </label>
              <input
                id={emailId}
                name="email"
                type="email"
                required
                autoComplete="email"
                placeholder="you@company.com"
                className="da-focus da-stroke-t min-w-0 flex-1 bg-transparent px-6 py-5 text-lg md:border-t-0"
              />
              <button type="submit" className="da-focus da-stroke-t bg-da-fg px-6 py-5 font-bold text-da-bg hover:bg-da-secondary hover:text-da-secondary-fg sm:da-stroke-l sm:border-t-0">
                {newsletter.button}
              </button>
            </form>
          )}
        </div>

        <div className="mt-12 grid grid-cols-2 gap-8 sm:grid-cols-3 lg:grid-cols-5">
          {(columns ?? []).map((c) => (
            <nav key={c.title} aria-label={c.title}>
              <h2 className="da-stroke-b pb-2 font-da-mono text-xs font-bold tracking-da-label uppercase">{c.title}</h2>
              <ul className="mt-3 space-y-2 text-sm">
                {c.links.map((l) => (
                  <li key={l.label}>
                    <a href={l.href} className="da-focus inline-flex items-center gap-2 font-medium hover:bg-da-primary hover:text-da-primary-fg">
                      {l.label}
                      {l.badge && <span className="da-stroke bg-da-accent px-1 font-da-mono text-[10px] font-bold text-da-accent-fg">{l.badge}</span>}
                    </a>
                  </li>
                ))}
              </ul>
            </nav>
          ))}
        </div>

        <div className="da-stroke-t mt-12 flex flex-col gap-4 pt-6 lg:flex-row lg:items-center lg:justify-between">
          <p className="flex items-center gap-3 font-da-display text-xl tracking-da-display uppercase">
            {brand}
            <span className="font-da-mono text-xs font-normal tracking-normal normal-case text-da-muted-fg">{copyright}</span>
          </p>
          <div className="flex flex-wrap items-center gap-4">
            <ul className="flex flex-wrap gap-4 font-da-mono text-xs font-bold uppercase">
              {legal.map((l) => (
                <li key={l.label}>
                  <a href={l.href} className="da-focus underline underline-offset-4 hover:no-underline">
                    {l.label}
                  </a>
                </li>
              ))}
            </ul>
            <label htmlFor={langId} className="sr-only">
              Language
            </label>
            <select id={langId} className="da-focus da-stroke bg-da-surface px-3 py-1.5 font-da-mono text-xs font-bold text-da-surface-fg">
              {languages.map((l) => (
                <option key={l}>{l}</option>
              ))}
            </select>
          </div>
        </div>
      </div>
    </footer>
  );
}

export default FooterSitemap;

modules/brutalist/section/footer-sitemap/index.tsx

Props

PropTypeDefaultDescription
brandstring"Shipyard"Brand name.
columns{ title, links: { label, href, badge? }[] }[]—Up to 5 link columns.
newsletter{ title, button, onSubscribe? }—Newsletter strip.
languagesstring[]—Options of the language select (wire onChange yourself).
legal{ label: string; href: string }[]—Legal links.
copyrightstring—Copyright line.
classNamestring—Classes on the <footer>.

Other footer variants in Brutalist

Footer in other art directions

Footer Cta

Glass footer panel that opens with a gradient CTA band (headline + white button), then brand, tagline, three link columns and a legal row.

GlassFooter

Footer Glass

Inset frosted footer panel with brand, tagline, pill newsletter form (success state), three link columns and a legal bar.

GlassFooter

Footer Pill

Compact floating glass pill footer: gradient dot brand with copyright, inline links and round social buttons; becomes a stacked rounded card on mobile.

GlassFooter

Footer Wordmark

A giant gradient brand wordmark sits behind a frosted glass panel holding three link columns and the legal line, so the letters glow through the blur.

GlassFooter

Footer Minimal

Single-row footer: small square mark, brand and copyright, a handful of links and a live ‘All systems normal’ status pill. Stacks on mobile.

MinimalFooter

Footer Newsletter

Tinted footer with brand, tagline and an inline newsletter form on the left, three link columns on the right and a legal row below.

MinimalFooter

Footer Simple

Quiet footer: brand, one-line description and a live status pill with pulsing dot, three link columns in muted text, and a hairline bottom bar with copyright and social links.

MinimalFooter

Footer Wordmark

Footer with a row of links, address and socials, ending on a giant edge-to-edge lowercase brand wordmark that fades into the page and is cropped by the bottom edge.

MinimalFooter

Footer Columns

Footer with a newsletter: underline-input “Studio notes” signup in the first half, link columns in the second, all on a hairline grid; mono copyright row.

Mono CleanFooter

Footer Giant

Signature footer: small link rows above, and the brand name set enormous across the full width at the bottom, clipped by the page edge; inverted (ink) background.

Mono CleanFooter

Footer Minimal

One-line footer: logo mark, copyright, and links separated by slashes — all in mono caps on a single hairline row; wraps gracefully on mobile.

Mono CleanFooter

Footer Mono

Grid footer: ink top rule, logo + one-line description in the first columns, three mono-headed link columns, and a bottom row of legal links and a live-status signal dot.

Mono CleanFooter

Footer Compact

Single-row compact footer: logo mark + copyright on the left, inline links on the right, hairline top border; stacks on mobile.

Neo CorporateFooter

Footer Enterprise

Large enterprise footer on surface-2: brand + tagline + region selector on the left, five link columns, then a bottom bar with legal links, compliance chips and copyright.

Neo CorporateFooter

Footer Newsletter

Footer led by a newsletter band (“The Finance Brief”) with an inline email form, then three link columns and a copyright line.

Neo CorporateFooter

Footer Status

Developer-oriented dark footer (fg/bg inverted): brand, live system-status pill with pulsing dot, API version in mono, and three link columns.

Neo CorporateFooter

Footer Garden

Large footer on deep sage (a dark sage tint in dark mode) with rolling-hill silhouettes along the top edge, logo + tagline, three link columns and a “we measure our own footprint” note in the bottom line.

Organic SoftFooter

Footer Letter

Footer written like the end of a letter: a huge italic serif sign-off, a handwritten-style signature line, contact email and a simple link row under a warm rule.

Organic SoftFooter

Footer Newsletter

Footer with an inline journal signup: logo + rounded email form in a sage-tinted card on the left, two link columns on the right, and a sand-colored bottom strip.

Organic SoftFooter

Footer Simple

Quiet centered footer: logo and serif wordmark, a single row of links separated by leaf dots, and a small copyright line on cream.

Organic SoftFooter

Footer Columns

Classic footer on a soft panel with big rounded top corners: shape logo and tagline, three link columns in rounded headings, and a legal row with a language pill.

Soft FlatFooter

Footer Cta

Footer opening with a lavender CTA card that overlaps the top of a soft bottom band holding the brand, inline links and legal line.

Soft FlatFooter

Footer Pastel

Playful footer where each link column is its own pastel tile (peach, mint, lavender) plus a butter brand tile with round emoji social buttons and the legal line.

Soft FlatFooter

Footer Simple

Centered minimal footer: rounded wordmark with a periwinkle dot, a row of soft pill links and a small “made with care” line.

Soft FlatFooter