<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>T3 Stack Architecture</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,sans-serif;
background:#0a0a0f;color:#e2e8f0;
display:flex;justify-content:center;align-items:flex-start;
min-height:100vh;padding:24px 16px;
}
.card{
width:100%;max-width:800px;
background:linear-gradient(145deg,rgba(15,15,25,0.95),rgba(10,10,18,0.98));
border:1px solid rgba(255,255,255,0.08);border-radius:16px;
overflow:hidden;
}
.card-header{
padding:28px 32px 20px;
border-bottom:1px solid rgba(255,255,255,0.06);
}
.card-header h1{font-size:1.35rem;font-weight:700;color:#f1f5f9;margin-bottom:6px}
.card-header p{font-size:0.82rem;color:#94a3b8;line-height:1.5}
.badge-row{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}
.badge{
font-size:0.68rem;padding:3px 10px;border-radius:20px;
background:rgba(34,211,238,0.1);color:#22d3ee;
border:1px solid rgba(34,211,238,0.2);
}
.badge.hard{
background:rgba(239,68,68,0.1);color:#f87171;
border-color:rgba(239,68,68,0.25);
}
.body{padding:24px 32px 28px}
.section-title{
font-size:0.78rem;font-weight:700;color:#22d3ee;
text-transform:uppercase;letter-spacing:0.08em;margin-bottom:12px;
}
/* ── Type safety chain ── */
.chain{
display:flex;align-items:stretch;gap:0;margin-bottom:28px;
background:rgba(0,0,0,0.25);border:1px solid rgba(255,255,255,0.06);
border-radius:12px;overflow:hidden;
}
.chain-step{
flex:1;padding:14px 10px;text-align:center;
border-right:1px solid rgba(255,255,255,0.05);
position:relative;
}
.chain-step:last-child{border-right:none}
.chain-step .icon{font-size:1.2rem;margin-bottom:4px}
.chain-step .name{font-size:0.65rem;font-weight:700;color:#f1f5f9;margin-bottom:2px}
.chain-step .desc{font-size:0.58rem;color:#64748b;line-height:1.4}
.chain-step::after{
content:'\2192';position:absolute;right:-8px;top:50%;
transform:translateY(-50%);color:#22d3ee;font-size:0.85rem;z-index:1;
}
.chain-step:last-child::after{content:none}
/* color coding each step */
.chain-step:nth-child(1){background:rgba(59,130,246,0.06)}
.chain-step:nth-child(2){background:rgba(168,85,247,0.06)}
.chain-step:nth-child(3){background:rgba(34,211,238,0.06)}
.chain-step:nth-child(4){background:rgba(74,222,128,0.06)}
.chain-step:nth-child(5){background:rgba(250,204,21,0.06)}
/* ── Folder tree ── */
.tree{
background:rgba(0,0,0,0.35);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:18px 20px;
font-family:'SF Mono',SFMono-Regular,Consolas,'Liberation Mono',Menlo,monospace;
font-size:0.7rem;line-height:1.7;overflow-x:auto;white-space:pre;
color:#cbd5e1;margin-bottom:24px;
}
.tree .folder{color:#22d3ee;font-weight:600}
.tree .file{color:#e2e8f0}
.tree .comment{color:#64748b;font-style:italic}
/* ── Stack grid ── */
.stack-grid{
display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:24px;
}
.stack-item{
background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);
border-radius:10px;padding:12px 14px;text-align:center;
}
.stack-item .tech{font-size:0.72rem;font-weight:700;color:#f1f5f9;margin-bottom:2px}
.stack-item .role{font-size:0.6rem;color:#64748b;line-height:1.4}
.stack-item:nth-child(1) .tech{color:#60a5fa}
.stack-item:nth-child(2) .tech{color:#c084fc}
.stack-item:nth-child(3) .tech{color:#22d3ee}
.stack-item:nth-child(4) .tech{color:#4ade80}
.stack-item:nth-child(5) .tech{color:#facc15}
.stack-item:nth-child(6) .tech{color:#f472b6}
/* ── CLI box ── */
.cli{
background:rgba(0,0,0,0.4);border:1px solid rgba(255,255,255,0.08);
border-radius:10px;padding:14px 18px;margin-bottom:24px;
font-family:'SF Mono',Consolas,monospace;font-size:0.72rem;
color:#4ade80;display:flex;align-items:center;gap:10px;
}
.cli .prompt{color:#64748b;user-select:none}
.cli .cmd{color:#e2e8f0}
/* ── When to Use ── */
.when-grid{
display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:24px;
}
.when-item{
background:rgba(255,255,255,0.02);border:1px solid rgba(255,255,255,0.06);
border-radius:8px;padding:10px 14px;
}
.when-item .label{font-size:0.65rem;color:#64748b;margin-bottom:2px}
.when-item .val{font-size:0.7rem;color:#e2e8f0}
.when-item .val.yes{color:#4ade80}
.when-item .val.no{color:#f87171}
.when-item .val.maybe{color:#facc15}
/* ── No codegen callout ── */
.callout{
background:rgba(168,85,247,0.06);border:1px solid rgba(168,85,247,0.2);
border-radius:10px;padding:14px 20px;margin-bottom:24px;
text-align:center;
}
.callout p{font-size:0.72rem;color:#c084fc;line-height:1.5}
.callout strong{color:#e2e8f0}
/* ── Sources ── */
.sources{
border-top:1px solid rgba(255,255,255,0.06);
padding:16px 32px;
}
.sources h4{font-size:0.68rem;color:#64748b;margin-bottom:8px;text-transform:uppercase;letter-spacing:0.06em}
.sources a{
display:inline-block;font-size:0.7rem;color:#3b82f6;
text-decoration:none;margin-right:16px;margin-bottom:4px;
transition:color 0.2s;
}
.sources a:hover{color:#60a5fa;text-decoration:underline}
@media(max-width:640px){
.body{padding:18px 16px 22px}
.stack-grid{grid-template-columns:repeat(2,1fr)}
.when-grid{grid-template-columns:1fr}
.chain{flex-direction:column}
.chain-step{border-right:none;border-bottom:1px solid rgba(255,255,255,0.05)}
.chain-step:last-child{border-bottom:none}
.chain-step::after{content:'\2193';right:auto;bottom:-8px;top:auto;left:50%;transform:translateX(-50%)}
.card-header{padding:20px 16px 16px}
.sources{padding:14px 16px}
}
</style>
</head>
<body>
<div class="card">
<!-- Header -->
<div class="card-header">
<h1>T3 Stack Architecture</h1>
<p>End-to-end type safety — Next.js App Router, tRPC, Prisma/Drizzle, NextAuth, and type-safe env vars.</p>
<div class="badge-row">
<span class="badge">Next.js</span>
<span class="badge">tRPC</span>
<span class="badge">Prisma</span>
<span class="badge">TypeScript</span>
<span class="badge hard">Hard</span>
</div>
</div>
<div class="body">
<!-- Type Safety Chain -->
<div class="section-title">Type Safety Chain</div>
<div class="chain">
<div class="chain-step">
<div class="name">DB Schema</div>
<div class="desc">Prisma / Drizzle models</div>
</div>
<div class="chain-step">
<div class="name">Prisma Client</div>
<div class="desc">Generated typed queries</div>
</div>
<div class="chain-step">
<div class="name">tRPC Router</div>
<div class="desc">Zod input, inferred output</div>
</div>
<div class="chain-step">
<div class="name">tRPC Client</div>
<div class="desc">Fully typed hooks</div>
</div>
<div class="chain-step">
<div class="name">React</div>
<div class="desc">Autocomplete everywhere</div>
</div>
</div>
<div class="callout">
<p><strong>No codegen between tRPC and frontend.</strong> The TypeScript compiler infers the entire chain. Change a DB column and your IDE flags every affected component.</p>
</div>
<!-- Stack -->
<div class="section-title">The Stack</div>
<div class="stack-grid">
<div class="stack-item">
<div class="tech">Next.js</div>
<div class="role">SSR, App Router, RSC</div>
</div>
<div class="stack-item">
<div class="tech">tRPC</div>
<div class="role">Typesafe API layer</div>
</div>
<div class="stack-item">
<div class="tech">Prisma</div>
<div class="role">Type-safe ORM</div>
</div>
<div class="stack-item">
<div class="tech">NextAuth</div>
<div class="role">Authentication</div>
</div>
<div class="stack-item">
<div class="tech">Zod</div>
<div class="role">Runtime validation</div>
</div>
<div class="stack-item">
<div class="tech">@t3-oss/env</div>
<div class="role">Env var validation</div>
</div>
</div>
<!-- Folder tree -->
<div class="section-title">Project Structure</div>
<div class="tree"><span class="folder">prisma/</span>
└── <span class="file">schema.prisma</span> <span class="comment">(Database models)</span>
<span class="folder">src/</span>
├── <span class="file">env.js</span> <span class="comment">(Type-safe env validation)</span>
├── <span class="folder">server/</span>
│ ├── <span class="file">db.ts</span> <span class="comment">(Prisma client singleton)</span>
│ ├── <span class="file">auth.ts</span> <span class="comment">(NextAuth config)</span>
│ └── <span class="folder">api/</span>
│ ├── <span class="file">root.ts</span> <span class="comment">(Merge all routers)</span>
│ ├── <span class="file">trpc.ts</span> <span class="comment">(Context & procedures)</span>
│ └── <span class="folder">routers/</span>
│ ├── <span class="file">post.ts</span>
│ └── <span class="file">user.ts</span>
├── <span class="folder">trpc/</span>
│ ├── <span class="file">react.tsx</span> <span class="comment">(Client hooks)</span>
│ └── <span class="file">server.ts</span> <span class="comment">(Server caller)</span>
└── <span class="folder">app/</span>
├── <span class="file">layout.tsx</span>
├── <span class="file">page.tsx</span>
└── <span class="folder">api/trpc/[trpc]/</span>
└── <span class="file">route.ts</span>
<span class="file">next.config.ts</span></div>
<!-- CLI -->
<div class="section-title">CLI Setup</div>
<div class="cli">
<span class="prompt">$</span>
<span class="cmd">npm create t3-app@latest</span>
</div>
<!-- When to Use -->
<div class="section-title">When to Use</div>
<div class="when-grid">
<div class="when-item">
<div class="label">Full-stack app with complex data</div>
<div class="val yes">Ideal — prevents entire bug classes</div>
</div>
<div class="when-item">
<div class="label">Solo dev / small team</div>
<div class="val yes">Great DX — autocomplete everywhere</div>
</div>
<div class="when-item">
<div class="label">Multi-client API (mobile, 3rd party)</div>
<div class="val no">Not ideal — tRPC is TS-only</div>
</div>
<div class="when-item">
<div class="label">Rapid prototyping</div>
<div class="val yes">Excellent — fast scaffolding</div>
</div>
</div>
</div>
<!-- Sources -->
<div class="sources">
<h4>Sources</h4>
<a href="https://create.t3.gg/" target="_blank" rel="noopener">create.t3.gg</a>
<a href="https://github.com/t3-oss/create-t3-app" target="_blank" rel="noopener">t3-oss/create-t3-app</a>
<a href="https://trpc.io/docs" target="_blank" rel="noopener">tRPC Docs</a>
<a href="https://www.prisma.io/docs" target="_blank" rel="noopener">Prisma Docs</a>
</div>
</div>
</body>
</html>