# Content Site (Blog / Docs) — product specification This is the contract the project is built against. It is written for the agent that scaffolds the repository, but it doubles as the human-readable brief: every requirement below is meant to be implementable and verifiable. Values in angle brackets (``, ``, ``) come from the project-creation dialog and are recorded in the project record; replace them as you read. `` is blog, documentation or marketing pages; `` is the authoring approach (Markdown/MDX, Contentful, Sanity or Notion). ## 1. Goal A content site a real team could launch with: write structured entries in ``, have them rendered as `` pages with navigation, tags, authors and search, and publish a fast, crawlable, shareable site. Content is stored against a **typed content model** and validated at build time, so a malformed entry fails the build instead of shipping a broken page. It must be **runnable end-to-end on day one** (even with seeded sample content), not a set of screens wired together later. Non-goals (do not build unless the must-have list says otherwise): multi-tenant CMS, comments, subscriptions/paywalls, live collaborative editing, multi-language content. ## 2. Roles | Role | Can do | |------|--------| | Reader | Anonymous. Browse the site, read entries, filter by tag/author, search, subscribe to the feed. | | Author | Everything a reader can, plus create and edit their own entries (draft → in review). | | Editor | Everything an author can, plus edit and review any entry, manage taxonomy and schedules, approve and publish. | | Site admin | Everything an editor can, plus site configuration, navigation, roles, feeds and redirects. | Reading is anonymous and never gated. Authoring happens in `` (for `Markdown / MDX`, in the repository itself; for hosted CMSs, in that CMS UI) and only the publishing side is guarded by roles. Provide a seeded author and editor for local development when the authoring approach has a local backend. ## 3. Information architecture (routes) Public site: - `/` — home: hero/intro, latest or featured entries, section links. - `/:section` — section index for one collection (e.g. `/blog`, `/docs`), with pagination. - `/:section/:slug` — one entry: title, metadata (author, date, reading time), body, table of contents, related entries. - `/tags` and `/tags/:tag` — tag archive; `/authors/:author` — author archive; both paginated. - `/search` — search over titles, tags and body, with a no-results state. - `/404` — not-found page (and a real 404 status for unknown routes). - Authoring preview where the chosen CMS provides one (draft preview URL for an unpublished entry); otherwise a local preview route. - Generated artifacts: `/sitemap.xml`, `/rss.xml` or `/atom.xml`, `/robots.txt`, and Open Graph images (`/og/:section/:slug.png` or static equivalents). When `` is `Documentation`, the section index is the docs sidebar: entries are ordered and grouped, and one document shows the same sidebar with the current page highlighted. Admin (guarded by the editor/admin roles, only where the authoring approach has a local backend; hosted CMSs keep this in the CMS UI): - `/admin` — dashboard: draft/review queue and scheduled entries. - `/admin/entries` — list, create, edit, preview and publish/unpublish entries. - `/admin/taxonomy` — manage tags and authors. - `/admin/site` — site config, navigation and redirects. ## 4. Data model Minimum viable entities (add fields the requirements imply; keep them typed and validated): - **ContentEntry** — slug, title, body (Markdown/MDX or a CMS reference), excerpt, type (reference to a ContentType), tags[], author (reference), status (`draft` | `in_review` | `scheduled` | `published` | `archived`), publishedAt, updatedAt, seo { title, description, canonicalUrl?, image?, noindex }. - **ContentType** — id/key, title, and `fields[]` (name, label, type, required, default) that define the front-matter a `` entry must carry; the schema entries are validated against. - **Author** — id, slug, name, bio, avatar, links[]. - **Tag** — id, slug, name, description, parentId? (categories are tags with children). - **SiteConfig** — site name, base URL, locale, defaultSeo, and `navigation[]` (label, href, order, children?) used for the header and the docs sidebar. - **Relationship** — a typed edge between entries: `parent`/`child` for the docs sidebar tree, `series` for an ordered series, `related` for hand-picked links. The content model is the source of truth: front-matter missing a required field, an unknown field, or a wrong type is a **build error**, with the offending file and field named. Document the model so an author can add an entry without reading the renderer. ## 5. Key flows 1. **Author → publish.** An author adds a Markdown/MDX file (or creates an entry in ``) with the required front-matter. A malformed entry fails validation with a clear message and the build stops; a valid `draft` entry renders in preview only. The author marks it ready, an editor reviews and publishes it, and it appears on the section index, the tag archive and the feed with `publishedAt` set. 2. **Read.** A reader lands on the home page, opens a section, reads an entry; the table of contents tracks the scroll position, related entries are listed below, and the document's canonical URL and Open Graph metadata are present in the HTML head. 3. **Documentation navigation.** A reader in the docs section sees the ordered sidebar, moves between sibling and child documents, and breadcrumbs show the path from the section root. 4. **Search.** A reader types a query on `/search` and gets ranked matches over current published entries (client-side over an index for small sites, or a hosted search service); an empty query shows recent entries and a no-results query shows a clear empty state. 5. **Schedule.** An editor sets a future `publishedAt` on an entry; it is `scheduled`, excluded from production builds and the feed, and a rebuild at/after that time publishes it. 6. **Taxonomy and feeds.** The editor renames a tag; the tag archive, the entry pages and the sitemap all reflect the change, and `sitemap.xml` and the feed stay valid. ## 6. Functional requirements - **Authoring:** Markdown/MDX with front-matter, or entries authored in ``; code blocks render with syntax highlighting; images and links resolve; the chosen CMS's preview is wired in where it exists. - **Typed validation:** entry front-matter is validated against its `ContentType` at build time; a required field missing, an unknown field or a wrong type fails the build with the file, field and expectation in the message. - **Rendering:** table of contents generated from headings; reading time computed; breadcrumbs in the docs layout; related content from series/related edges (fallback: shared tags). - **Indexes:** section, tag and author archives are paginated with a stable page size and their own metadata; pagination links are crawlable. - **Search:** over titles, tags and body; client-side or hosted; no query sent to a third party without the configuration saying so. - **Feeds & discovery:** `rss.xml` (or `atom.xml`) built from published entries newest-first; `sitemap.xml` covering every published route; `robots.txt` with the sitemap reference and non-production hosts disallowed. - **Metadata:** every route has a unique title and description and an absolute canonical URL; Open Graph and Twitter card tags are emitted; JSON-LD (`Article`/`BlogPosting`, `BreadcrumbList`, `WebSite`) is present where it applies. - **Draft/scheduled exclusion:** `draft`, `in_review` and `scheduled` entries are excluded from production builds, the sitemap and the feed; they are visible only in preview/local mode. - **Seed data:** sample content (≥ 8 entries across ≥ 2 sections, ≥ 4 tags, ≥ 2 authors, one draft and one scheduled entry) so the site is presentable on first run. ## 7. Non-functional requirements This is the **SEO and performance** template, so these are hard requirements, not polish. - **SEO:** semantic HTML (one `h1` per page, ordered headings), unique title/description per route, absolute canonical URLs, a valid sitemap and feed, correct `hreflang`/locale if used, and no indexable duplicate routes. - **Performance:** static output where possible (pre-render every published route at build time); a Lighthouse budget of LCP < 2.5 s and CLS < 0.1 on an article page; images optimised with explicit width/height and lazy loading below the fold; no client JavaScript on non-interactive pages (article, archives, 404); route-level code splitting only where interactivity needs it. - **Accessibility (AA):** correct landmarks (`header`/`nav`/`main`/`footer`), heading order, alt text on every meaningful image, labelled links and controls, visible focus, contrast at least AA, keyboard-operable search and navigation. - **Resilience:** explicit loading, empty and error states; a missing entry renders the 404 page with a real 404 status, not an empty shell. - **Observability:** build-time report of validation errors and skipped drafts; structured logs for publish/build events; a health or build-status check for the deployed site. - **Security:** no secrets in the repository; CMS tokens stay server-side and are documented in `.env.example`; validate and cap CMS and search inputs. ## 8. Acceptance criteria (definition of done) - [ ] Install, dev server, lint, typecheck, tests and production build all pass. - [ ] A malformed front-matter entry (missing required field, unknown field or wrong type) **fails the build** with a message naming the file and field. - [ ] `sitemap.xml` and `rss.xml`/`atom.xml` are generated, cover the published routes and validate against their schemas. - [ ] Every route has a unique title, description and absolute canonical URL; Open Graph and JSON-LD are present. - [ ] A static article page ships no client JavaScript and meets the LCP/CLS budget. - [ ] Draft, in-review and scheduled entries never appear in the production build, sitemap or feed; preview still shows them. - [ ] Search returns ranked results for published entries and has an explicit no-results state. - [ ] Code highlighting, table of contents, reading time, related content and pagination work on seeded entries. - [ ] Docs navigation (sidebar order, breadcrumbs, active highlight) works when `` is documentation. - [ ] Seeded sample content makes the site presentable immediately; `.env.example` documents every secret. - [ ] README quickstart (install, run, build, env) is accurate; loading/empty/error states exist. - [ ] Accessibility pass: landmarks, heading order, alt text, contrast AA, keyboard-operable search. - [ ] CI runs install + lint + typecheck + tests + build. ## 9. Suggested build order Follow this order and finish (and verify) a layer before starting the next: 1. **Scaffold** the chosen stack, install dependencies, get the dev server and the empty shell running, and commit the skeleton. 2. **Content model + typed validation**: the `ContentType`/field schema, front-matter parsing and the build-time validation that fails on a bad entry. 3. **Layouts + navigation**: base layout, section index, entry page, breadcrumbs, the docs sidebar and taxonomy pages, rendered from seeded entries. 4. **SEO / feed / sitemap**: metadata per route, canonical URLs, JSON-LD, `sitemap.xml`, `rss.xml`/`atom.xml`, `robots.txt`, Open Graph images, and draft/scheduled exclusion. 5. **Search & related content**: the search index or hosted search plus related/series links. 6. **Authoring workflow**: `` wiring and preview, roles, draft → review → publish, scheduling. 7. **Quality / performance**: tests for validation and rendering, static-output check, Lighthouse budget, accessibility pass, CI, README and `.env.example`.