Files
zeekayandHanzo Dev da98c0de75 feat(console): one icon set — every glyph comes from @luxfi/ui/icons
74 files imported `lucide-react` directly, pinned ^1.26.0, while @luxfi/ui —
which this console renders alongside — resolves 1.27. Two Lucide copies from two
resolutions in one bundle is how a set stops being one set. Two more glyphs were
not from any icon library at all: the node list drew its network in/out arrows as
the literal characters "↓" and "↑", and the chain onboarding selector drew its
tick as "✓". Those are font glyphs — they take the text metrics, not the icon
box, so they sit on a different baseline and scale with the font, not `size-*`.

One source now: `@luxfi/ui/icons`, Lucide as @hanzo/gui standardises on it and
@luxfi/ui themes it. `lucide-react` is out of package.json, so per-surface drift
is no longer expressible; @luxfi/ui comes in at 7.4.3, which is where `./icons`
lives. The three text glyphs are ArrowDown / ArrowUp / Check.

All 121 distinct icon names this console uses were checked against the 6014
symbols @luxfi/ui/icons exports before the rewrite — zero missing, with a
negative control. `next build` is clean: 78 routes, Next 16.2.11 + Turbopack.

The d3 chart roots (node-globe, node-map, globe-view, donut-chart, fleet-map)
keep their <svg>: those are plots, not glyphs. The command menu's "↑↓" stays —
it is a keyboard legend inside <kbd>, and it means the keys.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 14:57:55 -07:00

140 lines
4.9 KiB
TypeScript

"use client"
import Link from "next/link"
import { BrandLogo, useBrand } from "@/components/brand-logo"
import { ExternalLink } from "@luxfi/ui/icons"
// Footer, in lux.cloud's chrome: no tinted slab, just a hairline rule over the
// page ground with 13px muted type. The four sitemap columns stay — dropping
// them to match lux.cloud's single row would delete real navigation — but the
// brand row underneath is lux.cloud's, and every brand string in it now comes
// from the hostname-resolved catalog rather than a constant.
const footerLinks = {
products: [
{ name: "Node (RPC)", href: "/products/node" },
{ name: "Token API", href: "/products/data" },
{ name: "NFT API", href: "/products/data" },
{ name: "Smart Wallets", href: "/products/wallets" },
{ name: "Webhooks", href: "/products/webhooks" },
{ name: "Rollups", href: "/products/rollups" },
],
chains: [
{ name: "Ethereum", href: "/chains/ethereum" },
{ name: "Solana", href: "/chains/solana" },
{ name: "Base", href: "/chains/base" },
{ name: "Arbitrum", href: "/chains/arbitrum" },
{ name: "Polygon", href: "/chains/polygon" },
{ name: "View All", href: "/chains" },
],
developers: [
{ name: "Documentation", href: "/docs" },
{ name: "API Reference", href: "/docs/api" },
{ name: "Quickstart", href: "/docs/quickstart" },
{ name: "SDKs", href: "/docs/sdks" },
{ name: "Changelog", href: "/docs/changelog" },
{ name: "llms.txt", href: "/llms.txt" },
],
}
// One rule for every footer link. `min-h-8` is not decoration: on a phone these
// were 20px tap targets in a 44px world.
const linkClass =
"inline-flex min-h-8 items-center gap-1 text-[13px] text-muted-foreground transition-colors hover:text-foreground"
function Column({ title, links }: { title: string; links: { name: string; href: string }[] }) {
return (
<div>
<h3 className="text-[11px] font-medium uppercase tracking-[0.08em] text-muted-foreground">{title}</h3>
<ul className="mt-3 space-y-0.5">
{links.map((link) => (
<li key={link.name}>
<Link href={link.href} className={linkClass}>
{link.name}
</Link>
</li>
))}
</ul>
</div>
)
}
export function Footer() {
const brand = useBrand()
return (
<footer className="mt-24 border-t border-border">
<div className="mx-auto max-w-[1440px] px-6 py-12 max-sm:px-4">
<div className="grid gap-8 sm:grid-cols-2 md:grid-cols-5">
<div className="md:col-span-1">
<Link href="/">
<BrandLogo />
</Link>
<p className="mt-3 max-w-56 text-[13px] leading-relaxed text-muted-foreground">{brand.tagline}</p>
</div>
<Column title="Products" links={footerLinks.products} />
<Column title="Chains" links={footerLinks.chains} />
<Column title="Developers" links={footerLinks.developers} />
<div>
<h3 className="text-[11px] font-medium uppercase tracking-[0.08em] text-muted-foreground">Network</h3>
<ul className="mt-3 space-y-0.5">
<li>
<a href={brand.networkUrl} className={linkClass}>
{brand.networkName}
<ExternalLink className="h-3 w-3" />
</a>
</li>
<li>
<a href={brand.statusUrl} className={linkClass}>
Status
<ExternalLink className="h-3 w-3" />
</a>
</li>
{brand.social.github && (
<li>
<a href={brand.social.github} className={linkClass}>
GitHub
<ExternalLink className="h-3 w-3" />
</a>
</li>
)}
{brand.footerLinks.map((link) => (
<li key={link.name}>
<a
href={link.href}
target={link.external ? "_blank" : undefined}
rel={link.external ? "noopener noreferrer" : undefined}
className={linkClass}
>
{link.name}
{link.external && <ExternalLink className="h-3 w-3" />}
</a>
</li>
))}
</ul>
</div>
</div>
<div className="mt-10 flex flex-col items-center justify-between gap-3 border-t border-border pt-6 sm:flex-row">
<p className="text-[13px] text-muted-foreground">
© {new Date().getFullYear()} {brand.name}
</p>
<div className="flex gap-5">
<Link href="/privacy" className={linkClass}>
Privacy
</Link>
<Link href="/terms" className={linkClass}>
Terms
</Link>
<Link href="/security" className={linkClass}>
Security
</Link>
</div>
</div>
</div>
</footer>
)
}