1---
2import Logo from "./Logo.astro";
3import { CONTRIBUTORS } from "../../data/contributors";
4
5interface FooterLink {
6 label: string;
7 href: string;
8}
9interface FooterCol {
10 h: string;
11 links: FooterLink[];
12}
13
14const cols: FooterCol[] = [
15 {
16 h: "Product",
17 links: [
18 { label: "Docs", href: "/getting-started" },
19 { label: "Tutorial", href: "/cloudflare/tutorial/part-1" },
20 { label: "Compare", href: "/compare" },
21 { label: "Effect", href: "https://effect.website" },
22 // todo(sam): create an /examples landing page (or sidebar entry)
23 // and re-enable: { label: "Examples", href: "/examples" }
24 // todo(sam): create /providers/index.md that lists every provider
25 // (Starlight currently autogenerates the sidebar from the
26 // providers/ folder but there's no overview page to link to).
27 // Re-enable: { label: "Providers", href: "/providers" }
28 // todo(sam): wire a /changelog page that mirrors the GitHub
29 // releases / CHANGELOG.md (either fetch at build time or add a
30 // generator that drops a markdown file under content/docs).
31 // Re-enable: { label: "Changelog", href: "/changelog" }
32 ],
33 },
34 {
35 h: "Community",
36 links: [
37 { label: "Discord", href: "/discord" },
38 { label: "GitHub", href: "https://github.com/alchemy-run/alchemy" },
39 { label: "X / Twitter", href: "/twitter" },
40 { label: "Blog", href: "/blog" },
41 { label: "Privacy", href: "/privacy" },
42 ],
43 },
44 // todo(sam): /compare covers SST, Pulumi, and Terraform on one page.
45 // Split into per-tool pages (and add CDK) to re-enable this column.
46 // {
47 // h: "Compare",
48 // links: [
49 // { label: "vs Pulumi", href: "/compare/pulumi" },
50 // { label: "vs SST", href: "/compare/sst" },
51 // { label: "vs Terraform", href: "/compare/terraform" },
52 // { label: "vs CDK", href: "/compare/cdk" },
53 // ],
54 // },
55];
56
57// Community contributors, most commits first: a few faces, then the rest
58// as a count linking to GitHub's contributors graph.
59const SHOWN = 8;
60const faces = CONTRIBUTORS.slice(0, SHOWN);
61const rest = CONTRIBUTORS.length - faces.length;
62const graph =
63 "https://github.com/alchemy-run/alchemy/graphs/contributors?all=1";
64---
65
66<footer class="alc-footer">
67 <div class="alc-footer__grid" style={`--footer-cols:${cols.length}`}>
68 <div>
69 <Logo />
70 <p class="alc-footer__tag">
71 Infrastructure as Effects. A single, type-safe TypeScript program for your cloud.
72 </p>
73 <div class="alc-footer__copy">Apache-2.0 · © 2026 Functionless, Inc.</div>
74 <div class="alc-footer__people">
75 <ul class="alc-footer__faces" aria-label="Contributors">
76 {faces.map((c) => (
77 <li>
78 <a
79 href={`https://github.com/${c.login}`}
80 title={`@${c.login} · ${c.commits} commits`}
81 >
82 <img
83 src={`https://github.com/${c.login}.png?size=64`}
84 alt={`@${c.login}`}
85 width="28"
86 height="28"
87 loading="lazy"
88 decoding="async"
89 />
90 </a>
91 </li>
92 ))}
93 </ul>
94 {rest > 0 && (
95 <a class="alc-footer__more" href={graph} aria-label={`${rest} more contributors on GitHub`}>
96 +{rest}
97 </a>
98 )}
99 </div>
100 </div>
101 {cols.map((col) => (
102 <div>
103 <div class="alc-footer__heading">{col.h}</div>
104 <ul class="alc-footer__list">
105 {col.links.map((l) => (
106 <li><a href={l.href} class="alc-footer__link">{l.label}</a></li>
107 ))}
108 </ul>
109 </div>
110 ))}
111 </div>
112</footer>
113
114<style>
115 .alc-footer {
116 padding: 40px 32px;
117 border-top: 1px solid var(--alc-hairline);
118 background: var(--alc-bg-nav);
119 }
120 .alc-footer__grid {
121 max-width: 1152px;
122 margin: 0 auto;
123 display: grid;
124 /* Brand on the left, link columns sized to their content and pinned to
125 the right edge, so the footer spans the same width as the page. */
126 grid-template-columns: minmax(0, 1fr) repeat(var(--footer-cols), max-content);
127 column-gap: clamp(48px, 8vw, 120px);
128 row-gap: 40px;
129 }
130 @media (max-width: 880px) {
131 .alc-footer__grid {
132 grid-template-columns: repeat(var(--footer-cols), minmax(0, 1fr));
133 gap: 28px 24px;
134 }
135 .alc-footer__grid > :first-child {
136 grid-column: 1 / -1;
137 max-width: 520px;
138 }
139 }
140 @media (max-width: 640px) {
141 .alc-footer { padding: 32px 20px; }
142 .alc-footer__grid {
143 grid-template-columns: 1fr 1fr;
144 gap: 24px 20px;
145 }
146 }
147 @media (max-width: 380px) {
148 .alc-footer__grid { grid-template-columns: 1fr; }
149 }
150 .alc-footer__tag {
151 max-width: 380px;
152 font-family: var(--alc-font-sans);
153 font-size: 13px;
154 line-height: 1.6;
155 color: var(--alc-fg-3);
156 margin: 14px 0 0;
157 }
158 .alc-footer__copy {
159 margin-top: 16px;
160 font-family: var(--alc-font-mono);
161 font-size: 11px;
162 color: var(--alc-fg-4);
163 }
164 .alc-footer__people {
165 margin-top: 22px;
166 display: flex;
167 align-items: center;
168 gap: 10px;
169 }
170 .alc-footer__faces {
171 list-style: none;
172 margin: 0;
173 padding: 0;
174 display: flex;
175 }
176 .alc-footer__faces li + li {
177 margin-left: -12px;
178 }
179 .alc-footer__faces a {
180 position: relative;
181 display: block;
182 border-radius: 50%;
183 transition: transform 160ms ease;
184 }
185 .alc-footer__faces a:hover,
186 .alc-footer__faces a:focus-visible {
187 z-index: 1;
188 transform: translateY(-2px);
189 }
190 .alc-footer__faces a:focus-visible {
191 outline: 2px solid var(--alc-accent);
192 outline-offset: 2px;
193 }
194 .alc-footer__faces img {
195 display: block;
196 width: 28px;
197 height: 28px;
198 border-radius: 50%;
199 /* A ring in the footer color separates the overlapping faces. */
200 box-shadow: 0 0 0 2px var(--alc-bg-nav);
201 background: var(--alc-hairline);
202 }
203 .alc-footer__more {
204 font-family: var(--alc-font-mono);
205 font-size: 13px;
206 color: var(--alc-accent-deep);
207 text-decoration: none;
208 }
209 .alc-footer__more:hover {
210 text-decoration: underline;
211 }
212 .alc-footer__heading {
213 font-family: var(--alc-font-mono);
214 font-size: 11px;
215 letter-spacing: 0.12em;
216 text-transform: uppercase;
217 color: var(--alc-fg-4);
218 margin-bottom: 12px;
219 }
220 .alc-footer__list {
221 list-style: none;
222 padding: 0;
223 margin: 0;
224 display: flex;
225 flex-direction: column;
226 gap: 8px;
227 }
228 .alc-footer__link {
229 text-decoration: none;
230 color: var(--alc-fg-2);
231 font-size: 13px;
232 }
233 .alc-footer__link:hover {
234 color: var(--alc-accent-deep);
235 }
236</style>