<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nx Monorepo Architecture</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0a0a0f;
color: #e2e8f0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
min-height: 100vh;
display: flex;
justify-content: center;
padding: 2rem 1rem;
}
.card {
max-width: 780px;
width: 100%;
background: linear-gradient(135deg, rgba(34,211,238,0.04), rgba(59,130,246,0.04));
border: 1px solid rgba(255,255,255,0.08);
border-radius: 16px;
padding: 2rem;
overflow: hidden;
}
.badge {
display: inline-block;
background: rgba(34,211,238,0.12);
color: #22d3ee;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0.25rem 0.75rem;
border-radius: 999px;
margin-bottom: 0.75rem;
}
h1 {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.25rem;
background: linear-gradient(135deg, #22d3ee, #3b82f6);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
color: #94a3b8;
font-size: 0.85rem;
margin-bottom: 1.5rem;
line-height: 1.5;
}
.section-title {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #22d3ee;
margin-bottom: 0.75rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid rgba(34,211,238,0.15);
}
.tree {
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
font-size: 0.8rem;
line-height: 1.7;
background: rgba(0,0,0,0.35);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 10px;
padding: 1.25rem;
margin-bottom: 1.5rem;
overflow-x: auto;
}
.tree .file { color: #e2e8f0; }
.tree .comment { color: #64748b; }
.tree .dir { color: #22d3ee; }
.tree .accent { color: #3b82f6; }
.tree .highlight { color: #f59e0b; }
.split-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1.5rem;
}
@media (max-width: 500px) {
.split-section { grid-template-columns: 1fr; }
}
.split-card {
background: rgba(0,0,0,0.25);
border: 1px solid rgba(255,255,255,0.06);
border-radius: 10px;
padding: 1rem;
}
.split-card h3 {
font-size: 0.85rem;
margin-bottom: 0.25rem;
}
.split-card .pct {
font-size: 1.8rem;
font-weight: 800;
margin-bottom: 0.25rem;
}
.split-card .pct.apps { color: #3b82f6; }
.split-card .pct.libs { color: #22d3ee; }
.split-card p {
font-size: 0.75rem;
color: #94a3b8;
line-height: 1.4;
}
.graph-section {
background: rgba(59,130,246,0.06);
border: 1px solid rgba(59,130,246,0.15);
border-radius: 10px;
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
}
.graph-section h3 {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #3b82f6;
margin-bottom: 0.75rem;
}
.graph-nodes {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.g-node {
font-family: 'SF Mono', monospace;
font-size: 0.72rem;
padding: 0.35rem 0.7rem;
border-radius: 8px;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
.g-node.app {
background: rgba(59,130,246,0.15);
border: 1px solid rgba(59,130,246,0.3);
color: #93c5fd;
}
.g-node.feat {
background: rgba(34,211,238,0.12);
border: 1px solid rgba(34,211,238,0.25);
color: #22d3ee;
}
.g-node.data {
background: rgba(168,85,247,0.12);
border: 1px solid rgba(168,85,247,0.25);
color: #c084fc;
}
.g-node.ui {
background: rgba(245,158,11,0.12);
border: 1px solid rgba(245,158,11,0.25);
color: #fbbf24;
}
.g-node.util {
background: rgba(34,197,94,0.12);
border: 1px solid rgba(34,197,94,0.25);
color: #4ade80;
}
.g-arrow {
color: #475569;
font-size: 0.9rem;
}
.affected-box {
margin-top: 0.75rem;
font-family: 'SF Mono', monospace;
font-size: 0.75rem;
color: #94a3b8;
background: rgba(0,0,0,0.3);
padding: 0.6rem 0.8rem;
border-radius: 6px;
}
.affected-box code { color: #22d3ee; }
.source {
display: flex;
gap: 1rem;
flex-wrap: wrap;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid rgba(255,255,255,0.06);
}
.source a {
color: #64748b;
font-size: 0.75rem;
text-decoration: none;
transition: color 0.2s;
}
.source a:hover { color: #22d3ee; }
</style>
</head>
<body>
<div class="card">
<div class="badge">Architecture</div>
<h1>Nx Monorepo</h1>
<p class="subtitle">Project graph, affected commands, computation caching, and the 80/20 apps-to-libs split.</p>
<div class="section-title">Project Structure</div>
<div class="tree">
<span class="file">nx.json</span> <span class="comment">(Global config)</span>
<span class="file">package.json</span>
<span class="dir">apps/</span> <span class="highlight">~20% code — thin shells</span>
<span class="accent"> ├── web/</span>
<span class="file"> │ └── project.json</span> <span class="comment">(Targets & executors)</span>
<span class="accent"> └── mobile/</span>
<span class="file"> └── project.json</span>
<span class="dir">libs/</span> <span class="highlight">~80% code — domain logic</span>
<span class="accent"> ├── feature-auth/</span> <span class="comment">(Feature library)</span>
<span class="accent"> ├── feature-dashboard/</span>
<span class="accent"> ├── data-access-api/</span> <span class="comment">(API clients)</span>
<span class="accent"> ├── ui-shared/</span> <span class="comment">(UI components)</span>
<span class="accent"> └── util-helpers/</span> <span class="comment">(Pure utilities)</span>
<span class="dir">tools/</span> <span class="comment">(Generators, scripts)</span>
</div>
<div class="section-title">The 80/20 Pattern</div>
<div class="split-section">
<div class="split-card">
<div class="pct apps">20%</div>
<h3>Apps</h3>
<p>Thin shells: routing, layout, configuration. Compose features from libs. Minimal business logic.</p>
</div>
<div class="split-card">
<div class="pct libs">80%</div>
<h3>Libraries</h3>
<p>Domain logic, feature modules, shared UI, data access, and utilities. Clear boundaries and public APIs.</p>
</div>
</div>
<div class="section-title">Project Graph (nx graph)</div>
<div class="graph-section">
<h3>Dependency Flow</h3>
<div class="graph-nodes">
<span class="g-node app">web</span>
<span class="g-arrow">→</span>
<span class="g-node feat">feature-auth</span>
<span class="g-arrow">→</span>
<span class="g-node data">data-access-api</span>
<span class="g-arrow">→</span>
<span class="g-node util">util-helpers</span>
</div>
<br />
<div class="graph-nodes">
<span class="g-node app">web</span>
<span class="g-arrow">→</span>
<span class="g-node feat">feature-dashboard</span>
<span class="g-arrow">→</span>
<span class="g-node ui">ui-shared</span>
</div>
<div class="affected-box">
<code>nx affected -t test</code> — only runs tests for changed projects + their dependents
</div>
</div>
<div class="source">
<a href="https://nx.dev/docs" target="_blank" rel="noopener">Source: Nx Docs</a>
<a href="https://nx.dev/docs/concepts/decisions/folder-structure" target="_blank" rel="noopener">Folder Structure</a>
<a href="https://nx.dev/features/explore-graph" target="_blank" rel="noopener">Project Graph</a>
</div>
</div>
</body>
</html>