diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..3353803 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "Dev Server", + "runtimeExecutable": "/Users/andripurnomo/.volta/bin/pnpm", + "runtimeArgs": ["dev"], + "port": 5173 + }, + { + "name": "Preview (production build)", + "runtimeExecutable": "pnpm", + "runtimeArgs": ["preview"], + "port": 4173 + } + ] +} diff --git a/biome.json b/biome.json index e28fa5d..143ff10 100644 --- a/biome.json +++ b/biome.json @@ -19,6 +19,13 @@ "recommended": true } }, + "css": { + "parser": { + "allowWrongLineComments": false, + "cssModules": false, + "tailwindDirectives": true + } + }, "javascript": { "formatter": { "quoteStyle": "double" diff --git a/index.html b/index.html index d4808a5..7ee0942 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,7 @@ + Full-Stack Visualized diff --git a/src/components/api-patterns/DecisionTreeDemo.tsx b/src/components/api-patterns/DecisionTreeDemo.tsx index 34d0fec..4e18a41 100644 --- a/src/components/api-patterns/DecisionTreeDemo.tsx +++ b/src/components/api-patterns/DecisionTreeDemo.tsx @@ -126,7 +126,7 @@ export function DecisionTreeDemo() { return (
{/* Decision tree visualization */} -
+
{!finalResult ? ( {idx < Object.keys(QUESTIONS).length - 1 && ( -
+
)}
); @@ -163,7 +163,7 @@ export function DecisionTreeDemo() { {/* Question */}
-

+

{question.text}

@@ -173,14 +173,14 @@ export function DecisionTreeDemo() { @@ -196,62 +196,62 @@ export function DecisionTreeDemo() { {/* Result */}
{result?.icon}
-

+

{result?.technology}

-

{result?.description}

+

{result?.description}

{/* Architecture diagram */} -
+
💻
- Client + Client
{finalResult === "websocket" ? ( <>
- +
- +
- +
- +
- + Bidirectional ) : finalResult === "sse" ? ( <> -
+
- +
- +
- + Server → Client ) : ( <>
- +
- +
- + Periodic Requests @@ -262,14 +262,14 @@ export function DecisionTreeDemo() {
🖥️
- Server + Server
{/* Use cases */}
-

+

Common Use Cases

@@ -279,9 +279,9 @@ export function DecisionTreeDemo() { initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: idx * 0.1 }} - className="flex items-center gap-2 text-sm text-zinc-300" + className="flex items-center gap-2 text-sm text-text-secondary" > - + {useCase} ))} @@ -291,7 +291,7 @@ export function DecisionTreeDemo() { @@ -307,7 +307,7 @@ export function DecisionTreeDemo() { animate={{ opacity: 1, y: 0 }} className="space-y-4" > -

+

Implementation Example

@@ -408,29 +408,33 @@ app.get('/api/status', async (req, res) => { {/* Decision summary */} {!finalResult && ( -
-

Quick Guide

-
+
+

+ Quick Guide +

+
- + - WebSocket: Chat, gaming, - collaborative tools, or any bidirectional real-time + WebSocket: Chat, + gaming, collaborative tools, or any bidirectional real-time communication
- 📡 + 📡 - SSE: Notifications, - logs, dashboards - when server pushes updates to client only + SSE:{" "} + Notifications, logs, dashboards - when server pushes updates to + client only
- 🔄 + 🔄 - Polling: Background - sync, email checks - when updates are infrequent (<1/min) + Polling:{" "} + Background sync, email checks - when updates are infrequent + (<1/min)
diff --git a/src/components/api-patterns/FetchingTradeoffsDemo.tsx b/src/components/api-patterns/FetchingTradeoffsDemo.tsx index 2b28550..30a914f 100644 --- a/src/components/api-patterns/FetchingTradeoffsDemo.tsx +++ b/src/components/api-patterns/FetchingTradeoffsDemo.tsx @@ -79,8 +79,8 @@ export function FetchingTradeoffsDemo() { onClick={() => setScenario("initial")} className={`px-4 py-2 rounded-lg text-sm font-semibold border transition-all ${ scenario === "initial" - ? "bg-violet-500/15 text-violet-300 border-violet-500/40" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600" + ? "bg-violet-500/15 text-accent-violet border-violet-500/40" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary" }`} > 📝 Initial Feature @@ -90,15 +90,15 @@ export function FetchingTradeoffsDemo() { onClick={() => setScenario("add-avatar")} className={`px-4 py-2 rounded-lg text-sm font-semibold border transition-all ${ scenario === "add-avatar" - ? "bg-violet-500/15 text-violet-300 border-violet-500/40" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600" + ? "bg-violet-500/15 text-accent-violet border-violet-500/40" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary" }`} > ➕ Add Avatar Feature
-
+
{scenario === "initial" ? ( <> Social media feed showing post title, content, author name, and @@ -120,15 +120,15 @@ export function FetchingTradeoffsDemo() { onClick={() => setSelectedApproach("rest")} className={`w-full text-left px-4 py-3 rounded-lg border transition-all ${ selectedApproach === "rest" - ? "bg-amber-500/15 text-amber-300 border-amber-500/40" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600" + ? "bg-amber-500/15 text-accent-amber border-amber-500/40" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary" }`} >
📦 REST
-
+
Fixed response structure
@@ -141,29 +141,29 @@ export function FetchingTradeoffsDemo() { > {/* Metrics */}
-
-
Used
-
+
+
Used
+
{usedBytes}KB
-
-
Wasted
-
+
+
Wasted
+
{wastedBytes}KB
-
-
Efficiency
-
+
+
Efficiency
+
{efficiency}%
{/* Fields visualization */} -
-
+
+
GET /api/posts/123 ({totalBytes}KB)
@@ -175,17 +175,17 @@ export function FetchingTradeoffsDemo() { transition={{ delay: i * 0.03 }} className={`flex items-center justify-between px-3 py-2 rounded text-xs font-mono ${ field.used - ? "bg-green-500/10 text-green-300 border border-green-500/20" - : "bg-rose-500/10 text-rose-300 border border-rose-500/20" + ? "bg-green-500/10 text-accent-green border border-green-500/20" + : "bg-rose-500/10 text-accent-rose border border-rose-500/20" }`} > {field.name}
- {field.bytes}KB + {field.bytes}KB {field.used ? ( - + ) : ( - + )}
@@ -197,8 +197,8 @@ export function FetchingTradeoffsDemo() {
{scenario === "initial" ? ( @@ -237,15 +237,15 @@ const post = await fetch('/api/posts/123'); onClick={() => setSelectedApproach("graphql")} className={`w-full text-left px-4 py-3 rounded-lg border transition-all ${ selectedApproach === "graphql" - ? "bg-cyan-500/15 text-cyan-300 border-cyan-500/40" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600" + ? "bg-cyan-500/15 text-accent-cyan border-cyan-500/40" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary" }`} >
GraphQL
-
Request exact fields
+
Request exact fields
{selectedApproach === "graphql" && ( @@ -256,29 +256,29 @@ const post = await fetch('/api/posts/123'); > {/* Metrics */}
-
-
Used
-
+
+
Used
+
{usedBytes}KB
-
-
Wasted
-
+
+
Wasted
+
{wastedBytes}KB
-
-
Efficiency
-
+
+
Efficiency
+
{efficiency}%
{/* Fields visualization */} -
-
+
+
POST /graphql ({totalBytes}KB)
@@ -288,12 +288,12 @@ const post = await fetch('/api/posts/123'); initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: i * 0.03 }} - className="flex items-center justify-between px-3 py-2 rounded text-xs font-mono bg-green-500/10 text-green-300 border border-green-500/20" + className="flex items-center justify-between px-3 py-2 rounded text-xs font-mono bg-green-500/10 text-accent-green border border-green-500/20" > {field.name}
- {field.bytes}KB - + {field.bytes}KB +
))} @@ -304,8 +304,8 @@ const post = await fetch('/api/posts/123');
{scenario === "initial" ? ( @@ -360,28 +360,30 @@ const post = await fetch('/api/posts/123');
{/* Key insights */} -
-

Key Insights

-
+
+

+ Key Insights +

+
- + - REST over-fetches - wastes - bandwidth but future-proof (new UI needs might already be + REST over-fetches - + wastes bandwidth but future-proof (new UI needs might already be satisfied)
- + - GraphQL optimal - zero - waste but requires query updates for new fields + GraphQL optimal - + zero waste but requires query updates for new fields
- + - Trade-off - REST: + Trade-off - REST: bandwidth vs flexibility. GraphQL: efficiency vs maintenance.
diff --git a/src/components/api-patterns/RealTimeStrategiesDemo.tsx b/src/components/api-patterns/RealTimeStrategiesDemo.tsx index a8fcd43..15bbcca 100644 --- a/src/components/api-patterns/RealTimeStrategiesDemo.tsx +++ b/src/components/api-patterns/RealTimeStrategiesDemo.tsx @@ -294,27 +294,27 @@ export function RealTimeStrategiesDemo() { const className = match({ isSelected, color: s.color }) .with( { isSelected: true, color: "rose" }, - () => "bg-rose-500/15 text-rose-300 border-rose-500/40", + () => "bg-rose-500/15 text-accent-rose border-rose-500/40", ) .with( { isSelected: true, color: "amber" }, - () => "bg-amber-500/15 text-amber-300 border-amber-500/40", + () => "bg-amber-500/15 text-accent-amber border-amber-500/40", ) .with( { isSelected: true, color: "cyan" }, - () => "bg-cyan-500/15 text-cyan-300 border-cyan-500/40", + () => "bg-cyan-500/15 text-accent-cyan border-cyan-500/40", ) .with( { isSelected: true, color: "green" }, - () => "bg-green-500/15 text-green-300 border-green-500/40", + () => "bg-green-500/15 text-accent-green border-green-500/40", ) .with( { isSelected: true, color: "violet" }, - () => "bg-violet-500/15 text-violet-300 border-violet-500/40", + () => "bg-violet-500/15 text-accent-violet border-violet-500/40", ) .otherwise( () => - "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600", + "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary", ); return ( @@ -335,12 +335,12 @@ export function RealTimeStrategiesDemo() { {/* Live dashboard simulation */}
{/* Stock price widget */} -
-
ACME Stock Price
-
+
+
ACME Stock Price
+
${stockPrice}
-
+
{activities.slice(-20).map((act) => ( -
-
Latency
-
+
+
Latency
+
{strategy.latency}
-
-
Bandwidth
-
+
+
Bandwidth
+
{strategy.bandwidth}
-
-
Time
-
{elapsedTime}s
+
+
Time
+
+ {elapsedTime}s +
{/* Network activity log */} -
-
+
+
Network Activity ({totalBytes.toFixed(1)} KB total)
@@ -390,14 +392,14 @@ export function RealTimeStrategiesDemo() { exit={{ opacity: 0 }} className="flex items-center gap-2 text-xs font-mono" > - + {new Date(act.timestamp).toLocaleTimeString()} "text-amber-400") - .with("response", () => "text-cyan-400") - .with("push", () => "text-green-400") + .with("request", () => "text-accent-amber-soft") + .with("response", () => "text-accent-cyan-soft") + .with("push", () => "text-accent-green-soft") .exhaustive()} > {match(act.type) @@ -406,8 +408,8 @@ export function RealTimeStrategiesDemo() { .with("push", () => "↓") .exhaustive()} - {act.type} - {act.bytes}KB + {act.type} + {act.bytes}KB ))} @@ -419,8 +421,8 @@ export function RealTimeStrategiesDemo() { onClick={running ? reset : runAnimation} className={`w-full py-2 rounded-lg text-sm font-semibold transition-colors ${ running - ? "bg-zinc-700 text-zinc-300" - : "bg-violet-600 hover:bg-violet-500 text-white" + ? "bg-surface-tertiary text-text-secondary" + : "bg-violet-600 hover:bg-violet-500 text-text-primary" }`} > {running ? "⏹ Stop" : "▶ Start Monitoring"} @@ -429,7 +431,7 @@ export function RealTimeStrategiesDemo() { {/* Implementation */}
-

+

Implementation

@@ -449,7 +451,7 @@ setInterval(async () => { showLineNumbers={false} className="text-xs" /> -
+
⚠️ High bandwidth waste. Most polls return "no updates". Slow latency.
@@ -473,7 +475,7 @@ async function longPoll() { showLineNumbers={false} className="text-xs" /> -
+
Better than polling but still uses HTTP overhead for each update.
@@ -498,7 +500,7 @@ eventSource.onmessage = (event) => { showLineNumbers={false} className="text-xs" /> -
+
✓ Simpler than WebSocket. Perfect for server → client updates (logs, notifications).
@@ -526,7 +528,7 @@ ws.onmessage = (event) => { showLineNumbers={false} className="text-xs" /> -
+
✓ Lowest latency, most efficient. Use when <1s latency required.
@@ -554,7 +556,7 @@ const [result] = useSubscription({ showLineNumbers={false} className="text-xs" /> -
+
✓ WebSocket under the hood. GraphQL syntax for subscriptions.
diff --git a/src/components/api-patterns/RequestPatternComparisonDemo.tsx b/src/components/api-patterns/RequestPatternComparisonDemo.tsx index 2cca027..6a459f5 100644 --- a/src/components/api-patterns/RequestPatternComparisonDemo.tsx +++ b/src/components/api-patterns/RequestPatternComparisonDemo.tsx @@ -220,23 +220,23 @@ export function RequestPatternComparisonDemo() { const className = match({ isSelected, color: p.color }) .with( { isSelected: true, color: "rose" }, - () => "bg-rose-500/15 text-rose-300 border-rose-500/40", + () => "bg-rose-500/15 text-accent-rose border-rose-500/40", ) .with( { isSelected: true, color: "amber" }, - () => "bg-amber-500/15 text-amber-300 border-amber-500/40", + () => "bg-amber-500/15 text-accent-amber border-amber-500/40", ) .with( { isSelected: true, color: "cyan" }, - () => "bg-cyan-500/15 text-cyan-300 border-cyan-500/40", + () => "bg-cyan-500/15 text-accent-cyan border-cyan-500/40", ) .with( { isSelected: true, color: "violet" }, - () => "bg-violet-500/15 text-violet-300 border-violet-500/40", + () => "bg-violet-500/15 text-accent-violet border-violet-500/40", ) .otherwise( () => - "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600", + "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary", ); return ( @@ -253,46 +253,46 @@ export function RequestPatternComparisonDemo() { })}
-

{pattern.description}

+

{pattern.description}

{/* Animation */}
{/* Metrics */}
-
-
Requests
-
+
+
Requests
+
{pattern.requestCount}
-
-
Data (KB)
-
+
+
Data (KB)
+
{pattern.totalBytes}
-
+
Waterfall Depth
-
+
{pattern.waterfallDepth}
{/* Waterfall visualization */} -
+
-
+
Network Waterfall
{selected === "rest-waterfall" && requests.length > 0 && ( -
+
Indentation = Sequential dependency
@@ -306,8 +306,8 @@ export function RequestPatternComparisonDemo() { className="flex items-center gap-3" style={{ paddingLeft: `${req.depth * 20}px` }} > -
-
+
+
{req.label}
@@ -338,7 +338,7 @@ export function RequestPatternComparisonDemo() { )}
-
+
{req.bytes}KB
@@ -346,9 +346,9 @@ export function RequestPatternComparisonDemo() {
{requests.length > 0 && ( -
+
{completedCount === requests.length ? ( - + ✓ All requests completed ) : running ? ( @@ -356,7 +356,7 @@ export function RequestPatternComparisonDemo() { Loading... ({completedCount}/{requests.length}) ) : ( - Ready to simulate + Ready to simulate )}
)} @@ -367,8 +367,8 @@ export function RequestPatternComparisonDemo() { onClick={running ? reset : runAnimation} className={`w-full py-2 rounded-lg text-sm font-semibold transition-colors ${ running - ? "bg-zinc-700 text-zinc-300" - : "bg-violet-600 hover:bg-violet-500 text-white" + ? "bg-surface-tertiary text-text-secondary" + : "bg-violet-600 hover:bg-violet-500 text-text-primary" }`} > {running @@ -381,7 +381,7 @@ export function RequestPatternComparisonDemo() { {/* Code example */}
-

+

Implementation

@@ -400,7 +400,7 @@ const authors = await fetch(\`/api/posts/authors?ids=\${posts.map(p => p.authorI showLineNumbers={false} className="text-xs" /> -
+
⚠️ Each request waits for the previous one. Depth-4 waterfall kills performance.
@@ -427,7 +427,7 @@ const data = await fetch('/api/user/123?embed=posts,likes,authors'); showLineNumbers={false} className="text-xs" /> -
+
⚠️ Fast but wastes bandwidth. Returns 45KB when you only need 12KB.
@@ -459,7 +459,7 @@ query GetUserProfile($id: ID!) { showLineNumbers={false} className="text-xs" /> -
+
✓ Solves both over-fetching and under-fetching. Single request, exact data.
@@ -490,7 +490,7 @@ export const appRouter = router({ showLineNumbers={false} className="text-xs" /> -
+
✓ Perfect for TS monorepos. No GraphQL schema, no codegen. Just functions.
diff --git a/src/components/api-patterns/WaterfallOptimizerDemo.tsx b/src/components/api-patterns/WaterfallOptimizerDemo.tsx index 31a7f07..8669864 100644 --- a/src/components/api-patterns/WaterfallOptimizerDemo.tsx +++ b/src/components/api-patterns/WaterfallOptimizerDemo.tsx @@ -246,8 +246,8 @@ export function WaterfallOptimizerDemo() { const lcpColor = opt.lcp <= 600 ? "green" : opt.lcp <= 1200 ? "amber" : "rose"; const className = isSelected - ? "bg-violet-500/15 text-violet-300 border-violet-500/40" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:border-zinc-600"; + ? "bg-violet-500/15 text-accent-violet border-violet-500/40" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:border-border-tertiary"; return (
-

{optimization.description}

+

{optimization.description}

{/* Metrics */}
-
-
Waterfall Depth
-
+
+
Waterfall Depth
+
{optimization.depth}
-
-
LCP (ms)
+
+
LCP (ms)
{optimization.lcp}
-
-
Requests
-
{requests.length}
+
+
Requests
+
+ {requests.length} +
{/* Waterfall visualization */} -
+
-
+
Request Timeline
-
+
{running ? `${currentTime}ms` : "Ready"}
{/* Timeline */} -
+
{/* Time markers */} -
+
{selected === "prefetch" ? ( <> -400ms @@ -373,9 +375,9 @@ export function WaterfallOptimizerDemo() { width: `${widthPercent}%`, }} > -
+
- + {req.label}
@@ -422,8 +424,8 @@ export function WaterfallOptimizerDemo() { onClick={running ? reset : runAnimation} className={`mt-4 w-full py-2 rounded-lg text-sm font-semibold transition-colors ${ running - ? "bg-zinc-700 text-zinc-300" - : "bg-violet-600 hover:bg-violet-500 text-white" + ? "bg-surface-tertiary text-text-secondary" + : "bg-violet-600 hover:bg-violet-500 text-text-primary" }`} > {running ? "⏹ Stop" : requests.length > 0 ? "↺ Replay" : "▶ Animate"} @@ -431,19 +433,19 @@ export function WaterfallOptimizerDemo() {
{/* Key insights */} -
-

+
+

Optimization Strategy

-
+
{selected === "sequential" && ( <>
- + Depth-3 waterfall adds 1800ms latency
- + Each request blocks the next - poor LCP
@@ -451,11 +453,11 @@ export function WaterfallOptimizerDemo() { {selected === "parallel" && ( <>
- + All requests fire at once - depth reduced to 1
- + Over-fetching trade-off for speed (extra /user/profile) @@ -465,11 +467,11 @@ export function WaterfallOptimizerDemo() { {selected === "graphql" && ( <>
- + Single query fetches all related data
- + Best of both worlds - fast + no over-fetching
@@ -477,11 +479,11 @@ export function WaterfallOptimizerDemo() { {selected === "prefetch" && ( <>
- + Starts loading 400ms before user clicks (on hover)
- + Instant perceived load - data ready when needed
diff --git a/src/components/async-patterns/AsyncAwaitDemo.tsx b/src/components/async-patterns/AsyncAwaitDemo.tsx index a5b09c9..665d395 100644 --- a/src/components/async-patterns/AsyncAwaitDemo.tsx +++ b/src/components/async-patterns/AsyncAwaitDemo.tsx @@ -101,17 +101,17 @@ const STEPS: Step[] = [ const PHASE_COLORS = { sync: { - text: "text-violet-300", + text: "text-accent-violet", bg: "bg-violet-500/10", border: "border-violet-500/20", }, await: { - text: "text-amber-300", + text: "text-accent-amber", bg: "bg-amber-500/10", border: "border-amber-500/20", }, microtask: { - text: "text-emerald-300", + text: "text-accent-emerald", bg: "bg-emerald-500/10", border: "border-emerald-500/20", }, @@ -127,7 +127,7 @@ export function AsyncAwaitDemo() {
{/* Code panel */}
-
+
Source Code
{/* Call Stack */}
-
+
Call Stack
-
+
{current.stack.length === 0 ? ( - + (empty) ) : ( @@ -157,7 +157,7 @@ export function AsyncAwaitDemo() { initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -10 }} - className="px-3 py-1.5 rounded bg-violet-500/10 border border-violet-500/20 text-xs font-mono text-violet-300" + className="px-3 py-1.5 rounded bg-violet-500/10 border border-violet-500/20 text-xs font-mono text-accent-violet" > {frame} @@ -169,13 +169,13 @@ export function AsyncAwaitDemo() { {/* Microtask Queue */}
-
+
Microtask Queue
-
+
{current.microtasks.length === 0 ? ( - + (empty) ) : ( @@ -185,7 +185,7 @@ export function AsyncAwaitDemo() { initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} - className="px-3 py-1.5 rounded bg-emerald-500/10 border border-emerald-500/20 text-xs font-mono text-emerald-300" + className="px-3 py-1.5 rounded bg-emerald-500/10 border border-emerald-500/20 text-xs font-mono text-accent-emerald" > {task} @@ -214,7 +214,7 @@ export function AsyncAwaitDemo() { type="button" onClick={() => setStep((s) => Math.max(0, s - 1))} disabled={step <= 0} - className="px-4 py-2 rounded-lg text-sm font-medium bg-zinc-800 text-zinc-400 border border-zinc-700 hover:text-zinc-300 transition-colors disabled:opacity-40 disabled:cursor-not-allowed" + className="px-4 py-2 rounded-lg text-sm font-medium bg-surface-secondary text-text-tertiary border border-border-secondary hover:text-text-secondary transition-colors disabled:opacity-40 disabled:cursor-not-allowed" > ← Back @@ -222,18 +222,18 @@ export function AsyncAwaitDemo() { type="button" onClick={() => setStep((s) => Math.min(STEPS.length - 1, s + 1))} disabled={step >= STEPS.length - 1} - className="px-4 py-2 rounded-lg text-sm font-medium bg-emerald-500/20 text-emerald-300 border border-emerald-500/30 hover:bg-emerald-500/30 transition-colors disabled:opacity-40 disabled:cursor-not-allowed" + className="px-4 py-2 rounded-lg text-sm font-medium bg-emerald-500/20 text-accent-emerald border border-emerald-500/30 hover:bg-emerald-500/30 transition-colors disabled:opacity-40 disabled:cursor-not-allowed" > Next → - + Step {step + 1} / {STEPS.length}
@@ -249,14 +249,15 @@ export function AsyncAwaitDemo() { {/* Key insight */} -
-

Key insight

+
+

Key insight

- await doesn't block the - thread — it suspends the current async function and queues - its continuation as a microtask when the awaited value settles. + await doesn't block + the thread — it suspends the current async function and + queues its continuation as a microtask when the awaited value settles. Synchronous code after the{" "} - await call site runs first. + await call site runs + first.

diff --git a/src/components/async-patterns/CombinatorsDemo.tsx b/src/components/async-patterns/CombinatorsDemo.tsx index 54b075e..beab50d 100644 --- a/src/components/async-patterns/CombinatorsDemo.tsx +++ b/src/components/async-patterns/CombinatorsDemo.tsx @@ -209,8 +209,8 @@ export function CombinatorsDemo() { }} className={`px-4 py-2 rounded-lg text-sm font-mono font-medium border transition-colors ${ combinator === c - ? "bg-cyan-500/20 text-cyan-300 border-cyan-500/30" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200" + ? "bg-cyan-500/20 text-accent-cyan border-cyan-500/30" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:text-text-secondary" }`} > Promise.{c}() @@ -218,21 +218,23 @@ export function CombinatorsDemo() { ))}
-

{desc.summary}

+

{desc.summary}

{/* Promise configs */}
{promises.map((p, i) => (
-
{p.label}
+
+ {p.label} +
@@ -263,8 +265,8 @@ export function CombinatorsDemo() { }} className={`text-xs px-2 py-1 rounded border transition-colors ${ p.succeeds - ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" - : "bg-rose-500/10 text-rose-400 border-rose-500/20" + ? "bg-emerald-500/10 text-accent-emerald-soft border-emerald-500/20" + : "bg-rose-500/10 text-accent-rose-soft border-rose-500/20" }`} > {p.succeeds ? "✓ Fulfills" : "✗ Rejects"} @@ -274,14 +276,14 @@ export function CombinatorsDemo() { {/* Progress bar */} {result && ( -
+
{running ? "Running..." : "▶ Run"} @@ -312,8 +314,8 @@ export function CombinatorsDemo() { exit={{ opacity: 0 }} className={`p-4 rounded-lg border text-sm font-mono ${ result.status === "done" - ? "bg-emerald-500/10 border-emerald-500/20 text-emerald-300" - : "bg-rose-500/10 border-rose-500/20 text-rose-300" + ? "bg-emerald-500/10 border-emerald-500/20 text-accent-emerald" + : "bg-rose-500/10 border-rose-500/20 text-accent-rose" }`} > @@ -331,15 +333,15 @@ export function CombinatorsDemo() {
- - - + + - + - + {( Object.entries(COMBINATOR_DESCRIPTIONS) as [ Combinator, @@ -350,11 +352,11 @@ export function CombinatorsDemo() { key={key} className={combinator === key ? "bg-cyan-500/5" : ""} > - - - + + ))} diff --git a/src/components/async-patterns/ErrorHandlingDemo.tsx b/src/components/async-patterns/ErrorHandlingDemo.tsx index 4d0ac90..202c565 100644 --- a/src/components/async-patterns/ErrorHandlingDemo.tsx +++ b/src/components/async-patterns/ErrorHandlingDemo.tsx @@ -149,10 +149,10 @@ export function ErrorHandlingDemo() {
{/* Error propagation visualizer */}
-
+
Inject error at stage
-

+

When an async function throws, every subsequent await in the chain is skipped. Select a stage to see where the error surfaces and what gets bypassed. @@ -166,9 +166,9 @@ export function ErrorHandlingDemo() { className={`px-3 py-1.5 rounded-lg text-sm font-mono border transition-colors ${ errorStage === stage ? stage === "none" - ? "bg-emerald-500/20 text-emerald-300 border-emerald-500/30" - : "bg-rose-500/20 text-rose-300 border-rose-500/30" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200" + ? "bg-emerald-500/20 text-accent-emerald border-emerald-500/30" + : "bg-rose-500/20 text-accent-rose border-rose-500/30" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:text-text-secondary" }`} > {stage === "none" ? "No error" : stage} @@ -177,7 +177,7 @@ export function ErrorHandlingDemo() {

{/* Pipeline visualization */} -
+
{CHAIN_STAGES.map((stage, i) => { const isError = errorStage !== "none" && i === stageIndex; @@ -196,12 +196,12 @@ export function ErrorHandlingDemo() { }} className={`px-3 py-2 rounded-lg text-xs font-mono border ${ isError - ? "bg-rose-500/20 text-rose-300 border-rose-500/30" + ? "bg-rose-500/20 text-accent-rose border-rose-500/30" : isSkipped - ? "bg-zinc-800/50 text-zinc-600 border-zinc-800" + ? "bg-surface-secondary/50 text-text-faint border-border-primary" : isSuccess - ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" - : "bg-zinc-800 text-zinc-400 border-zinc-700" + ? "bg-emerald-500/10 text-accent-emerald-soft border-emerald-500/20" + : "bg-surface-secondary text-text-tertiary border-border-secondary" }`} > {stage}() @@ -210,7 +210,7 @@ export function ErrorHandlingDemo() { {i < CHAIN_STAGES.length - 1 && ( = stageIndex ? "text-zinc-700" : "text-zinc-500"}`} + className={`text-xs ${errorStage !== "none" && i + 1 >= stageIndex ? "text-text-secondary" : "text-text-muted"}`} > → @@ -226,7 +226,7 @@ export function ErrorHandlingDemo() { initial={{ opacity: 0, y: 4 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }} - className="mt-3 text-xs text-rose-400 font-mono" + className="mt-3 text-xs text-accent-rose-soft font-mono" > Error thrown at {errorStage} — bubbles up through the async chain @@ -237,7 +237,7 @@ export function ErrorHandlingDemo() { initial={{ opacity: 0, y: 4 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0 }} - className="mt-3 text-xs text-emerald-400 font-mono" + className="mt-3 text-xs text-accent-emerald-soft font-mono" > All stages pass — pipeline resolves successfully @@ -250,10 +250,10 @@ export function ErrorHandlingDemo() { {/* Retry with backoff */}
-
+
Retry with exponential backoff
-

+

Transient failures (network timeouts, rate limits) can be retried safely — unlike deterministic errors (parse, validate) which will always fail again. Exponential backoff doubles the wait between @@ -261,9 +261,9 @@ export function ErrorHandlingDemo() {

-
+
Simulating:{" "} - + transient network error {" "} — e.g. timeout, 503 @@ -273,7 +273,7 @@ export function ErrorHandlingDemo() { type="button" onClick={runRetry} disabled={retrying} - className="px-5 py-2 rounded-lg text-sm font-medium bg-amber-500/20 text-amber-300 border border-amber-500/30 hover:bg-amber-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" + className="px-5 py-2 rounded-lg text-sm font-medium bg-amber-500/20 text-accent-amber border border-amber-500/30 hover:bg-amber-500/30 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" > {retrying ? "Running..." : "▶ Simulate retry sequence"} @@ -287,10 +287,10 @@ export function ErrorHandlingDemo() { animate={{ opacity: 1, x: 0 }} className="flex items-center gap-3 text-sm" > - + Attempt {a.attempt} -
+
{a.status === "running" @@ -327,10 +327,10 @@ export function ErrorHandlingDemo() { {/* Best practices */}
-
+
Best practices checklist
-

+

Not all errors are equal — some should be retried, some re-thrown with context, and some indicate a fatal bug. These patterns separate robust async code from fragile code. @@ -346,11 +346,15 @@ export function ErrorHandlingDemo() { }`} > {item.good ? "✓" : "✗"} - + {item.text}

diff --git a/src/components/async-patterns/RaceConditionsDemo.tsx b/src/components/async-patterns/RaceConditionsDemo.tsx index bf72f8f..6617453 100644 --- a/src/components/async-patterns/RaceConditionsDemo.tsx +++ b/src/components/async-patterns/RaceConditionsDemo.tsx @@ -171,9 +171,9 @@ export function RaceConditionsDemo() { className={`px-4 py-2 rounded-lg text-sm font-medium border transition-colors ${ mode === m ? m === "none" - ? "bg-rose-500/20 text-rose-300 border-rose-500/30" - : "bg-emerald-500/20 text-emerald-300 border-emerald-500/30" - : "bg-zinc-800 text-zinc-400 border-zinc-700 hover:text-zinc-200" + ? "bg-rose-500/20 text-accent-rose border-rose-500/30" + : "bg-emerald-500/20 text-accent-emerald border-emerald-500/30" + : "bg-surface-secondary text-text-tertiary border-border-secondary hover:text-text-secondary" }`} > {FIX_LABELS[m]} @@ -186,7 +186,7 @@ export function RaceConditionsDemo() {
@@ -196,13 +196,13 @@ export function RaceConditionsDemo() { value={query} onChange={handleInput} placeholder="Type here..." - className="w-full px-3 py-2 rounded-lg bg-zinc-900 border border-zinc-700 text-sm text-zinc-200 placeholder-zinc-600 focus:outline-none focus:border-cyan-500/50" + className="w-full px-3 py-2 rounded-lg bg-surface-primary border border-border-secondary text-sm text-text-secondary placeholder-text-faint focus:outline-none focus:border-cyan-500/50" />
@@ -220,20 +220,20 @@ export function RaceConditionsDemo() {
{/* Request timeline */} -
-
+
+
Request Timeline
{requests.length === 0 ? ( -

+

Type above to fire requests...

) : ( @@ -245,15 +245,17 @@ export function RaceConditionsDemo() { animate={{ opacity: 1, x: 0 }} className="flex items-center gap-3 text-xs" > - #{req.id} - + + #{req.id} + + "{req.query}" -
+
{req.cancelled @@ -301,8 +303,8 @@ export function RaceConditionsDemo() { animate={{ opacity: 1, scale: 1 }} className={`p-3 rounded-lg border text-sm font-mono ${ mode === "none" && requests.some((r) => r.stale && r.resolvedAt) - ? "bg-rose-500/10 border-rose-500/20 text-rose-300" - : "bg-emerald-500/10 border-emerald-500/20 text-emerald-300" + ? "bg-rose-500/10 border-rose-500/20 text-accent-rose" + : "bg-emerald-500/10 border-emerald-500/20 text-accent-emerald" }`} > Displayed: {displayedResult} diff --git a/src/components/auth-flows/AuthFlowSequence.tsx b/src/components/auth-flows/AuthFlowSequence.tsx index f9d1a6e..0a82b39 100644 --- a/src/components/auth-flows/AuthFlowSequence.tsx +++ b/src/components/auth-flows/AuthFlowSequence.tsx @@ -226,7 +226,7 @@ export function AuthFlowSequence({ className="absolute -top-6 left-1/2 -translate-x-1/2 whitespace-nowrap" >
-
+
{step.data.map((item) => (
- {item.key}: - + {item.key}: + {item.value}
diff --git a/src/components/auth-flows/ComparisonTableDemo.tsx b/src/components/auth-flows/ComparisonTableDemo.tsx index c76267e..a9f5475 100644 --- a/src/components/auth-flows/ComparisonTableDemo.tsx +++ b/src/components/auth-flows/ComparisonTableDemo.tsx @@ -169,94 +169,104 @@ export function ComparisonTableDemo() {
Method +
Method Resolves when Rejects whenRejects when
+ Promise.{key}() {d.resolves}{d.rejects}{d.resolves}{d.rejects}
- - + - - - - + - - - + + - - - - - - + + + + + - - + - - + - - - + + - - + + - - - + + - - - - + + - - + + - - - - + + + + - - - - + + + - +
+
Feature + Session-Based + JWT + OAuth 2.0
StorageServer (DB/Redis) + Storage + Server (DB/Redis) + Client (depends on impl) + Delegated (provider)
Stateful?YesNoHybrid
Stateful?YesNoHybrid
Logout + Logout Server deletes session + Client deletes (can't force) Revoke at provider + Revoke at provider +
XSS Safe? +
XSS Safe? ✓ Yes (httpOnly cookie) Depends on storageDepends on impl + Depends on storage + Depends on impl
CSRF Safe?No (needs tokens) + CSRF Safe? + No (needs tokens) + ✓ Yes (manual header) + ✓ Yes (state param)
Scalability +
Scalability Harder (session store) Easier (stateless)Offloaded + Easier (stateless) + Offloaded
Mobile AppsPoor fitGoodExcellentMobile AppsPoor fitGoodExcellent
Token RotationN/A +
Token RotationN/A Manual (refresh tokens) Built-inBuilt-in
{/* Interactive Decision Guide */} -
+
{!finalResult ? ( {idx < Object.keys(QUESTIONS).length - 1 && ( -
+
)}
); @@ -292,10 +302,10 @@ export function ComparisonTableDemo() { {/* Question */}
-

+

Decision Guide

-

+

{question.text}

@@ -305,14 +315,14 @@ export function ComparisonTableDemo() { @@ -328,15 +338,15 @@ export function ComparisonTableDemo() { {/* Result */}
{result?.icon}
-

+

{result?.technology}

-

{result?.description}

+

{result?.description}

{/* Use cases */}
-

+

Common Use Cases

@@ -346,9 +356,9 @@ export function ComparisonTableDemo() { initial={{ opacity: 0, x: -10 }} animate={{ opacity: 1, x: 0 }} transition={{ delay: idx * 0.08 }} - className="flex items-center gap-2 text-sm text-zinc-300" + className="flex items-center gap-2 text-sm text-text-secondary" > - + {useCase} ))} @@ -358,7 +368,7 @@ export function ComparisonTableDemo() { @@ -369,29 +379,29 @@ export function ComparisonTableDemo() { {/* Quick reference shown while deciding */} {!finalResult && ( -
-

+
+

Quick Reference

-
+
- 🔑 + 🔑 - OAuth 2.0: Third-party - login, SSO, API delegation + OAuth 2.0:{" "} + Third-party login, SSO, API delegation
- 🪙 + 🪙 - JWT: Microservices, - mobile apps, stateless scaling + JWT:{" "} + Microservices, mobile apps, stateless scaling
- 🗄️ + 🗄️ - Session-Based:{" "} + Session-Based:{" "} Traditional web apps, immediate revocation
@@ -402,11 +412,13 @@ export function ComparisonTableDemo() { {/* Visual Flow Comparison */}
-

Flow Comparison

+

+ Flow Comparison +

@@ -510,7 +522,7 @@ function FlowCard({ {index + 1}
-
{step}
+
{step}
))}
diff --git a/src/components/auth-flows/InspectPanel.tsx b/src/components/auth-flows/InspectPanel.tsx index c5b6609..72c5eab 100644 --- a/src/components/auth-flows/InspectPanel.tsx +++ b/src/components/auth-flows/InspectPanel.tsx @@ -36,14 +36,16 @@ export function InspectPanel({ transition={{ type: "spring", stiffness: 300, damping: 30 }} className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 w-full max-w-2xl max-h-[80vh] overflow-auto" > -
+
{/* Header */} -
-

{title}

+
+

+ {title} +

@@ -303,7 +303,7 @@ export function JWTAuthDemo() { initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} onClick={handleTamper} - className="px-6 py-2.5 rounded-lg bg-rose-500 hover:bg-rose-600 text-white font-medium transition-colors" + className="px-6 py-2.5 rounded-lg bg-rose-500 hover:bg-rose-600 text-text-primary font-medium transition-colors" > Tamper with Token @@ -317,15 +317,15 @@ export function JWTAuthDemo() { animate={{ opacity: 1, y: 0 }} className="flex flex-wrap gap-4" > -
- Storage: +
+ Storage:
-
-