-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
The site is a Next.js App Router static export: every page is pre-rendered to HTML at build time and served as plain files. There is no server runtime, no database, no API, and no live podcast feed. All content is embedded in source-controlled TypeScript modules.
This architecture is a direct consequence of the Project Constitution, whose first principle is Static Output First.
| Concern | Choice | Notes |
|---|---|---|
| Framework | Next.js App Router |
output: "export", trailingSlash: true, images: { unoptimized: true }
|
| Language | TypeScript (strict) | Path alias @/* to ./src/*
|
| UI | React | Server components by default; client components only where interactivity is needed |
| Styling | Plain CSS with custom properties | Design tokens in src/styles/globals.css; no CSS framework |
| Content | Typed TS modules |
src/content/podcast.ts, episodes.ts, faq.ts
|
| Linting | ESLint 9 flat config | Extends next/core-web-vitals and next/typescript
|
| Testing | Playwright + @axe-core/playwright
|
Content, accessibility, and responsive suites |
The site has exactly four primary routes, each pre-rendered to its own
index.html.
flowchart LR
L[/ Landing\nhero + 1 featured episode/] --> E[/episodes\n20-episode catalog/]
L --> A[/about\nshow premise + host/]
L --> F[/faq\n7 Q&A items/]
E --> L
A --> L
F --> L
| Route | File | Content |
|---|---|---|
/ |
app/page.tsx |
Hero identity and the single featured episode |
/episodes |
app/episodes/page.tsx |
All 20 mocked episodes, newest first |
/about |
app/about/page.tsx |
Premise, host, audience, and tone |
/faq |
app/faq/page.tsx |
Seven question-and-answer items |
app/
├── layout.tsx # html shell, skip link, header/footer, metadata
├── page.tsx # landing page
├── episodes/page.tsx
├── about/page.tsx
└── faq/page.tsx
src/
├── components/
│ ├── SiteHeader.tsx # nav with aria-current
│ ├── SiteFooter.tsx
│ ├── MockEpisodeAction.tsx # in-page mock action (client component)
│ ├── EpisodeMeta.tsx # date/runtime/guest/category display
│ ├── FeaturedEpisode.tsx
│ ├── EpisodeCard.tsx
│ ├── EpisodeCatalog.tsx
│ └── FaqList.tsx
├── content/
│ ├── podcast.ts # PodcastProfile (name, premise, host, about sections)
│ ├── episodes.ts # exactly 20 Episode records + helpers
│ └── faq.ts # FaqItem records
└── styles/
└── globals.css # tokens, base, responsive design system
public/assets/
└── signal-static-artwork.svg
tests/
├── build/content-contract.spec.ts # data invariants
├── accessibility/primary-pages.spec.ts # axe scans
└── e2e/responsive-navigation.spec.ts # viewports + navigation
Content is typed and validated at the source. The key entities mirror the specification's data model.
- PodcastProfile — name, tagline, premise, audience, tone, host name, and the about-page sections.
-
Episode — id, slug, number, title, summary, description, ISO publish date,
duration, category, optional guest,
featuredflag, and the mock action label and message. - FaqItem — id, question, answer, optional category.
episodes.ts exposes helpers that enforce the invariants from the spec:
export function getFeaturedEpisode(): Episode {
const featured = episodes.filter((e) => e.featured);
// throws unless exactly one episode is featured
...
}This is where intent becomes code: the spec requires exactly one featured episode within the catalog of twenty, and the helper makes any violation a hard failure rather than a silent bug.
-
Operationally trivial — host the
out/directory on any static host or CDN. No servers to patch, scale, or page someone about at 3 a.m. - Deterministic and cheap — no request-time rendering, no runtime data fetches, minimal bundle.
- Secure by construction — no server code path, no secrets in the client bundle, no live external dependency to fail.
- Accessible and fast — pre-rendered semantic HTML with a small CSS layer.
Most components are React server components rendered to static HTML. Only the pieces that need browser interactivity are client components:
-
SiteHeaderreads the current path to setaria-currenton the active link. -
MockEpisodeActiontoggles an in-page status notice when a mock episode action is activated — no navigation, no dead link, no real feed.
Continue to Testing and Validation to see how these guarantees are enforced automatically.
spec-podcast-site — a Spec-Driven Development lab built with Spec Kit. Built for Signal & Static, a fictional podcast.
Workflow
The Project
External