From cf36c54b764ac2c2d26bdb2a26b51e32a032b44d Mon Sep 17 00:00:00 2001
From: Justin Levine <20596508+justinlevinedotme@users.noreply.github.com>
Date: Mon, 30 Mar 2026 17:26:58 -0700
Subject: [PATCH] feat(docs): add sponsor page, site footer, and header sponsor
link
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add /sponsor page with hero, metallic plaque sponsor tiers, stargazers wall, and CTA
- Stargazers fetched from GitHub API at build time (1hr revalidation)
- Hatch pattern strips flanking content with grid background outside
- Add shared SiteFooter component with logo, project links, Discord, and GitHub
- Add ♡ Sponsor button to docs header
- Footer used on both docs layout and sponsor page
---
apps/docs/app/docs/layout.tsx | 10 +
apps/docs/app/sponsor/page.tsx | 375 +++++++++++++++++++++++++++
apps/docs/components/docs/footer.tsx | 126 +++++++++
3 files changed, 511 insertions(+)
create mode 100644 apps/docs/app/sponsor/page.tsx
create mode 100644 apps/docs/components/docs/footer.tsx
diff --git a/apps/docs/app/docs/layout.tsx b/apps/docs/app/docs/layout.tsx
index 08ccdad..494993a 100644
--- a/apps/docs/app/docs/layout.tsx
+++ b/apps/docs/app/docs/layout.tsx
@@ -1,5 +1,6 @@
import type { ReactNode } from "react"
import Link from "next/link"
+import { Heart } from "lucide-react"
import { FumadocsSidebar } from "@/components/docs/fumadocs-sidebar"
import { MobileNav } from "@/components/docs/mobile-nav"
import { ThemeSwitcher } from "@/components/docs/theme-switcher"
@@ -8,6 +9,7 @@ import { source } from "@/lib/source"
import { JalcoLogo } from "@/components/icons/jalco-logo"
import { GitHubStarsButton } from "@/registry/github-stars-button/github-stars-button"
+import { SiteFooter } from "@/components/docs/footer"
export default function DocsLayout({ children }: { children: ReactNode }) {
@@ -50,6 +52,12 @@ export default function DocsLayout({ children }: { children: ReactNode }) {
+
+
+
+ Sponsor
+
+
+
+
)
diff --git a/apps/docs/app/sponsor/page.tsx b/apps/docs/app/sponsor/page.tsx
new file mode 100644
index 0000000..62a14b3
--- /dev/null
+++ b/apps/docs/app/sponsor/page.tsx
@@ -0,0 +1,375 @@
+import type { Metadata } from "next"
+import Image from "next/image"
+import Link from "next/link"
+import { Heart, ExternalLink, Star, Plus } from "lucide-react"
+import { JalcoLogo } from "@/components/icons/jalco-logo"
+import { ThemeSwitcher } from "@/components/docs/theme-switcher"
+import { GitHubStarsButton } from "@/registry/github-stars-button/github-stars-button"
+import { SiteFooter } from "@/components/docs/footer"
+import { Button } from "@/registry/ui/button"
+
+export const metadata: Metadata = {
+ title: "Sponsor — jal-co/ui",
+ description:
+ "Support jal-co/ui. Every component is free and always will be — but if you want to help, this is the way.",
+}
+
+const GITHUB_SPONSORS_URL = "https://github.com/sponsors/jal-co"
+
+interface Stargazer {
+ login: string
+ avatar_url: string
+}
+
+async function getStargazers(): Promise {
+ try {
+ const pages: Stargazer[] = []
+ let page = 1
+ // Fetch up to 5 pages (500 stargazers) to be safe
+ while (page <= 5) {
+ const res = await fetch(
+ `https://api.github.com/repos/jal-co/ui/stargazers?per_page=100&page=${page}`,
+ {
+ headers: { Accept: "application/vnd.github.v3+json" },
+ next: { revalidate: 3600 },
+ }
+ )
+ if (!res.ok) break
+ const data: Stargazer[] = await res.json()
+ if (data.length === 0) break
+ pages.push(...data)
+ if (data.length < 100) break
+ page++
+ }
+ // Filter out the org account
+ return pages.filter((s) => s.login !== "jal-co")
+ } catch {
+ return []
+ }
+}
+
+const tiers = [
+ {
+ name: "Gold",
+ slots: 3,
+ sponsors: [] as { name: string; href: string; logo?: string }[],
+ colors: {
+ bg: "linear-gradient(145deg, #d4a84b 0%, #f5d98a 30%, #c9952e 60%, #e8c55a 100%)",
+ text: "#5c3d0e",
+ border: "#c9952e",
+ slotBg: "rgba(212, 168, 75, 0.08)",
+ slotBorder: "rgba(201, 149, 46, 0.25)",
+ },
+ },
+ {
+ name: "Silver",
+ slots: 3,
+ sponsors: [] as { name: string; href: string; logo?: string }[],
+ colors: {
+ bg: "linear-gradient(145deg, #a8a8a8 0%, #d4d4d4 30%, #8a8a8a 60%, #c0c0c0 100%)",
+ text: "#2a2a2a",
+ border: "#8a8a8a",
+ slotBg: "rgba(168, 168, 168, 0.06)",
+ slotBorder: "rgba(138, 138, 138, 0.2)",
+ },
+ },
+ {
+ name: "Bronze",
+ slots: 4,
+ sponsors: [] as { name: string; href: string; logo?: string }[],
+ colors: {
+ bg: "linear-gradient(145deg, #b5745a 0%, #d4956e 30%, #8c5a3e 60%, #c98a68 100%)",
+ text: "#3d1e0e",
+ border: "#8c5a3e",
+ slotBg: "rgba(181, 116, 90, 0.06)",
+ slotBorder: "rgba(140, 90, 62, 0.2)",
+ },
+ },
+]
+
+export default async function SponsorPage() {
+ const stargazers = await getStargazers()
+ return (
+
+
+
+
+
+
+
+
+
+ {/* Hero */}
+
+
+
+
+ Sponsor
+
+
+ I'll never charge, but if you want to help
+
+
+ jal-co/ui is an open source React component library distributed
+ through the shadcn registry. Every component is free and that's
+ not changing.
+
+
+ I'm not going to paywall components or gate features behind a
+ sponsorship tier. That's not the point. But if something from
+ the registry saved you an afternoon, or you just like that this
+ exists in the open, sponsoring is a nice way to say so. It helps
+ me justify spending real time on it instead of treating it like a
+ side-of-desk thing.
+
+
+ Any amount is genuinely appreciated. And if money's not your
+ thing, starring the repo or sharing a component you liked works
+ too.
+
+
+
+
+
+
+ Sponsor on GitHub
+
+
+
+
+
+
+
+ {/* Sponsors */}
+
+
+
+ {tiers.map((tier) => {
+ const filled = tier.sponsors.length
+ const empty = tier.slots - filled
+
+ return (
+
+ {/* Plaque header */}
+
+ {/* Rivets */}
+ {["left-2.5 top-1/2 -translate-y-1/2", "right-2.5 top-1/2 -translate-y-1/2"].map((pos) => (
+
+ ))}
+
+ {tier.name}
+
+
+
+ {/* Sponsor slots */}
+
+
+ )
+ })}
+
+
+ {/* Stargazers */}
+ {stargazers.length > 0 && (
+
+
+
+ Stargazers
+
+
+ {stargazers.length}
+
+
+
+
+ {stargazers.map((user) => (
+
+
+
+ ))}
+
+
+ )}
+
+ {/* CTA */}
+
+
+
+ Want to support the project?
+
+
+ Every bit helps — whether it's a sponsorship, a star, or sharing
+ something you found useful.
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/docs/components/docs/footer.tsx b/apps/docs/components/docs/footer.tsx
new file mode 100644
index 0000000..4750f1b
--- /dev/null
+++ b/apps/docs/components/docs/footer.tsx
@@ -0,0 +1,126 @@
+import Link from "next/link"
+import { JalcoLogo } from "@/components/icons/jalco-logo"
+
+function DiscordIcon(props: React.SVGProps) {
+ return (
+
+
+
+ )
+}
+
+function GitHubIcon(props: React.SVGProps) {
+ return (
+
+
+
+ )
+}
+
+export function SiteFooter() {
+ return (
+
+
+
+
+
+
+ jal-co/ui
+
+
+ Open source React components for Tailwind CSS. Free forever, never
+ paywalled.
+
+
+
+
+
+
+ Project
+
+
+ Components
+
+
+ Installation
+
+
+ Sponsor
+
+
+
+
+
+
+
+
+
+
+ )
+}