Files
tested/docs/03-architecture.md
joshuaandClaude Opus 5.5 54ae9c49a1 Tested: static Astro site answering hard Bible questions
Built from the kit specs (CLAUDE.md, docs/01-05): question index import,
status seals, connections graph, Pagefind search, Scripture margin,
and 157 evidence articles (drafts unless published) with sources and
verified "Learn more" links. Includes link, owner-notes, a11y and
publish scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:46:19 +02:00

4.7 KiB
Raw Permalink Blame History

03 — Architecture

Stack

  • Astro (latest stable) with TypeScript in strict mode. Static output.
  • MDX for full articles, via Astro content collections.
  • Plain CSS with custom properties for tokens. One global stylesheet for tokens and base type; component-scoped styles for the rest. No Tailwind, so the design stays its own.
  • Pagefind for static full-text search (runs after astro build).
  • D3 (only d3-force, d3-selection, d3-zoom) for the Connections graph, loaded as a client island on that page only.
  • sharp for generating PNG icons and the OG image from the SVGs in brand/.
  • Fonts: Literata and IBM Plex Sans, self-hosted via Fontsource (no Google Fonts requests at runtime).
  • Hosting: any static host (Cloudflare Pages, Netlify or Vercel).

Why Astro: the site is mostly reading content that should load fast on phones and rank well in search. Astro ships zero JS by default and adds interactivity only where needed (search, arcs, graph).

Folder structure

/
├── CLAUDE.md
├── docs/                     # These specs
├── brand/                    # Source SVGs
├── content/source/           # question-index.md exported from the owner's doc
├── scripts/
│   ├── import-index.mjs      # Markdown index → src/data/questions.json
│   └── build-icons.mjs       # brand SVGs → public/ icons and OG image
├── public/                   # Generated icons, robots.txt
└── src/
    ├── site.config.ts        # Site name, tagline, URL, nav
    ├── data/questions.json   # Generated. Do not hand-edit.
    ├── content/
    │   └── articles/         # One MDX file per full article, e.g. 3-4.mdx
    ├── lib/
    │   ├── questions.ts      # Typed loaders, lookups, related resolution
    │   └── graph.ts          # Builds nodes/edges for arcs and graph
    ├── components/
    ├── layouts/
    ├── styles/
    │   ├── tokens.css
    │   └── base.css
    └── pages/
        ├── index.astro
        ├── questions/index.astro
        ├── questions/[slug].astro
        ├── groups/[group].astro
        ├── connections.astro
        ├── about.astro       # Explains the status system and method
        └── 404.astro

Data model

src/data/questions.json is generated by npm run import.

type Status = "green" | "yellow" | "red";
type Domain =
  | "Scripture" | "History" | "Archaeology" | "Science"
  | "Philosophy" | "Textual Criticism" | "Context" | "Theology";

interface Group {
  id: string;          // "3", "3B", "12"
  slug: string;        // "the-canon", "missing-books"
  title: string;       // "The Canon"
  intro?: string;      // Paragraph under the heading, if any
  order: number;
}

interface Question {
  id: string;          // "3.4", "3B.10"
  slug: string;        // "3-4-why-isnt-enoch-in-the-bible"
  groupId: string;     // "3"
  title: string;       // Question text, or the book name for 3B rows
  status: Status;
  domains: Domain[];
  answer: string;      // Short answer (markdown allowed: *italics*)
  related: string[];   // Question ids, e.g. ["3.5", "3.11", "3B.10"]
  bookGroup?: string;  // 3B only: "Apocrypha", "Pseudepigrapha", ...
  authorNotes?: string;// Private. Never rendered outside dev mode.
  hasArticle: boolean; // True when src/content/articles/<id>.mdx exists
}

Related links in the source look like "3B.1–3B.9 Apocrypha books" (ranges) or "3B Missing books" (a whole group). The importer expands ranges into each id and maps a bare group id to the group page.

Routes

Route Content
/ Arc diagram, headline, search, status counts, group list
/questions/ Full filterable list (?status=green&domain=History)
/questions/[slug]/ Question page: short answer, article if present, marginalia
/groups/[slug]/ One group's questions with its intro
/connections/ Force-directed graph plus accessible list fallback
/about/ The status system, the domains, the method, who's behind it

Question pages without a full article still render: title, seal, domains, short answer, related questions, and a quiet "Full article coming" line.

Dev-only owner notes

When import.meta.env.DEV is true, question pages show authorNotes in a clearly styled "Owner notes (dev only)" box. Production builds must not include the notes in HTML, JSON, or the search index. Add a build-time check that fails if any note text appears in dist/.

SEO

  • Per-page <title>, meta description (the short answer), canonical URL, Open Graph tags.
  • JSON-LD FAQPage on question pages (question plus short answer).
  • sitemap.xml via @astrojs/sitemap, plus robots.txt.