COMPONENT / monolith-footer
Monolith Footer_
Footer with up to four link or text columns and a giant wordmark cropped by its bottom edge. Requires @cybr/tokens.
Install
1. Map the
@cybrnamespace, once per project. Add this inside yourcomponents.json, or into its existing"registries"object. Nocomponents.jsonyet? Runnpx shadcn@latest initfirst to create one."registries": { "@cybr": "https://cybr-ui.com/r/{name}.json" }2. Add components:
$ npx shadcn@latest add @cybr/monolith-footer
If shadcn reports Unknown registry "@cybr", step 1 is missing from that project's components.json.
Preview
registry/ui/monolith-footer.tsx
import { useId, type ComponentProps, type ReactNode } from "react";
import { cn } from "@/lib/utils";
export type MonolithFooterItem = { label: string; href: string } | { text: string };
export type MonolithFooterColumn = { heading: string; items: MonolithFooterItem[] };
// Footer with up to four columns of links or plain text lines over a giant
// wordmark that the footer's bottom and right edges crop. The wordmark is
// decorative and aria-hidden; overflow-hidden keeps it from widening the page.
export function MonolithFooter({
columns,
wordmark,
className,
...props
}: Omit<ComponentProps<"footer">, "children"> & {
columns: MonolithFooterColumn[];
wordmark?: ReactNode;
}) {
const id = useId();
return (
<footer
className={cn("relative overflow-hidden bg-ground-deep px-6 pt-5.5 font-mono sm:px-10", className)}
{...props}
>
<div className="grid max-w-225 grid-cols-2 gap-8 sm:grid-cols-4">
{columns.map((column, i) => (
<div key={column.heading}>
<p id={`${id}-${i}`} className="m-0 mb-2 text-caption tracking-[0.16em] text-signal">
{column.heading}
</p>
<ul aria-labelledby={`${id}-${i}`} className="m-0 list-none p-0 text-xs tracking-[0.08em]">
{column.items.map((entry, j) => (
<li key={j} className="py-1">
{"href" in entry ? (
<a
href={entry.href}
className="text-label hover:text-ink focus-visible:outline-(length:--focus-width) focus-visible:outline-offset-2 focus-visible:outline-solid focus-visible:outline-signal"
>
{entry.label}
</a>
) : (
<span className="text-label">{entry.text}</span>
)}
</li>
))}
</ul>
</div>
))}
</div>
<div
aria-hidden="true"
className="pointer-events-none mt-10 -mb-[0.12em] -ml-2 font-display text-[clamp(4.5rem,17vw,13.375rem)] leading-[0.8] tracking-[-0.03em] whitespace-nowrap text-ink select-none"
>
{wordmark ?? (
<>
CYBR<span className="text-signal">{"//"}</span>UI
</>
)}
</div>
</footer>
);
}
Repo source. The shadcn CLI rewrites import aliases to match your project.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| columns | { heading: string; items: ({ label: string; href: string } | { text: string })[] }[] | — | Up to four columns. Items with href render as links; items with text render as plain lines, such as a build number. |
| wordmark | ReactNode | CYBR//UI | The giant cropped wordmark, set in the display font. |
| className | string | — | Extra classes, merged after the defaults with cn(). |
| ...props | Omit<ComponentProps<"footer">, "children"> | — | Every other native <footer> attribute and ref is forwarded. |
Usage
Needs the stylesheet from @cybr/tokens. After the install, import it in app/globals.css:
@import "tailwindcss";
@import "../styles/cybr-tokens.css";
import { MonolithFooter } from "@/components/ui/monolith-footer";
export function SiteFooter() {
return (
<MonolithFooter
columns={[
{ heading: "LIBRARY", items: [{ label: "Docs", href: "/docs" }] },
{ heading: "SYSTEM", items: [{ text: "REV 0.1" }] },
]}
/>
);
}
States
- Default: columns on
--ground-deepwith--signalheadings and--labelitems, over an--inkwordmark cropped by the bottom and right edges. - Link hover: the link turns
--ink. - Focus-visible: a 3px solid
--signaloutline 2px outside the link. - Narrow: columns drop from 4 to 2 below the
smbreakpoint, and the wordmark shrinks with the viewport (clamp(4.5rem, 17vw, 13.375rem)). It always crops, and never widens the page. - Nothing animates.
Accessibility
- A
<footer>element, which is the page's contentinfo landmark when it is not inside another sectioning element. - Each column is a list named by its heading.
- The wordmark is
aria-hiddenand ignores pointer events. Put your real site name in a link or text elsewhere if it matters to screen reader users. --labelon--ground-deepis 8.51:1 and--signalheadings on--ground-deepare 5.31:1.