@geekity/cms 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -110
- package/admin/pages/documents/editor.njk +0 -13
- package/admin/pages/{settings/federation.njk → federation/settings.njk} +7 -5
- package/admin/pages/navigation/menus.njk +132 -0
- package/admin/pages/settings/reading.njk +2 -10
- package/admin/pages/tools/content-index.njk +89 -0
- package/admin/pages/users/edit.njk +25 -2
- package/admin/pages/users/list.njk +5 -2
- package/admin/static/admin.css +78 -3
- package/dist/access-log.d.ts +44 -0
- package/dist/access-log.d.ts.map +1 -0
- package/dist/access-log.js +71 -0
- package/dist/access-log.js.map +1 -0
- package/dist/admin/accounts.d.ts +10 -0
- package/dist/admin/accounts.d.ts.map +1 -1
- package/dist/admin/accounts.js +15 -3
- package/dist/admin/accounts.js.map +1 -1
- package/dist/admin/documents.d.ts +3 -13
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +2 -34
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/federation.d.ts +10 -0
- package/dist/admin/federation.d.ts.map +1 -1
- package/dist/admin/federation.js +10 -0
- package/dist/admin/federation.js.map +1 -1
- package/dist/admin/index.d.ts +3 -3
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +2 -2
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/menu.d.ts.map +1 -1
- package/dist/admin/menu.js +19 -2
- package/dist/admin/menu.js.map +1 -1
- package/dist/admin/navigation.d.ts +155 -0
- package/dist/admin/navigation.d.ts.map +1 -0
- package/dist/admin/navigation.js +292 -0
- package/dist/admin/navigation.js.map +1 -0
- package/dist/admin/routes.d.ts.map +1 -1
- package/dist/admin/routes.js +9 -0
- package/dist/admin/routes.js.map +1 -1
- package/dist/admin/session.d.ts +17 -3
- package/dist/admin/session.d.ts.map +1 -1
- package/dist/admin/session.js +19 -5
- package/dist/admin/session.js.map +1 -1
- package/dist/admin/settings-federation.d.ts +10 -1
- package/dist/admin/settings-federation.d.ts.map +1 -1
- package/dist/admin/settings-federation.js +26 -6
- package/dist/admin/settings-federation.js.map +1 -1
- package/dist/admin/settings-page.d.ts +26 -9
- package/dist/admin/settings-page.d.ts.map +1 -1
- package/dist/admin/settings-page.js +18 -12
- package/dist/admin/settings-page.js.map +1 -1
- package/dist/admin/settings-pages.d.ts +14 -3
- package/dist/admin/settings-pages.d.ts.map +1 -1
- package/dist/admin/settings-pages.js +15 -5
- package/dist/admin/settings-pages.js.map +1 -1
- package/dist/admin/settings-reading.d.ts +5 -4
- package/dist/admin/settings-reading.d.ts.map +1 -1
- package/dist/admin/settings-reading.js +6 -5
- package/dist/admin/settings-reading.js.map +1 -1
- package/dist/admin/settings.d.ts +29 -15
- package/dist/admin/settings.d.ts.map +1 -1
- package/dist/admin/settings.js +28 -78
- package/dist/admin/settings.js.map +1 -1
- package/dist/admin/templates.d.ts +3 -1
- package/dist/admin/templates.d.ts.map +1 -1
- package/dist/admin/templates.js +3 -1
- package/dist/admin/templates.js.map +1 -1
- package/dist/admin/tools.d.ts +111 -0
- package/dist/admin/tools.d.ts.map +1 -0
- package/dist/admin/tools.js +182 -0
- package/dist/admin/tools.js.map +1 -0
- package/dist/admin/users.d.ts +24 -0
- package/dist/admin/users.d.ts.map +1 -1
- package/dist/admin/users.js +102 -30
- package/dist/admin/users.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/comments/form.d.ts +48 -0
- package/dist/comments/form.d.ts.map +1 -1
- package/dist/comments/form.js +15 -0
- package/dist/comments/form.js.map +1 -1
- package/dist/comments/index.d.ts +4 -3
- package/dist/comments/index.d.ts.map +1 -1
- package/dist/comments/index.js +2 -1
- package/dist/comments/index.js.map +1 -1
- package/dist/comments/routes.d.ts +3 -1
- package/dist/comments/routes.d.ts.map +1 -1
- package/dist/comments/routes.js +46 -11
- package/dist/comments/routes.js.map +1 -1
- package/dist/comments/submission.d.ts +37 -3
- package/dist/comments/submission.d.ts.map +1 -1
- package/dist/comments/submission.js +59 -20
- package/dist/comments/submission.js.map +1 -1
- package/dist/comments/viewer.d.ts +21 -0
- package/dist/comments/viewer.d.ts.map +1 -0
- package/dist/comments/viewer.js +45 -0
- package/dist/comments/viewer.js.map +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/content/store.d.ts +18 -0
- package/dist/content/store.d.ts.map +1 -1
- package/dist/content/store.js +18 -0
- package/dist/content/store.js.map +1 -1
- package/dist/env.d.ts +12 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/federation/mount.d.ts +4 -0
- package/dist/federation/mount.d.ts.map +1 -1
- package/dist/federation/mount.js +60 -15
- package/dist/federation/mount.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -4
- package/dist/index.js.map +1 -1
- package/dist/web/authors.d.ts +15 -1
- package/dist/web/authors.d.ts.map +1 -1
- package/dist/web/authors.js +9 -1
- package/dist/web/authors.js.map +1 -1
- package/dist/web/index.d.ts +4 -4
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +1 -1
- package/dist/web/index.js.map +1 -1
- package/dist/web/navigation.d.ts +235 -35
- package/dist/web/navigation.d.ts.map +1 -1
- package/dist/web/navigation.js +333 -57
- package/dist/web/navigation.js.map +1 -1
- package/dist/web/negotiate.d.ts +22 -0
- package/dist/web/negotiate.d.ts.map +1 -1
- package/dist/web/negotiate.js +25 -10
- package/dist/web/negotiate.js.map +1 -1
- package/dist/web/render.d.ts +16 -10
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +21 -14
- package/dist/web/render.js.map +1 -1
- package/dist/web/routes.d.ts.map +1 -1
- package/dist/web/routes.js +11 -2
- package/dist/web/routes.js.map +1 -1
- package/dist/web/themes.d.ts +21 -0
- package/dist/web/themes.d.ts.map +1 -1
- package/dist/web/themes.js +37 -0
- package/dist/web/themes.js.map +1 -1
- package/package.json +1 -1
- package/templates/site/content/_data/site.json +7 -0
- package/templates/site/content/pages/about.md +7 -0
- package/themes/default/README.md +215 -80
- package/themes/default/layouts/author.njk +3 -4
- package/themes/default/layouts/base.njk +38 -49
- package/themes/default/layouts/front-page.njk +1 -2
- package/themes/default/partials/bio.njk +20 -22
- package/themes/default/partials/comment-form.njk +31 -3
- package/themes/default/partials/menu.njk +34 -0
- package/themes/default/partials/search-form.njk +5 -0
- package/themes/default/static/style.css +54 -13
- package/themes/default/theme.json +5 -1
package/themes/default/README.md
CHANGED
|
@@ -21,13 +21,14 @@ themes/default/
|
|
|
21
21
|
post-list.njk the h-feed a listing is made of
|
|
22
22
|
pagination.njk previous/next pager
|
|
23
23
|
tags.njk macros for tag and category links
|
|
24
|
-
bio.njk who an entry is by, as an h-card
|
|
24
|
+
bio.njk who an entry is by, as an h-card
|
|
25
|
+
menu.njk one named menu, as a nav of links
|
|
25
26
|
feeds.njk macros for the feed links in <head>
|
|
26
27
|
conversation.njk the replies, likes and boosts under a post
|
|
27
28
|
comment-form.njk the form under a post that is taking comments
|
|
28
29
|
contact-form.njk the form on a page whose front matter says contact: true
|
|
29
30
|
archive.njk every post by month, on a page that says archive: true
|
|
30
|
-
search-form.njk the search box,
|
|
31
|
+
search-form.njk the search box, on the search page
|
|
31
32
|
mail/
|
|
32
33
|
test.*.njk the Send test email message
|
|
33
34
|
password-reset.*.njk the forgot-password link
|
|
@@ -141,24 +142,36 @@ words above the posts; `layouts/home.njk` already does.
|
|
|
141
142
|
skip link, one `.global-wrapper` at the 42rem measure, `.global-header`,
|
|
142
143
|
`<main id="main">` and the footer, in that order.
|
|
143
144
|
|
|
144
|
-
**
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
**One home for each kind of link.** The header carries the site menu, on every
|
|
146
|
+
page. [The bio](#the-bio) carries the person whose page or post it is. The
|
|
147
|
+
footer carries the site's own links. The search box is on the search page and
|
|
148
|
+
nowhere else.
|
|
149
|
+
|
|
150
|
+
**The header has one rule, and the menu under it.** On the front page it is the
|
|
151
|
+
site title as `h1.main-heading`, linked home, with `site.tagline` in a
|
|
152
|
+
paragraph under it; on every other page it is `a.header-link-home`, the site
|
|
153
|
+
title small and linked home, and no tagline. The wrapper carries
|
|
154
|
+
`data-is-root-path="true"` at `/` and nothing anywhere else, which is how the
|
|
155
|
+
stylesheet tells the two apart. `menus.primary` is printed inside the
|
|
156
|
+
`.global-header` on both: at the root it is a line of its own under the
|
|
157
|
+
tagline, and everywhere else the stylesheet lays the header out as one line so
|
|
158
|
+
it sits beside the link home. See [Navigation](#navigation).
|
|
150
159
|
|
|
151
160
|
**The footer** prints the copyright with the current year and `site.author`,
|
|
152
|
-
`Published with Geekity`, and then
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
`Published with Geekity`, and then `menus.footer` — the `footer` area this
|
|
162
|
+
theme declares, and the whole of what the footer links. A site that wants its
|
|
163
|
+
feed there types an `RSS | /feed/` line into it, the way the starter site does.
|
|
164
|
+
Nothing in the footer is read off an account: it used to hold one `rel="me"`
|
|
165
|
+
link per entry of `siteAuthor.links`, which was one nominated user's profile
|
|
166
|
+
presented as the site's. An empty or missing footer menu prints no list at all,
|
|
167
|
+
and the copyright line stands on its own. The year is
|
|
156
168
|
`{{ "now" | date("year") }}` — `now` is the one word the `date` filter reads
|
|
157
169
|
rather than parses — so it is the year at the moment the page is rendered, in
|
|
158
170
|
the site's own timezone.
|
|
159
171
|
|
|
160
|
-
Webrings, badges, a licence notice and anything else
|
|
161
|
-
deliberately not in the package. They go in a site theme's `footer`
|
|
172
|
+
Webrings, badges, a licence notice and anything else that is markup rather than
|
|
173
|
+
a link are deliberately not in the package. They go in a site theme's `footer`
|
|
174
|
+
block:
|
|
162
175
|
|
|
163
176
|
```njk
|
|
164
177
|
{% extends "layouts/base.njk" %}
|
|
@@ -244,25 +257,31 @@ meta line, and this is the whole credit.
|
|
|
244
257
|
`bioAuthor` is the one thing to set, and nothing renders when it is absent.
|
|
245
258
|
It is one of the profile objects the context already carries: `author` on a
|
|
246
259
|
post, falling back to `siteAuthor`; `siteAuthor` on a page; the archive's
|
|
247
|
-
person on an author archive.
|
|
248
|
-
[Navigation](#navigation).
|
|
260
|
+
person on an author archive.
|
|
249
261
|
|
|
250
262
|
What the bio prints: a round `u-photo` at 50px when they have an avatar,
|
|
251
263
|
"Written by" and their name as a `p-name u-url` linked `rel="author me"` to
|
|
252
|
-
their archive, then `p-job-title` and `p-locality` when the profile says
|
|
253
|
-
|
|
254
|
-
|
|
264
|
+
their archive, then `p-job-title` and `p-locality` when the profile says, then
|
|
265
|
+
their `p-note` and their `rel="me"` links as a `ul.hlist.bio-links` — each
|
|
266
|
+
link's `rel` is on it ready to print, `me` first and then whatever else the
|
|
267
|
+
Links box typed. Each of
|
|
268
|
+
those is printed only when the profile says it, so a profile holding a name
|
|
269
|
+
alone prints a name alone. A name this site has no account for is printed
|
|
270
|
+
unlinked, because the file still said somebody wrote this.
|
|
271
|
+
|
|
272
|
+
**The person and only the person.** The site menu used to be printed here as
|
|
273
|
+
well, because the design had no header navigation; it is in the header on
|
|
274
|
+
every page now — see [Navigation](#navigation) — so the bio says nothing about
|
|
275
|
+
the site. The note and the links used to be behind a `bioProfile` switch that
|
|
276
|
+
an author archive set and an entry did not, on the grounds that the page
|
|
277
|
+
footer already carried the site's identity links. It carries nobody's, so the
|
|
278
|
+
switch is gone and the card is the same wherever it appears: at the top of
|
|
279
|
+
somebody's archive, and under each of their posts.
|
|
255
280
|
|
|
256
281
|
`bioLead` is what the line opens with, `Written by` unless a layout sets
|
|
257
282
|
another; `layouts/author.njk` sets `Posts by`, because the card there heads
|
|
258
283
|
somebody's writing rather than crediting one piece of it.
|
|
259
284
|
|
|
260
|
-
Set `bioProfile` as well and it also prints their `p-note` and their `rel="me"`
|
|
261
|
-
links; `layouts/author.njk` does, because that page is about the person rather
|
|
262
|
-
than about something they wrote. An entry leaves it unset: the page footer
|
|
263
|
-
already prints the site's identity links, and the `rel="author me"` link leads
|
|
264
|
-
to the archive where this person's own are.
|
|
265
|
-
|
|
266
285
|
**A site that used `partials/byline.njk`** — `{{ byline.line(author) }}` from
|
|
267
286
|
an overridden layout — either includes this partial instead or writes the line
|
|
268
287
|
itself. It was four lines of markup, and the object behind it has not changed:
|
|
@@ -363,17 +382,17 @@ saying how many documents matched, a `div.search-results` of
|
|
|
363
382
|
indexed with `<meta name="robots" content="noindex">` in its `head` block.
|
|
364
383
|
|
|
365
384
|
The context is a listing's with two differences. `query` is the words searched
|
|
366
|
-
for, trimmed, and an empty string on the page before a search
|
|
367
|
-
|
|
368
|
-
search box off it. Each entry in `posts` carries a `snippet`: a few words of
|
|
385
|
+
for, trimmed, and an empty string on the page before a search. Each entry in
|
|
386
|
+
`posts` carries a `snippet`: a few words of
|
|
369
387
|
HTML around the match, escaped, with every matched word in `<mark>`. Print it
|
|
370
388
|
with `safe`. The results are best match first rather than newest first, and
|
|
371
389
|
only what the public site would serve is ever among them.
|
|
372
390
|
|
|
373
391
|
`partials/search-form.njk` is a `GET` form to `/search/` with the words in `q`,
|
|
374
|
-
so it needs no JavaScript and a search is a URL.
|
|
375
|
-
the
|
|
376
|
-
|
|
392
|
+
so it needs no JavaScript and a search is a URL. This layout is the only thing
|
|
393
|
+
that includes it: the box used to be in the footer of every page as well, and
|
|
394
|
+
search is a menu item now — a `Search | /search/` line like any other. The
|
|
395
|
+
search page takes the path `/search/` ahead of any document permalinked there.
|
|
377
396
|
|
|
378
397
|
### The front page
|
|
379
398
|
|
|
@@ -393,8 +412,8 @@ It draws the page's own words and then what the site has been writing:
|
|
|
393
412
|
3. `p.front-links`, a line of links to where the writing is. The posts page is
|
|
394
413
|
linked by its own title when the site names one, and the search always is
|
|
395
414
|
(TASK-22).
|
|
396
|
-
4. The bio, under a rule, exactly as an entry ends
|
|
397
|
-
|
|
415
|
+
4. The bio, under a rule, exactly as an entry ends: whoever the site's author
|
|
416
|
+
setting names, with their note and their own links.
|
|
398
417
|
|
|
399
418
|
Two context keys are the front page's alone. `recentPosts` is the entries to
|
|
400
419
|
list, in the same shape a listing's are: the posts of the current month when
|
|
@@ -691,7 +710,7 @@ Every template gets:
|
|
|
691
710
|
| Key | What it holds |
|
|
692
711
|
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
693
712
|
| `site` | `content/_data/site.json`, if the site has one, over the defaults `title` and `url`. Any key in the file is readable, so `site.tagline`, `site.author` and anything else a site adds are all available. |
|
|
694
|
-
| `
|
|
713
|
+
| `menus` | Every menu the site stores, by name, marked for this page: `menus.primary`, `menus.footer`, and any other name. See [Navigation](#navigation). |
|
|
695
714
|
| `siteAuthor` | Who the page is by, as a profile. **Absent** when nobody matches. See [Bylines and author archives](#bylines-and-author-archives). |
|
|
696
715
|
| `icons` | The site's icons, as `{ rel, sizes, href }`. Empty until the site has an avatar to derive them from. See [The head](#the-head). |
|
|
697
716
|
|
|
@@ -774,17 +793,18 @@ the `partials/byline.njk` it used to have.
|
|
|
774
793
|
|
|
775
794
|
`layouts/author.njk` is that person's archive, at `/author/{username}/`, with
|
|
776
795
|
their pages at `/author/{username}/page/2/` and their three feeds under
|
|
777
|
-
`/author/{username}/feed/`. It is headed with their name and then the bio
|
|
778
|
-
|
|
779
|
-
their published posts newest first. The source design has no author archive, so
|
|
796
|
+
`/author/{username}/feed/`. It is headed with their name and then the bio —
|
|
797
|
+
the avatar, the note and the `rel="me"` links, the same card that ends each of
|
|
798
|
+
their posts — and lists their published posts newest first. The source design has no author archive, so
|
|
780
799
|
the heading is the CMS's own; the h-card under it is the one partial the theme
|
|
781
800
|
has. A user with no profile still has one; they are called by their username.
|
|
782
801
|
|
|
783
802
|
`siteAuthor` is the same object on a different question: not who wrote this
|
|
784
|
-
document, but who the page in front of the reader is by. It is what the bio
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
803
|
+
document, but who the page in front of the reader is by. It is what the bio and
|
|
804
|
+
any structured data a theme emits should both read, so that what a reader sees
|
|
805
|
+
and what a machine reads cannot drift apart. The footer does not read it at
|
|
806
|
+
all: the site's links are `menus.footer`, typed rather than borrowed from an
|
|
807
|
+
account. It resolves in this order:
|
|
788
808
|
|
|
789
809
|
- the document's own `author`, on a post or a page that names one;
|
|
790
810
|
- the person whose archive it is, on an author archive;
|
|
@@ -795,8 +815,7 @@ It is **absent** when none of those name anybody this site has. The site
|
|
|
795
815
|
setting is read more strictly than a byline is: a byline prints the name a file
|
|
796
816
|
gives whether or not somebody answers to it, but a site author with no profile
|
|
797
817
|
behind it has no picture, no bio and nowhere to link, so there is nothing to
|
|
798
|
-
print and the key is not there. Write `{% if siteAuthor %}` around the bio
|
|
799
|
-
the identity links.
|
|
818
|
+
print and the key is not there. Write `{% if siteAuthor %}` around the bio.
|
|
800
819
|
|
|
801
820
|
The URL is not only a page. Each user is an ActivityPub actor at that address,
|
|
802
821
|
so it is the page a follower lands on when they click through from the
|
|
@@ -806,50 +825,126 @@ it.
|
|
|
806
825
|
|
|
807
826
|
## Navigation
|
|
808
827
|
|
|
809
|
-
|
|
810
|
-
is the page being looked at. Every template gets it.
|
|
828
|
+
A site stores its menus by name in `content/_data/site.json`:
|
|
811
829
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
830
|
+
```json
|
|
831
|
+
{
|
|
832
|
+
"menus": {
|
|
833
|
+
"primary": [
|
|
834
|
+
{ "label": "Home", "url": "/" },
|
|
835
|
+
{ "label": "About", "url": "/about/" }
|
|
836
|
+
],
|
|
837
|
+
"footer": [
|
|
838
|
+
{ "label": "Colophon", "url": "/colophon/" },
|
|
839
|
+
{ "label": "Mastodon", "url": "https://example.social/@me", "rel": "me" }
|
|
840
|
+
]
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
```
|
|
820
844
|
|
|
821
|
-
|
|
822
|
-
|
|
845
|
+
Every template gets them as `menus`, keyed by the same names, already in order
|
|
846
|
+
and already knowing which item is the page being looked at:
|
|
823
847
|
|
|
824
848
|
```njk
|
|
825
|
-
{%
|
|
826
|
-
<
|
|
827
|
-
|
|
828
|
-
{% for item in menu %}
|
|
829
|
-
<li><a href="{{ item.url | url }}"{% if item.current %} class="is-current" aria-current="page"{% endif %}>{{ item.label }}</a></li>
|
|
830
|
-
{% endfor %}
|
|
831
|
-
</ul>
|
|
832
|
-
</nav>
|
|
833
|
-
{% endif %}
|
|
849
|
+
{% for item in menus.footer %}
|
|
850
|
+
<a href="{{ item.url | url }}">{{ item.label }}</a>
|
|
851
|
+
{% endfor %}
|
|
834
852
|
```
|
|
835
853
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
854
|
+
That is the whole interface, so a theme renders a menu this one has never heard
|
|
855
|
+
of by looping over the name a site stored it under.
|
|
856
|
+
|
|
857
|
+
### The areas a theme declares
|
|
858
|
+
|
|
859
|
+
`theme.json` says where a theme renders a menu, so the Navigation screen can
|
|
860
|
+
offer those places and nowhere else. This theme declares two:
|
|
861
|
+
|
|
862
|
+
```json
|
|
863
|
+
{
|
|
864
|
+
"name": "Default",
|
|
865
|
+
"kind": "site",
|
|
866
|
+
"areas": [
|
|
867
|
+
{ "name": "primary", "label": "Site menu" },
|
|
868
|
+
{ "name": "footer", "label": "Footer links" }
|
|
869
|
+
]
|
|
870
|
+
}
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
`name` is the key under `menus`, in `site.json` and on the context. `label` is
|
|
874
|
+
what a person choosing an area reads; leave it out and it falls back to the
|
|
875
|
+
name. Declare a third area and a site can fill it in; declare none and the
|
|
876
|
+
theme renders no menu.
|
|
877
|
+
|
|
878
|
+
The declaration is for the screen, not for the render. Every menu the site
|
|
879
|
+
stores is on the context whether or not a theme declared an area for it — a
|
|
880
|
+
menu nothing loops over is rendered nowhere and kept, which is what lets
|
|
881
|
+
somebody write the menu a theme will use before switching to that theme. An
|
|
882
|
+
`areas` that is missing, or that will not read, leaves the theme with none
|
|
883
|
+
rather than failing to load it.
|
|
841
884
|
|
|
842
|
-
|
|
843
|
-
names it, and then every published page whose front matter says
|
|
844
|
-
`navigation: true`, ordered by `navigationOrder` and then by title. A page that
|
|
845
|
-
names no order sorts after every page that does.
|
|
885
|
+
### One item
|
|
846
886
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
the
|
|
852
|
-
|
|
887
|
+
Each item is `{ label, url, current }`, plus `rel` on a link that carries `rel`
|
|
888
|
+
values. `url` is a site-root path or an absolute URL for somewhere else, so put
|
|
889
|
+
it through the `url` filter and a site served from a subdirectory still links
|
|
890
|
+
correctly. `current` is true for the item whose path is the one being rendered,
|
|
891
|
+
comparing without the trailing slash; an item pointing off the site is never
|
|
892
|
+
current. `rel` is one string holding every value the line carried, already
|
|
893
|
+
lower case and each said once — print it as the whole attribute,
|
|
894
|
+
`rel="{{ item.rel }}"`, rather than building one of your own. `me` is the value
|
|
895
|
+
most menus have, for the IndieWeb's identity check: Mastodon verifies a link on
|
|
896
|
+
a profile by looking for a `rel="me"` link back, so a footer link to a profile
|
|
897
|
+
marked `me` is what makes the tick appear.
|
|
898
|
+
|
|
899
|
+
### Where this theme prints them
|
|
900
|
+
|
|
901
|
+
`partials/menu.njk` holds the markup, as one macro:
|
|
902
|
+
|
|
903
|
+
```njk
|
|
904
|
+
{% import "partials/menu.njk" as nav %}
|
|
905
|
+
{{ nav.list(menus.footer, "Footer") }}
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
The second argument is the `aria-label`, which is what tells a reader on a
|
|
909
|
+
screen reader which of a page's menus this one is. Nothing is printed for a
|
|
910
|
+
menu with nothing in it.
|
|
911
|
+
|
|
912
|
+
`menus.primary` is printed once, in `header.global-header`, on every page
|
|
913
|
+
(TASK-105). At the root, where the header is the site title with the tagline
|
|
914
|
+
under it, the menu is a line of its own under the tagline; everywhere else,
|
|
915
|
+
where the header is the small link home, the stylesheet lays the header out as
|
|
916
|
+
one line and the menu sits beside it. Nothing decides between two places any
|
|
917
|
+
more: a reader looks in the same spot whatever they are reading, and a layout
|
|
918
|
+
that overrides the `content` block cannot lose the navigation by not printing
|
|
919
|
+
a bio.
|
|
920
|
+
|
|
921
|
+
`menus.footer` is printed in the `footer` block of `layouts/base.njk`, under
|
|
922
|
+
the copyright line, and is the whole of what the footer links — a feed, a
|
|
923
|
+
colophon, a webring, a profile marked `me`. The footer reads nothing off an
|
|
924
|
+
account: it used to print one `rel="me"` link per entry of `siteAuthor.links`,
|
|
925
|
+
which meant one nominated user's links stood for the site and nobody else's
|
|
926
|
+
appeared at all.
|
|
927
|
+
|
|
928
|
+
Every menu reads the same: a link in one is the colour, the underline and the
|
|
929
|
+
size of a link in the page's prose, in the sans heading font a `nav` is set in
|
|
930
|
+
(TASK-111). A menu is navigation somebody is meant to use rather than a
|
|
931
|
+
footnote, so none of them is set smaller than the words around it, and the same
|
|
932
|
+
goes for the `rel="me"` links `partials/bio.njk` prints under a post. The two
|
|
933
|
+
links that read as plain text instead are the site title and
|
|
934
|
+
`a.header-link-home`, which are the page saying where it is rather than
|
|
935
|
+
somewhere to go, and the stylesheet names those two rather than reaching for
|
|
936
|
+
every link that happens to sit in a `<header>`.
|
|
937
|
+
|
|
938
|
+
`menus.primary` is the whole of the site menu, in the order the Navigation
|
|
939
|
+
screen names it. Both areas are declared in this theme's `theme.json`, which is
|
|
940
|
+
what puts them on `/admin/navigation` with those labels; a theme that renders a
|
|
941
|
+
third menu declares a third area. A page cannot put itself in a menu; a page
|
|
942
|
+
that should be linked — including the one a site serves as its front page,
|
|
943
|
+
typed `Home | /` — is typed into the box there.
|
|
944
|
+
|
|
945
|
+
Because the menus live in `site.json`, an Eleventy build of the same content
|
|
946
|
+
renders the same ones; `docs/eleventy.config.example.js` builds them as
|
|
947
|
+
`collections.menus`.
|
|
853
948
|
|
|
854
949
|
## Feeds
|
|
855
950
|
|
|
@@ -1084,7 +1179,7 @@ that has CSS for the WordPress theme can bring it.
|
|
|
1084
1179
|
| Key | What it holds |
|
|
1085
1180
|
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1086
1181
|
| `action` | Where the form posts. One fixed path; the post travels as a field. |
|
|
1087
|
-
| `fields` | The name each field is submitted under: `post`, `name`, `email`, `url`, `body`, `inReplyTo`, `trap`, `loaded`, `notify`. Use these rather than typing the names.
|
|
1182
|
+
| `fields` | The name each field is submitted under: `post`, `name`, `email`, `url`, `body`, `inReplyTo`, `trap`, `loaded`, `notify`, `csrf`. Use these rather than typing the names. |
|
|
1088
1183
|
| `post` | The post's slug, for the hidden field. |
|
|
1089
1184
|
| `loaded` | When this form was rendered, in epoch milliseconds, for the hidden field. A submission that comes back too fast is refused. |
|
|
1090
1185
|
| `values` | What is in the fields: empty on a fresh form, what was typed on a refused one. |
|
|
@@ -1098,6 +1193,46 @@ technology, and give it `tabindex="-1"` and `autocomplete="off"`. A submission
|
|
|
1098
1193
|
that filled it is dropped. **Do not** remove it from a replacement partial —
|
|
1099
1194
|
it is one of three things standing between the site and a spam queue.
|
|
1100
1195
|
|
|
1196
|
+
### When somebody is signed in
|
|
1197
|
+
|
|
1198
|
+
Two more keys are on `commentForm` when — and only when — the request carries a
|
|
1199
|
+
valid session for this site's admin:
|
|
1200
|
+
|
|
1201
|
+
| Key | What it holds |
|
|
1202
|
+
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
1203
|
+
| `signedInAs` | Who the comment will be posted as: `name`, which is their display name or their username, and `url`, which is their author archive. |
|
|
1204
|
+
| `csrfToken` | The value for a hidden `fields.csrf`, without which the submission is refused. |
|
|
1205
|
+
|
|
1206
|
+
**A replacement partial has to draw both forms**, because the site draws this
|
|
1207
|
+
one for its own author and the stranger's one for everybody else:
|
|
1208
|
+
|
|
1209
|
+
```njk
|
|
1210
|
+
{% if commentForm.signedInAs %}
|
|
1211
|
+
<input type="hidden" name="{{ commentForm.fields.csrf }}" value="{{ commentForm.csrfToken }}" />
|
|
1212
|
+
<p class="comment-signed-in">
|
|
1213
|
+
Commenting as <a href="{{ commentForm.signedInAs.url }}">{{ commentForm.signedInAs.name }}</a>.
|
|
1214
|
+
</p>
|
|
1215
|
+
{% else %}
|
|
1216
|
+
{# fields.loaded, fields.trap, and the name, email and website boxes #}
|
|
1217
|
+
{% endif %}
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
The signed-in branch renders **no** name, email or website box, **no**
|
|
1221
|
+
`fields.trap` and **no** `fields.loaded`: the site already knows all three
|
|
1222
|
+
answers, and a session is better evidence that this is a person than a honeypot
|
|
1223
|
+
or a stopwatch. It renders `fields.csrf` instead, because this is the one form
|
|
1224
|
+
on the public site that acts on somebody's behalf — without the token, a page
|
|
1225
|
+
on another site could make them post under their own name. The comment box, the
|
|
1226
|
+
reply-to field and the notify box are the same in both branches.
|
|
1227
|
+
|
|
1228
|
+
The account's email address is **never** on the context. The comment is stored
|
|
1229
|
+
with it, exactly as a stranger's is, and it is no more printable than theirs.
|
|
1230
|
+
|
|
1231
|
+
A page carrying this form is one reader's page rather than the post, so the CMS
|
|
1232
|
+
answers it `Cache-Control: private, no-store` and with no ETag. A theme does
|
|
1233
|
+
not have to do anything about that, but it is why printing somebody's name into
|
|
1234
|
+
a page is safe here.
|
|
1235
|
+
|
|
1101
1236
|
Two more keys travel beside it, both from the URL rather than from the post:
|
|
1102
1237
|
|
|
1103
1238
|
| Key | What it holds |
|
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
The source design has no author archive, so there is nothing to port: the
|
|
11
11
|
heading is the CMS's own. What it does not keep is an h-card of its own.
|
|
12
|
-
`partials/bio.njk` is the one the theme has, and
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
`partials/bio.njk` is the one the theme has, and it prints the whole profile
|
|
13
|
+
— the note and the `rel="me"` links — wherever it is, so this page is headed
|
|
14
|
+
with the same card that ends each of their posts.
|
|
15
15
|
#}
|
|
16
16
|
{% extends "layouts/base.njk" %}
|
|
17
17
|
|
|
18
18
|
{% set bioAuthor = author %}
|
|
19
|
-
{% set bioProfile = true %}
|
|
20
19
|
{% set bioLead = "Posts by" %}
|
|
21
20
|
|
|
22
21
|
{% block title %}{{ author.name }}{% if pagination.pageNumber > 0 %}, page {{ pagination.pageNumber + 1 }}{% endif %} · {{ site.title }}{% endblock %}
|
|
@@ -3,25 +3,26 @@
|
|
|
3
3
|
|
|
4
4
|
The shell of the andrewshell.org design (decision-16): one wrapper at the
|
|
5
5
|
measure, a big site title and tagline on the front page and a small link home
|
|
6
|
-
everywhere else, and a footer holding the copyright, the colophon
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
everywhere else, and a footer holding the copyright, the colophon and the
|
|
7
|
+
site's own links. `data-is-root-path` on the wrapper is how the stylesheet
|
|
8
|
+
tells the front page from every other one.
|
|
9
|
+
|
|
10
|
+
One home for each kind of link (TASK-105). The header carries the site menu,
|
|
11
|
+
on every page. `partials/bio.njk` carries the person whose page or post it
|
|
12
|
+
is. The footer carries `menus.footer`, the site's own list, and nothing read
|
|
13
|
+
off an account. The search form is on the search page and nowhere else.
|
|
10
14
|
|
|
11
15
|
Blocks a site can override without copying the whole file:
|
|
12
16
|
title the contents of <title>
|
|
13
17
|
head the cards, the icons and the structured data, at the end of
|
|
14
18
|
<head>. Call `super()` from an override that only adds to it.
|
|
15
19
|
alternates <link rel="alternate"> tags, where the feeds go
|
|
16
|
-
header the site header
|
|
20
|
+
header the site header, with the site menu
|
|
17
21
|
content the page body
|
|
18
|
-
footer the site footer
|
|
22
|
+
footer the site footer
|
|
19
23
|
scripts scripts at the end of <body>
|
|
20
|
-
|
|
21
|
-
One variable crosses between a layout and this file: `bioAuthor`, whoever an
|
|
22
|
-
entry's bio credits. A layout that prints `partials/bio.njk` sets it, and the
|
|
23
|
-
footer here leaves the site menu out because the bio is carrying it.
|
|
24
24
|
#}{% import "partials/feeds.njk" as feeds %}
|
|
25
|
+
{%- import "partials/menu.njk" as nav %}
|
|
25
26
|
{#- Whether this is the front page, which is the one page the header and the
|
|
26
27
|
stylesheet treat differently. `page.url` is the URL being served rather
|
|
27
28
|
than the document's own permalink, so a static homepage under the Reading
|
|
@@ -107,6 +108,16 @@
|
|
|
107
108
|
<a class="screen-reader-text" href="#main">Skip to content</a>
|
|
108
109
|
<div class="global-wrapper"{% if atRoot %} data-is-root-path="true"{% endif %}>
|
|
109
110
|
{% block header %}
|
|
111
|
+
{#
|
|
112
|
+
The header, and the site menu inside it on every page (TASK-105): one
|
|
113
|
+
place a reader looks for the navigation, whatever they are reading.
|
|
114
|
+
|
|
115
|
+
The header is two shapes and the menu sits differently in each. At the
|
|
116
|
+
root it is the site title and the tagline, and the menu is a line of
|
|
117
|
+
its own under them; everywhere else it is the small link home, and the
|
|
118
|
+
menu is beside it on that line. The stylesheet does the beside part,
|
|
119
|
+
off `data-is-root-path` on the wrapper.
|
|
120
|
+
#}
|
|
110
121
|
<header class="global-header">
|
|
111
122
|
{% if atRoot %}
|
|
112
123
|
<h1 class="main-heading">
|
|
@@ -118,6 +129,7 @@
|
|
|
118
129
|
{% else %}
|
|
119
130
|
<a class="header-link-home" href="{{ "/" | url }}">{{ site.title }}</a>
|
|
120
131
|
{% endif %}
|
|
132
|
+
{{ nav.list(menus.primary, "Site") }}
|
|
121
133
|
</header>
|
|
122
134
|
{% endblock %}
|
|
123
135
|
|
|
@@ -129,47 +141,24 @@
|
|
|
129
141
|
<footer>
|
|
130
142
|
<p>© {{ "now" | date("year") }}{% if site.author %}, {{ site.author }}{% endif %} · Published with Geekity</p>
|
|
131
143
|
{#
|
|
132
|
-
The
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
{% if siteAuthor.links %}
|
|
142
|
-
{% for link in siteAuthor.links %}
|
|
143
|
-
<li><a rel="me" href="{{ link.href }}">{{ link.label }}</a></li>
|
|
144
|
-
{% endfor %}
|
|
145
|
-
{% endif %}
|
|
146
|
-
</ul>
|
|
147
|
-
{#
|
|
148
|
-
The site menu, on a page that has no bio to carry it.
|
|
144
|
+
The footer menu: the `footer` area this theme declares in its
|
|
145
|
+
`theme.json`, which a site fills in on the Navigation screen, and the
|
|
146
|
+
whole of what the footer links (TASK-105). It is the site's own list
|
|
147
|
+
— the feed, a colophon, a webring, a Mastodon link it wants verified
|
|
148
|
+
— rather than anything read off an account: one nominated user's
|
|
149
|
+
profile links presented as the site's were nobody's to give, and on a
|
|
150
|
+
site with more than one person nobody else's appeared at all. An item
|
|
151
|
+
marked `me` carries `rel="me"`, which is what a site moving a
|
|
152
|
+
verified profile link in here needs.
|
|
149
153
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
{#
|
|
157
|
-
The search box, on every page but the one that already has it at the
|
|
158
|
-
top: `query` is on the context of the search page alone, empty or
|
|
159
|
-
not, so its being defined is what says this is that page.
|
|
154
|
+
Nothing is printed until a site has filled the area in: an empty
|
|
155
|
+
footer menu is no list rather than an empty one, and the copyright
|
|
156
|
+
line above stands on its own.
|
|
157
|
+
|
|
158
|
+
Webrings, badges and anything else that is markup rather than a link
|
|
159
|
+
go in a site theme's `footer` override.
|
|
160
160
|
#}
|
|
161
|
-
{
|
|
162
|
-
{% include "partials/search-form.njk" %}
|
|
163
|
-
{% endif %}
|
|
164
|
-
{% if menu.length and not bioAuthor %}
|
|
165
|
-
<nav class="site-nav" aria-label="Site">
|
|
166
|
-
<ul class="hlist">
|
|
167
|
-
{% for item in menu %}
|
|
168
|
-
<li><a href="{{ item.url | url }}"{% if item.current %} class="is-current" aria-current="page"{% endif %}>{{ item.label }}</a></li>
|
|
169
|
-
{% endfor %}
|
|
170
|
-
</ul>
|
|
171
|
-
</nav>
|
|
172
|
-
{% endif %}
|
|
161
|
+
{{ nav.list(menus.footer, "Footer") }}
|
|
173
162
|
</footer>
|
|
174
163
|
{% endblock %}
|
|
175
164
|
</div>
|
|
@@ -15,8 +15,7 @@
|
|
|
15
15
|
#}
|
|
16
16
|
{% extends "layouts/base.njk" %}
|
|
17
17
|
|
|
18
|
-
{#- Who the bio credits
|
|
19
|
-
the bio rather than in the page footer. -#}
|
|
18
|
+
{#- Who the bio credits: the person the site's author setting names. -#}
|
|
20
19
|
{% set bioAuthor = siteAuthor %}
|
|
21
20
|
|
|
22
21
|
{% block content %}
|