// Project Plan page — modules, sprints, principles, roadmap const Plan = ({ setPage }) => { const [tab, setTab] = React.useState('overview'); return (
{/* Sticky tab bar */}
{[ ['overview','Overview'], ['modules','Modules'], ['sprints','Sprints'], ['principles','Principles'], ['stack','Stack'], ].map(([id,l]) => ( ))}
{tab === 'overview' && } {tab === 'modules' && } {tab === 'sprints' && } {tab === 'principles' && } {tab === 'stack' && }
); }; const PlanHero = () => (
Internal · v0.4 LAST UPDATED MAY 13, 2026

SolidAtoms Build Plan

The full module breakdown, sprint roadmap, and engineering principles for the SolidAtoms website. Living document. Updates after every retro.

{[ ['Modules done', '13 of 14', '93% complete'], ['Sprints shipped', '13 of 14', '336 of 362 pts'], ['Active sprint', 'SP12', 'W44–W45 · Analytics'], ['Target launch', 'W45', 'Nov 2026'], ].map(([l,v,sub],i) => (
0 ? '1px solid var(--line)' : 'none', background:'var(--bg-2)'}}>
{l.toUpperCase()}
{v}
{sub.toUpperCase()}
))}
); // OVERVIEW const PlanOverview = () => (

How we'll actually build this.

The SolidAtoms site is split into fourteen modules, sequenced across fourteen two-week sprints. Each module is scoped, documented, and reviewed before any code is written.

We don't ship monoliths. Every feature is its own folder — components, data, and tests — with hard limits: no file over 300 lines, no component doing more than one job, and no copy-paste duplication of logic.

Progress is tracked sprint-by-sprint with velocity, burndown, and a retro after every closing Friday. New ideas don't derail the current sprint — they go into the backlog and get prioritised at the next planning session.

● THIS WEEK · SPRINT 12 · ANALYTICS STUDIO

Analytics — event pipeline, dashboards, cohorts, alerts

IN PROGRESS
    {['Cohort retention heatmap', 'Funnel drop-off labels', 'Alert routing to Slack', 'Scheduled PDF reports'].map(i => (
  • {Icon.dot()}{i}
  • ))}
DONE THIS SPRINT
    {['Event pipeline scaffolding', 'Realtime visitor stream', 'Overview dashboard + sparklines', 'Events catalog + tracking SDK'].map(i => (
  • {Icon.check()}{i}
  • ))}
{/* Side panel */}
); // MODULES const PlanModules = () => { const StatusBadge = ({ s }) => { const map = { 'in-progress': ['var(--accent)', '#0a0a0a', 'In progress'], 'planned': ['var(--line-2)', 'var(--ink-2)', 'Planned'], 'future': ['rgba(255,255,255,0.06)', 'var(--ink-3)', 'Future'], 'done': ['var(--ok)', '#0a0a0a', 'Done'], }; const [bg, c, label] = map[s] || map.planned; return {label}; }; return (

Modules

14 modules · 13 shipped, 1 active
{MODULES.map(m => (
{m.id}
{m.name}
{m.scope.map(s => {s})}
{m.priority} priority {Icon.arrow()}
))}
); }; // SPRINTS const PlanSprints = () => { return (

Sprints

2-week cadence · 362 total points · W18 → W45
{/* Gantt-ish timeline */}
{Array.from({length:28}, (_,i)=>(
W{18+i}
))}
{SPRINTS.map(sp => { const startW = parseInt(sp.weeks.split('–')[0].slice(1)); const endW = parseInt(sp.weeks.split('–')[1].slice(1)); const startCol = startW - 18 + 2; const span = endW - startW + 1; const colorMap = {done:'var(--ink-3)', active:'var(--accent)', next:'#7c9bff', planned:'var(--line-2)'}; return (
{sp.id}
{sp.name}
{sp.points}P
); })}
{SPRINTS.map(sp => (
{sp.id} · {sp.weeks}
{sp.status}

{sp.name}

    {sp.items.map(it => (
  • {it}
  • ))}
{sp.points} STORY POINTS {sp.items.length} STORIES
))}
); }; // PRINCIPLES const PlanPrinciples = () => (

Engineering principles

5 rules we don't bend
{PRINCIPLES.map(p => (
{p.n}

{p.t}

{p.d}

))}
FOLDER CONVENTION
{`src/
  modules/
    marketing/        ← M02 marketing site
      components/     ← max 300 lines each
      data/
      hooks/
      index.tsx
    portfolio/        ← M03
    team/             ← M04
    blog/             ← M05
    tools/            ← M06
    cms/              ← M07
    payments/         ← M08
    portal/           ← M09
  shared/             ← cross-module primitives
    components/
    tokens/
    utils/
  app/                ← routing only`}
); // STACK const PlanStack = () => { const stack = [ {layer:'Frontend', items:[['Next.js 15','Framework'],['React 19','UI'],['TypeScript','Type safety'],['Tailwind v4','Styling'],['Framer Motion','Animation']]}, {layer:'Backend', items:[['Next.js Route Handlers','API'],['tRPC','Type-safe RPC'],['Drizzle ORM','Database'],['Postgres (Neon)','DB hosting'],['Upstash Redis','Cache + queue']]}, {layer:'CMS', items:[['Payload CMS 3','Headless CMS'],['MDX','Long-form posts'],['UploadThing','Media'],['Resend','Email']]}, {layer:'AI', items:[['Vercel AI SDK','LLM client'],['OpenAI / Anthropic','Models'],['Pinecone','Vector DB']]}, {layer:'Payments', items:[['Stripe','Payments + Connect'],['Stripe Tax','Tax handling'],['Webhooks via Inngest','Reliable events']]}, {layer:'Infra', items:[['Vercel','Hosting'],['Sentry','Errors'],['PostHog','Product analytics'],['GitHub Actions','CI/CD'],['Linear','Project management']]}, ]; return (

The stack

Boring where it matters, sharp where it counts
{stack.map(s => (
● {s.layer.toUpperCase()}
{s.items.map(([n,r],i) => (
0 ? '1px solid var(--line)' : 'none'}}> {n} {r}
))}
))}
); }; window.Plan = Plan;