caliper uidocs

Footer block

Block

The page's last word: link columns, a newsletter that confirms in place, the legal line, status and the name as a full-width wordmark.

Installs components/ui/footer-block.tsxDepends on lucide-reactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/footer-block.json

When to use #

Use it for

  • The end of every marketing page.
  • You want sign-up and navigation within reach of the last section.

Skip it when

  • Inside an app; use the app frame.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/footer-block.json

Adds its 4 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { type ReactNode } from "react"
import { FooterBlock } from "@/components/ui/footer-block"

function Frame({ children }: { children: ReactNode }) {
  return <div className="w-full overflow-hidden rounded-[14px] shadow-[0_0_0_1px_var(--border)]">{children}</div>;
}

const columns = (product: string[], company: string[] = ["About", "Careers", "Contact"], resources: string[] = ["Docs", "Changelog", "Status"]) => [
  { title: "Product", links: product.map((label) => ({ label, href: "#" })) },
  { title: "Resources", links: resources.map((label) => ({ label, href: "#" })) },
  { title: "Company", links: company.map((label) => ({ label, href: "#" })) },
  { title: "Legal", links: ["Privacy", "Terms", "DPA"].map((label) => ({ label, href: "#" })) },
];

const footers: Parameters<typeof FooterBlock>[0] = { brand: "Halden", tagline: "Month-end close for finance teams. Lisbon and London.", columns: columns(["Matching", "Review", "Audit trail", "Pricing"]), newsletter: { label: "The close, monthly", note: "One email at month end. Unsubscribe in one click.", onSubmit: () => new Promise((resolve) => setTimeout(resolve, 600)) }, legal: "© 2026 Halden Ltd", status: { label: "All systems normal", href: "#" } };

export function FooterBlockDemo() {
  return <Frame><FooterBlock {...footers} /></Frame>;
}

API reference #

FooterBlock

component
PropTypeDefaultDescription
brand *string—
taglinestring—
columns *readonly BlockFooterColumn[]—
newsletter{ label: string; note?: string | undefined; onSubmit?: ((email: string) => void | Promise<void>) | undefined; }—An email sign-up. `onSubmit` may return a promise; the field confirms when it settles.
legalReactNode—e.g. "© 2026 Halden Ltd · Privacy · Terms".
status{ label: string; href: string; }—A live system status line, e.g. { label: "All systems normal", href: "https://status…" }.
wordmarkbooleantrueSet the brand as a full-width wordmark at the foot.
classNamestring—

Accessibility #

It exposes status semantics, with aria-hidden, aria-invalid, aria-describedby and aria-label kept in step with state. Changes that matter are announced through a live region.

No custom key handling; Tab and the native activation keys do the work.

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.