Skip to content

Astro site rules

Edward Baitsewe edited this page Nov 10, 2026 · 5 revisions

Astro site rules

Status: draft. Written before milestone 3 and settled while Honey Badger moves to Slim Killer. Rules marked to settle are open questions.

The rules an Astro site follows from its first day, so Slim Killer can be added later without rework. The usual order of work is:

  1. Build the client's website in Astro, following these rules.
  2. The client approves the website.
  3. Add Slim Killer: the control panel, publishing and the host setup.

Step 3 should be routine. Every rule below exists because breaking it would make step 3 a rebuild. Rule numbers here are separate from the twelve build rules.

Layout

1. One repository per client, in the client-site layout from the start.

client-repo/
  site/                Astro project (package.json, astro.config.mjs, src/)
  content/             everything the client edits
    <collection>/      one folder per collection, one Markdown file per entry
    images/            the client's photos (compressed when the site is built)
    graphics/          maps, flags, logos, diagrams, SVGs (never compressed)
    settings/          site-wide settings (see rule 5)
    <page>/index.md    a page that exists once: home, about, not-found
    <collection>-list/ a list page's own text: articles-list
  .github/workflows/   build-site.yml, from Slim Killer's template

cp/ (the control panel) is added in step 3. Why: moving content and changing paths after approval is exactly the rework these rules prevent.

2. Everything the client might change lives in content/, never in .astro files. The test: "could the client want to change this sentence or photo without a developer?" If yes, it is content. Headings, intros, prices, team members, opening hours, the footer text: content. Why: the control panel can only edit what is in content/.

3. Three kinds of image, three places.

  • Photos live in content/images/. Astro compresses them to WebP when it builds the site.
  • Graphics live in content/graphics/: maps, flags, logos, diagrams and every SVG. They are copied into the site exactly as they are. Why: they are already optimised; compressing them again makes them worse and hardly smaller.
  • The theme's own images (its logo, icons, textures) live in site/src/assets/.

An image field holds a file name ("oryx.jpg"). The site finds the file in content/images/ with import.meta.glob at build time. The control panel decides photo or graphic when a file is uploaded: an SVG is always a graphic, and a PNG or JPEG that WebP makes less than about 25% smaller is a graphic too. The client never chooses.

Collections and schemas

4. Every kind of content is a collection, with its own schema, in site/src/content.config.ts, loaded from ../content/<collection>. One schema per kind of page. A page that exists once (About, FAQ, Privacy) is its own collection with one entry, content/<page>/index.md, never one schema shared by different pages. Why: spike S3 found that a schema shared by six pages gives every page every other page's fields in the editor.

5. Site-wide settings are content too. The business names (businessName, shortName), phone numbers, email address, physical address, social links, menus and footer text live in one settings collection (content/settings/index.md), not in constants inside components.

6. Every field carries its editor hint. .meta({ title }) with the label in the client's words; input: 'image' on every image field; input: 'textarea' or 'markdown' on long text; description wherever a client might hesitate. Field names follow Field names and building blocks: one name for one idea, groups instead of prefixes (cta.heading, not ctaHeading), and every image an image() of { src, alt }. Why: the control panel builds its forms from these (spike S3), and nothing else describes the fields. See the Glossary for "editor hint".

7. Required only when the page breaks without it. Everything else is .optional() or has a .default(). Why: every required field is one the client must fill in before she can save.

Markdown

8. Bodies are plain Markdown. CommonMark plus tables. No MDX, no HTML blocks, no components inside a body. Layout variety (sections with an image beside the text, quotes, galleries) comes from front matter fields that the template renders. Why: the editor can keep plain Markdown unchanged (spike S4), and cannot edit components inside text. Obsidian-style callouts wait until the editor supports them.

9. File names are web addresses. Lowercase, hyphens, no spaces: fish-river-canyon.md becomes /articles/fish-river-canyon/. Files starting with _ are not content (templates, notes for the client) and are never published.

10. One front matter style. UTF-8 without a byte-order mark, Unix line endings, double quotes, | for text over several lines. Why: the editor keeps any style it finds (spike S4), but files made in one style keep every diff small and readable.

Build

11. npm ci && npm run build in site/ makes the complete site in site/dist/. Static output only: no server code, no secrets, no build-time API calls that can fail, and no host-specific files such as .htaccess in dist/. Why: GitHub Actions builds the site with nothing but the repository (spike S5), and the host serves only what is fetched.

12. Versions are pinned. Node in package.json engines and .nvmrc; dependencies by package-lock.json; Astro within the versions Slim Killer supports.

13. The site works at its domain root, with site set in astro.config.mjs to the real address.

14. It publishes the Slim Killer way from day one. The workflow from Slim Killer's template builds the site and publishes it to the dist branch, and the host fetches it (see Glossary: build, fetch, release). The control panel arrives later; the publishing path does not change when it does.

Checklist before step 3

  • The layout matches rule 1.
  • No client-editable text or photo is inside site/src/ (rule 2).
  • No SVG, GIF or ICO in content/images/; graphics are in content/graphics/ (rule 3).
  • Every page type has its own collection and schema (rule 4).
  • Settings are in content/settings/ (rule 5).
  • Every field has a title, and every image and long-text field an input (rule 6).
  • Field names follow Field names and building blocks, and repeated shapes use its building blocks (rule 6).
  • No MDX or HTML in bodies (rule 8).
  • npm ci && npm run build works on a clean checkout (rule 11).

Planned: hammer site:check, which checks these rules automatically, the way hammer doctor checks a host.

Clone this wiki locally