|
| 1 | +// SPDX-License-Identifier: CC-BY-SA-4.0 |
| 2 | +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 3 | += Authoring a site with casket |
| 4 | +:toc: left |
| 5 | +:toclevels: 2 |
| 6 | + |
| 7 | +How to write content, build it, and understand the output. For customising the |
| 8 | +look see link:templates-and-theming.adoc[Templates & theming]; for the metadata |
| 9 | +layer see link:the-gnosis-layer.adoc[The Gnosis layer]. |
| 10 | + |
| 11 | +== Source layout |
| 12 | + |
| 13 | +A site is a directory of Markdown plus optional supporting folders: |
| 14 | + |
| 15 | +[source] |
| 16 | +---- |
| 17 | +my-site/ |
| 18 | +├── content/ # your pages (passed as the build input dir) |
| 19 | +│ ├── index.md |
| 20 | +│ └── posts/ |
| 21 | +│ └── 2026-06-23-hello.md |
| 22 | +├── templates/ # optional: layouts + partials (theming guide) |
| 23 | +│ ├── default.html |
| 24 | +│ └── partials/ |
| 25 | +├── assets/ # optional: copied to /assets in the output |
| 26 | +├── public/ # optional: copied verbatim to the OUTPUT ROOT |
| 27 | +└── site.conf # optional: site-wide {{site.*}} values |
| 28 | +---- |
| 29 | + |
| 30 | +Only `content/` (the input directory) is required; everything else has sane |
| 31 | +defaults — including a built-in accessible template. |
| 32 | + |
| 33 | +== Writing a page |
| 34 | + |
| 35 | +Each page is Markdown with a `---`-fenced front-matter block: |
| 36 | + |
| 37 | +[source,markdown] |
| 38 | +---- |
| 39 | +--- |
| 40 | +title: Hello, world |
| 41 | +date: 2026-06-23 |
| 42 | +description: A first page built with casket. |
| 43 | +tags: [intro, meta] |
| 44 | +--- |
| 45 | +# Hello |
| 46 | +
|
| 47 | +Body text in **Markdown**, rendered by Pandoc: tables, footnotes, |
| 48 | +task lists, fenced + highlighted code, smart punctuation. |
| 49 | +---- |
| 50 | + |
| 51 | +A leading SPDX HTML comment before the `---` fence is tolerated. |
| 52 | + |
| 53 | +Recognised keys: `title`, `date`, `description`, `tags`, `layout`, `draft`, |
| 54 | +`slug`, and `site`/`brand`. Any *other* key is exposed to templates as |
| 55 | +`{{key}}`, so front-matter doubles as page-scoped template data. |
| 56 | + |
| 57 | +== Building |
| 58 | + |
| 59 | +[source,bash] |
| 60 | +---- |
| 61 | +casket-ssg build content _site # input dir, then optional output dir (default _site) |
| 62 | +casket-ssg build --drafts content # include `draft: true` pages (or set CASKET_DRAFTS=1) |
| 63 | +casket-ssg build --no-clean-urls content |
| 64 | +casket-ssg clean _site # remove an output directory |
| 65 | +---- |
| 66 | + |
| 67 | +== Output structure |
| 68 | + |
| 69 | +By default casket emits *clean URLs*: |
| 70 | + |
| 71 | +[cols="2,2"] |
| 72 | +|=== |
| 73 | +| Source | Output |
| 74 | + |
| 75 | +| `content/index.md` | `_site/index.html` |
| 76 | +| `content/about.md` | `_site/about/index.html` (→ `/about/`) |
| 77 | +| `content/posts/2026-…-hello.md` | `_site/posts/2026-…-hello/index.html` |
| 78 | +|=== |
| 79 | + |
| 80 | +* `index.md` always maps to `index.html` in the same directory. |
| 81 | +* `slug: my-slug` front-matter overrides the output basename. |
| 82 | +* `--no-clean-urls` emits `about.html` instead of `about/index.html`. |
| 83 | + |
| 84 | +== Collections |
| 85 | + |
| 86 | +Any source directory with *two or more dated pages* and no `index.md` of its own |
| 87 | +gets a generated listing page (newest first). So `content/posts/` with several |
| 88 | +dated posts yields `/posts/` automatically — no configuration. |
| 89 | + |
| 90 | +== Tags |
| 91 | + |
| 92 | +Pages with `tags:` produce a page per tag at `/tags/<tag>/` (slugified) plus a |
| 93 | +`/tags/` index counting each tag. |
| 94 | + |
| 95 | +== Feeds & discovery |
| 96 | + |
| 97 | +casket writes, from `site.baseurl`: |
| 98 | + |
| 99 | +* `sitemap.xml` — every page URL (including generated collection/tag pages); |
| 100 | +* `feed.xml` — an Atom feed of the 20 most recent *dated* pages. |
| 101 | + |
| 102 | +== Drafts |
| 103 | + |
| 104 | +`draft: true` pages are skipped unless you pass `--drafts` (or set |
| 105 | +`CASKET_DRAFTS=1`). Skipped drafts are reported during the build. |
| 106 | + |
| 107 | +== Verbatim files (`public/`) |
| 108 | + |
| 109 | +Anything under `<input>/public/` is copied to the *output root*, so |
| 110 | +`public/CNAME`, `public/.well-known/security.txt`, `public/robots.txt`, and |
| 111 | +`public/favicon.svg` ship at `/CNAME`, `/.well-known/…`, etc. |
| 112 | + |
| 113 | +== Site configuration (`site.conf`) |
| 114 | + |
| 115 | +A simple `key = value` file (looked up in the CWD, then the input dir), exposed |
| 116 | +to templates as `{{site.*}}`: |
| 117 | + |
| 118 | +[source,ini] |
| 119 | +---- |
| 120 | +title = My Casket Site |
| 121 | +baseurl = https://example.com |
| 122 | +author = Jane Doe |
| 123 | +description = Built with casket |
| 124 | +nav = Home=/,Posts=/posts/,About=/about/ |
| 125 | +---- |
| 126 | + |
| 127 | +`nav` (optional) drives the primary navigation; without it, casket derives nav |
| 128 | +from your top-level pages. |
0 commit comments