Skip to content

Commit 152af36

Browse files
committed
♻️ Restructure home page to the v3 arc (GC framing + use-case tabs)
Reworks the narrative from the mockup's v3 ("arc B"): lead with the garbage-collection analogy instead of the leak bug, name structured concurrency at the moment the reader already feels the gap, then prove it with a new interactive "evidence" section. Changes: - New GC-first hero ("In JavaScript, you never free memory.") and a "The exception" section; drop the old problem/badge hero, the await-boundary table, the proven-model cards, and the leak-proof code block. - New components/usecase-tabs.tsx — five real use cases (timeout, parallel, stream, background, async teardown) as CSS-only tabs (radio/checkbox-hack, no JS), each a code sample + one-line guarantee. - Revised superpower/capstone/Effection copy per the v3 mockup; operation tree, rosetta and CTA unchanged.
1 parent 2ac5387 commit 152af36

2 files changed

Lines changed: 202 additions & 190 deletions

File tree

www/components/usecase-tabs.tsx

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import type { JSXElement } from "revolution/jsx-runtime";
2+
import { CodeWindow } from "./code-window.tsx";
3+
4+
interface UseCase {
5+
tab: string;
6+
filename: string;
7+
code: string;
8+
note: string;
9+
}
10+
11+
const CASES: UseCase[] = [
12+
{
13+
tab: "Timeout",
14+
filename: "timeout.ts",
15+
code: `function* fetchUser(id) {
16+
return yield* race([
17+
fetchJSON(\`/users/\${id}\`),
18+
sleep(5000),
19+
]);
20+
}`,
21+
note:
22+
"Nothing here cancels the losing request — you'd reach for an AbortController next. You don't have to. When one branch wins, the other is already halted.",
23+
},
24+
{
25+
tab: "Parallel",
26+
filename: "parallel.ts",
27+
code: `function* loadDashboard() {
28+
const [user, feed, prefs] = yield* all([
29+
fetchUser(),
30+
fetchFeed(),
31+
fetchPrefs(),
32+
]);
33+
return { user, feed, prefs };
34+
}`,
35+
note:
36+
"Promise.all rejects on the first failure but lets the other requests finish anyway. Here, one failure halts the siblings — no orphaned work.",
37+
},
38+
{
39+
tab: "Stream",
40+
filename: "stream.ts",
41+
code: `function* listen(socket) {
42+
for (const message of yield* each(socket)) {
43+
yield* handle(message);
44+
yield* each.next();
45+
}
46+
}`,
47+
note:
48+
"No socket.close(), no removeEventListener. Leave the loop — return, throw, or cancel — and the subscription closes itself.",
49+
},
50+
{
51+
tab: "Background",
52+
filename: "background.ts",
53+
code: `function* entrypoint() {
54+
yield* spawn(heartbeat);
55+
yield* serveRequests();
56+
}`,
57+
note:
58+
"heartbeat runs for as long as entrypoint does. No handle to keep, no teardown to write — when entrypoint returns or is cancelled, it's halted with it.",
59+
},
60+
{
61+
tab: "Async teardown",
62+
filename: "database.ts",
63+
code: `function* report() {
64+
const db = yield* resource(function* (provide) {
65+
const conn = yield* connect(DATABASE_URL);
66+
yield* ensure(() => conn.close());
67+
yield* provide(conn);
68+
});
69+
return yield* runQueries(db);
70+
}`,
71+
note:
72+
"conn.close() is async — an await inside a plain finally is exactly what gets abandoned on cancel. The resource's teardown runs to completion when report() exits, every time.",
73+
},
74+
];
75+
76+
/**
77+
* UseCaseTabs — five real Effection use cases that each look too simple to be
78+
* correct. CSS-only tabs (the site's radio/checkbox-hack convention, no JS): a
79+
* hidden radio per case drives which pill is active and which panel shows,
80+
* via `:checked ~` sibling selectors in a scoped <style> block.
81+
*/
82+
export function UseCaseTabs(): JSXElement {
83+
let GRADIENT = "linear-gradient(45deg,#f74d7b,#a855f7,#26abe8)";
84+
let css =
85+
CASES.map((_, i) =>
86+
`#uc-tab-${i}:checked~.uc-tablist label[for="uc-tab-${i}"]{color:#fff!important;border-color:transparent!important;background-image:${GRADIENT}!important;}` +
87+
`#uc-tab-${i}:checked~.uc-panels .uc-p${i}{display:block}`
88+
).join("\n") +
89+
"\n.uc-panel{display:none}.uc-panel{animation:ucfade .25s ease}" +
90+
"@keyframes ucfade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}";
91+
92+
return (
93+
<div class="uc-tabs mx-auto max-w-[680px]">
94+
<>
95+
{CASES.map((_, i) => (
96+
<input
97+
class="hidden"
98+
type="radio"
99+
name="uc-tabs"
100+
id={`uc-tab-${i}`}
101+
checked={i === 0}
102+
/>
103+
))}
104+
</>
105+
<div class="uc-tablist mb-5 flex flex-wrap justify-center gap-1.5">
106+
{CASES.map((c, i) => (
107+
<label
108+
for={`uc-tab-${i}`}
109+
class="cursor-pointer rounded-full border-2 border-gray-200 bg-white px-4 py-2 text-sm font-bold text-gray-600 transition-colors hover:border-[#26ABE8] hover:text-[#14315D] dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:text-[#26ABE8]"
110+
>
111+
{c.tab}
112+
</label>
113+
))}
114+
</div>
115+
<div class="uc-panels">
116+
{CASES.map((c, i) => (
117+
<div class={`uc-panel uc-p${i}`}>
118+
<CodeWindow filename={c.filename} code={c.code} />
119+
<p class="mx-auto mt-4 max-w-lg text-center text-[15px] leading-relaxed text-gray-600 dark:text-gray-300">
120+
{c.note}
121+
</p>
122+
</div>
123+
))}
124+
</div>
125+
<style>{css}</style>
126+
</div>
127+
);
128+
}

0 commit comments

Comments
 (0)