@geekity/cms 0.10.0 → 0.12.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 +22 -5
- package/admin/components/fields.njk +55 -12
- package/admin/layouts/base.njk +5 -1
- package/admin/layouts/settings-page.njk +7 -2
- package/admin/layouts/shell.njk +1 -1
- package/admin/pages/account/forgot.njk +1 -1
- package/admin/pages/account/login.njk +1 -1
- package/admin/pages/account/reset.njk +4 -2
- package/admin/pages/account/setup.njk +1 -1
- package/admin/pages/dashboard/home.njk +1 -0
- package/admin/pages/documents/list.njk +1 -0
- package/admin/pages/documents/taxonomy.njk +1 -0
- package/admin/pages/federation/followers.njk +3 -0
- package/admin/pages/federation/settings.njk +5 -0
- package/admin/pages/media/library.njk +39 -0
- package/admin/pages/navigation/menus.njk +6 -1
- package/admin/pages/settings/discussion.njk +7 -0
- package/admin/pages/settings/email.njk +10 -0
- package/admin/pages/settings/general.njk +7 -0
- package/admin/pages/settings/permalinks.njk +6 -0
- package/admin/pages/settings/reading.njk +7 -0
- package/admin/pages/tools/content-index.njk +1 -0
- package/admin/pages/tools/personal-data.njk +5 -0
- package/admin/pages/users/edit.njk +7 -5
- package/admin/pages/users/list.njk +1 -0
- package/admin/pages/users/new.njk +5 -1
- package/admin/static/admin-bar.css +16 -0
- package/admin/static/admin.css +103 -3
- package/dist/admin/documents.d.ts.map +1 -1
- package/dist/admin/documents.js +18 -0
- package/dist/admin/documents.js.map +1 -1
- package/dist/admin/media.d.ts +13 -2
- package/dist/admin/media.d.ts.map +1 -1
- package/dist/admin/media.js +58 -3
- package/dist/admin/media.js.map +1 -1
- package/dist/admin/store.d.ts +1 -1
- package/dist/admin/store.d.ts.map +1 -1
- package/dist/admin/store.js +1 -1
- package/dist/admin/store.js.map +1 -1
- package/dist/admin/uploads.d.ts +5 -2
- package/dist/admin/uploads.d.ts.map +1 -1
- package/dist/admin/uploads.js +6 -2
- package/dist/admin/uploads.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/federation/article.d.ts.map +1 -1
- package/dist/federation/article.js +37 -1
- package/dist/federation/article.js.map +1 -1
- package/dist/images/alt-text.d.ts +51 -0
- package/dist/images/alt-text.d.ts.map +1 -0
- package/dist/images/alt-text.js +113 -0
- package/dist/images/alt-text.js.map +1 -0
- package/dist/images/markup.d.ts +11 -0
- package/dist/images/markup.d.ts.map +1 -1
- package/dist/images/markup.js +12 -0
- package/dist/images/markup.js.map +1 -1
- package/dist/web/render.d.ts.map +1 -1
- package/dist/web/render.js +27 -12
- package/dist/web/render.js.map +1 -1
- package/package.json +14 -9
- package/themes/default/README.md +230 -106
- package/themes/default/layouts/404.njk +2 -0
- package/themes/default/layouts/500.njk +2 -0
- package/themes/default/layouts/503.njk +2 -0
- package/themes/default/layouts/author.njk +3 -1
- package/themes/default/layouts/base.njk +1 -1
- package/themes/default/layouts/category.njk +4 -2
- package/themes/default/layouts/front-page.njk +1 -1
- package/themes/default/layouts/page.njk +4 -2
- package/themes/default/layouts/post.njk +41 -23
- package/themes/default/layouts/search.njk +2 -5
- package/themes/default/layouts/tag.njk +7 -1
- package/themes/default/partials/comment-form.njk +41 -14
- package/themes/default/partials/contact-form.njk +35 -14
- package/themes/default/partials/conversation.njk +11 -4
- package/themes/default/partials/kicker.njk +34 -0
- package/themes/default/partials/post-list.njk +40 -46
- package/themes/default/partials/reply-context.njk +12 -5
- package/themes/default/src/style.css +1095 -0
- package/themes/default/static/style.css +2079 -1083
- package/themes/default/theme.json +1 -1
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
{#
|
|
2
|
-
One post,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
One post, in the Paper design (doc-9): `article.blog-post.h-entry` opening on
|
|
3
|
+
a kicker that names its kind (Article, Note, Reply) and the categories it is
|
|
4
|
+
filed under.
|
|
5
|
+
|
|
6
|
+
An article, and a reply with a title, has the only headline: the title as its
|
|
7
|
+
`p-name` and the author's `description` under it as the deck, a
|
|
8
|
+
`p-summary`. A post with no title opens on its words, set larger. A reply
|
|
9
|
+
cites what it answers above its words, or above its header when it has one.
|
|
10
|
+
Then the Published line, the tags, a rule and the bio. The neighbouring posts,
|
|
11
|
+
the conversation and the comment form follow the entry.
|
|
7
12
|
|
|
8
13
|
The Published line is inside the `e-content` on purpose. It is what the
|
|
9
|
-
source
|
|
14
|
+
source design does, and it is what makes a reader who copies the words out
|
|
10
15
|
take the date and the permalink with them.
|
|
11
16
|
#}
|
|
12
17
|
{% extends "layouts/base.njk" %}
|
|
13
18
|
{% import "partials/tags.njk" as taxonomy with context %}
|
|
19
|
+
{% import "partials/kicker.njk" as kicker with context %}
|
|
14
20
|
|
|
15
21
|
{#- Who the bio credits: the person the file names, and otherwise whoever the
|
|
16
22
|
site is by, which is what the source theme's bio reads. A post naming
|
|
@@ -24,16 +30,30 @@
|
|
|
24
30
|
{% set entryUrl = page.url or url %}
|
|
25
31
|
<article class="blog-post h-entry">
|
|
26
32
|
{#- A post with no name of its own (a note, or a reply without a title) is
|
|
27
|
-
its words, so it opens on them
|
|
33
|
+
its words, so it opens on them in sight. Its page still needs one h1 to
|
|
34
|
+
navigate by, so it gets one that says what it is, who wrote it and when,
|
|
35
|
+
hidden from sight. It is no `p-name`: a parser would take that as a
|
|
36
|
+
title and read the note as an article. -#}
|
|
28
37
|
{% if named %}
|
|
29
|
-
<header>
|
|
30
|
-
<h1 class="p-name">{{ title }}</h1>
|
|
31
|
-
</header>
|
|
32
|
-
{% endif %}
|
|
33
|
-
|
|
34
38
|
{#- A reply cites the post it answers, whose URL is also the link its
|
|
35
39
|
webmention points a receiver at. -#}
|
|
36
40
|
{% include "partials/reply-context.njk" %}
|
|
41
|
+
<header class="post-header">
|
|
42
|
+
{{ kicker.line(postType | capitalize, false, false, categories) }}
|
|
43
|
+
<h1 class="p-name">{{ title }}</h1>
|
|
44
|
+
{% if description %}
|
|
45
|
+
<p class="post-deck p-summary">{{ description }}</p>
|
|
46
|
+
{% endif %}
|
|
47
|
+
</header>
|
|
48
|
+
{% else %}
|
|
49
|
+
<h1 class="screen-reader-text">
|
|
50
|
+
{{- "Reply" if postType == "reply" else "Note" }}
|
|
51
|
+
{%- if bioAuthor.name %} by {{ bioAuthor.name }}{% endif %}
|
|
52
|
+
{%- if date %}, {{ date | date }}{% endif -%}
|
|
53
|
+
</h1>
|
|
54
|
+
{{ kicker.line(postType | capitalize, false, false, categories) }}
|
|
55
|
+
{% include "partials/reply-context.njk" %}
|
|
56
|
+
{% endif %}
|
|
37
57
|
|
|
38
58
|
<section class="e-content">
|
|
39
59
|
{{ content | safe }}
|
|
@@ -49,16 +69,14 @@
|
|
|
49
69
|
{#- Updated only when it is a different day in the site's own timezone:
|
|
50
70
|
a typo fixed an hour after publishing is not news. -#}
|
|
51
71
|
{%- if updated and (not date or (updated | date("html")) != (date | date("html"))) %}
|
|
52
|
-
{% if date %}<
|
|
72
|
+
{% if date %}<span aria-hidden="true">·</span> {% endif %}<time class="small dt-updated" datetime="{{ updated | date("iso") }}">Updated {{ updated | date }}</time>
|
|
53
73
|
{%- endif -%}
|
|
54
74
|
</p>
|
|
55
75
|
</section>
|
|
56
76
|
|
|
57
|
-
{#
|
|
58
|
-
|
|
59
|
-
{{ taxonomy.categories(categories) }}
|
|
77
|
+
{# The tags, inside the entry so that each link is a `p-category` of it. The
|
|
78
|
+
categories are in the kicker. #}
|
|
60
79
|
{{ taxonomy.list(tags) }}
|
|
61
|
-
|
|
62
80
|
{#- The rule and the footer are the bio's own: a post nobody is credited with
|
|
63
81
|
ends at its words rather than at an empty box. -#}
|
|
64
82
|
{% if bioAuthor %}
|
|
@@ -69,17 +87,17 @@
|
|
|
69
87
|
{% endif %}
|
|
70
88
|
</article>
|
|
71
89
|
|
|
72
|
-
{# The post either side of this one. Each is on the context only
|
|
73
|
-
one, so the ends of the archive draw one
|
|
74
|
-
draws none. #}
|
|
90
|
+
{# The post either side of this one, as two cards. Each is on the context only
|
|
91
|
+
when there is one, so the ends of the archive draw one card and a site of a
|
|
92
|
+
single post draws none. #}
|
|
75
93
|
{% if previous or next %}
|
|
76
|
-
<nav class="blog-post-nav">
|
|
94
|
+
<nav class="blog-post-nav" aria-label="Neighbouring posts">
|
|
77
95
|
<ul>
|
|
78
96
|
{% if previous %}
|
|
79
|
-
<li><a rel="prev" href="{{ previous.url | url }}"
|
|
97
|
+
<li><a rel="prev" href="{{ previous.url | url }}"><span class="blog-post-nav-label">Previous</span> {{ previous.title }}</a></li>
|
|
80
98
|
{% endif %}
|
|
81
99
|
{% if next %}
|
|
82
|
-
<li><a rel="next" href="{{ next.url | url }}">{{ next.title }}
|
|
100
|
+
<li><a rel="next" href="{{ next.url | url }}"><span class="blog-post-nav-label">Next</span> {{ next.title }}</a></li>
|
|
83
101
|
{% endif %}
|
|
84
102
|
</ul>
|
|
85
103
|
</nav>
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
<meta name="robots" content="noindex">
|
|
21
21
|
{% endblock %}
|
|
22
22
|
|
|
23
|
+
{% import "partials/kicker.njk" as kicker with context %}
|
|
23
24
|
{% block content %}
|
|
24
25
|
<h1 class="page-title">Search</h1>
|
|
25
26
|
{% include "partials/search-form.njk" %}
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
<div class="search-results">
|
|
38
39
|
{% for post in posts %}
|
|
39
40
|
<article class="search-result h-entry">
|
|
41
|
+
{{ kicker.line(("Page" if post.type == "page" else post.postType) | capitalize, post.date) }}
|
|
40
42
|
{#- A post with no name of its own has none to claim, so its first words
|
|
41
43
|
link it without being marked up as one. -#}
|
|
42
44
|
<h2 class="feed-title{% if post.named %} p-name{% endif %}">
|
|
@@ -45,11 +47,6 @@
|
|
|
45
47
|
{% if post.snippet %}
|
|
46
48
|
<p class="search-snippet">{{ post.snippet | safe }}</p>
|
|
47
49
|
{% endif %}
|
|
48
|
-
{% if post.date %}
|
|
49
|
-
<p class="feed-meta">
|
|
50
|
-
<time class="feed-date dt-published" datetime="{{ post.date | date("iso") }}">{{ post.date | date }}</time>
|
|
51
|
-
</p>
|
|
52
|
-
{% endif %}
|
|
53
50
|
</article>
|
|
54
51
|
{% endfor %}
|
|
55
52
|
</div>
|
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
{{ feeds.feedLinks(tagRoot, site.title + ": " + tag) }}
|
|
10
10
|
{% endblock %}
|
|
11
11
|
|
|
12
|
+
{% import "partials/kicker.njk" as kicker with context %}
|
|
12
13
|
{% block content %}
|
|
13
|
-
|
|
14
|
+
{# The archive's header: the kicker says what kind of archive, the heading
|
|
15
|
+
whose. #}
|
|
16
|
+
<header class="archive-header">
|
|
17
|
+
{{ kicker.line("Tag") }}
|
|
18
|
+
<h1 class="page-title">{{ tag }}</h1>
|
|
19
|
+
</header>
|
|
14
20
|
{% include "partials/post-list.njk" %}
|
|
15
21
|
{% include "partials/pagination.njk" %}
|
|
16
22
|
{% endblock %}
|
|
@@ -24,13 +24,37 @@
|
|
|
24
24
|
#}
|
|
25
25
|
<section class="comment-respond" id="respond">
|
|
26
26
|
<h2 class="comment-reply-title">Leave a comment</h2>
|
|
27
|
+
{#- A site that takes webmentions says so here, where somebody deciding how
|
|
28
|
+
to answer is looking. -#}
|
|
29
|
+
{% if webmention %}
|
|
30
|
+
<p class="comment-help">Or reply from your own site and send a <a href="https://indieweb.org/Webmention">webmention</a>.</p>
|
|
31
|
+
{% endif %}
|
|
27
32
|
|
|
28
33
|
{% if commentNotice %}
|
|
29
34
|
<p class="comment-notice" role="status">{{ commentNotice }}</p>
|
|
30
35
|
{% endif %}
|
|
31
36
|
|
|
32
|
-
{
|
|
33
|
-
|
|
37
|
+
{#
|
|
38
|
+
A refused submission leads with this: what went wrong, and a link to each
|
|
39
|
+
field to put right. It is an alert and takes focus on load, so a screen
|
|
40
|
+
reader hears it at once and a keyboard starts from it, with no script. A
|
|
41
|
+
field it links to is marked `aria-invalid` and names its message with
|
|
42
|
+
`aria-describedby`, so the message is read with the field.
|
|
43
|
+
#}
|
|
44
|
+
{% set problems = commentForm.problems %}
|
|
45
|
+
{% if commentForm.error or problems.name or problems.email or problems.url or problems.body %}
|
|
46
|
+
<div class="comment-error" role="alert" tabindex="-1" autofocus aria-labelledby="comment-error-heading">
|
|
47
|
+
<h3 id="comment-error-heading">{{ commentForm.error or 'Your comment was not posted. Fix the fields marked below.' }}</h3>
|
|
48
|
+
{% if problems.name or problems.email or problems.url or problems.body %}
|
|
49
|
+
<ul>
|
|
50
|
+
{% for key in ['name', 'email', 'url', 'body'] %}
|
|
51
|
+
{% if problems[key] %}
|
|
52
|
+
<li><a href="#comment-{{ key }}">{{ problems[key] }}</a></li>
|
|
53
|
+
{% endif %}
|
|
54
|
+
{% endfor %}
|
|
55
|
+
</ul>
|
|
56
|
+
{% endif %}
|
|
57
|
+
</div>
|
|
34
58
|
{% endif %}
|
|
35
59
|
|
|
36
60
|
<form method="post" action="{{ commentForm.action }}">
|
|
@@ -81,36 +105,39 @@
|
|
|
81
105
|
<input id="comment-website" name="{{ commentForm.fields.trap }}" type="text" tabindex="-1" autocomplete="off" value="" />
|
|
82
106
|
</div>
|
|
83
107
|
|
|
108
|
+
{#- Name and email side by side on a wide screen. -#}
|
|
109
|
+
<div class="field-row">
|
|
84
110
|
<p class="comment-field">
|
|
85
111
|
<label for="comment-name">Name</label>
|
|
86
|
-
<input id="comment-name" name="{{ commentForm.fields.name }}" type="text" value="{{ commentForm.values.name }}" maxlength="{{ commentForm.nameLength }}" autocomplete="name" required />
|
|
87
|
-
{% if
|
|
88
|
-
<span class="comment-field-error">{{
|
|
112
|
+
<input id="comment-name" name="{{ commentForm.fields.name }}" type="text" value="{{ commentForm.values.name }}" maxlength="{{ commentForm.nameLength }}" autocomplete="name" required{% if problems.name %} aria-invalid="true" aria-describedby="comment-name-error"{% endif %} />
|
|
113
|
+
{% if problems.name %}
|
|
114
|
+
<span class="comment-field-error" id="comment-name-error">{{ problems.name }}</span>
|
|
89
115
|
{% endif %}
|
|
90
116
|
</p>
|
|
91
117
|
|
|
92
118
|
<p class="comment-field">
|
|
93
119
|
<label for="comment-email">Email <span class="comment-optional">(optional, never shown)</span></label>
|
|
94
|
-
<input id="comment-email" name="{{ commentForm.fields.email }}" type="email" value="{{ commentForm.values.email }}" autocomplete="email" />
|
|
95
|
-
{% if
|
|
96
|
-
<span class="comment-field-error">{{
|
|
120
|
+
<input id="comment-email" name="{{ commentForm.fields.email }}" type="email" value="{{ commentForm.values.email }}" autocomplete="email"{% if problems.email %} aria-invalid="true" aria-describedby="comment-email-error"{% endif %} />
|
|
121
|
+
{% if problems.email %}
|
|
122
|
+
<span class="comment-field-error" id="comment-email-error">{{ problems.email }}</span>
|
|
97
123
|
{% endif %}
|
|
98
124
|
</p>
|
|
125
|
+
</div>
|
|
99
126
|
|
|
100
127
|
<p class="comment-field">
|
|
101
128
|
<label for="comment-url">Your site <span class="comment-optional">(optional)</span></label>
|
|
102
|
-
<input id="comment-url" name="{{ commentForm.fields.url }}" type="text" inputmode="url" value="{{ commentForm.values.url }}" autocomplete="url" />
|
|
103
|
-
{% if
|
|
104
|
-
<span class="comment-field-error">{{
|
|
129
|
+
<input id="comment-url" name="{{ commentForm.fields.url }}" type="text" inputmode="url" value="{{ commentForm.values.url }}" autocomplete="url"{% if problems.url %} aria-invalid="true" aria-describedby="comment-url-error"{% endif %} />
|
|
130
|
+
{% if problems.url %}
|
|
131
|
+
<span class="comment-field-error" id="comment-url-error">{{ problems.url }}</span>
|
|
105
132
|
{% endif %}
|
|
106
133
|
</p>
|
|
107
134
|
{% endif %}
|
|
108
135
|
|
|
109
136
|
<p class="comment-field">
|
|
110
137
|
<label for="comment-body">Comment</label>
|
|
111
|
-
<textarea id="comment-body" name="{{ commentForm.fields.body }}" rows="6" maxlength="{{ commentForm.bodyLength }}" required>{{ commentForm.values.body }}</textarea>
|
|
112
|
-
{% if
|
|
113
|
-
<span class="comment-field-error">{{
|
|
138
|
+
<textarea id="comment-body" name="{{ commentForm.fields.body }}" rows="6" maxlength="{{ commentForm.bodyLength }}" required{% if problems.body %} aria-invalid="true" aria-describedby="comment-body-error"{% endif %}>{{ commentForm.values.body }}</textarea>
|
|
139
|
+
{% if problems.body %}
|
|
140
|
+
<span class="comment-field-error" id="comment-body-error">{{ problems.body }}</span>
|
|
114
141
|
{% endif %}
|
|
115
142
|
<span class="comment-hint">Markdown works. Links are marked <code>nofollow ugc</code>; HTML is not.</span>
|
|
116
143
|
</p>
|
|
@@ -29,8 +29,26 @@
|
|
|
29
29
|
{% else %}
|
|
30
30
|
<h2 class="contact-form-title">Send a message</h2>
|
|
31
31
|
|
|
32
|
-
{
|
|
33
|
-
|
|
32
|
+
{#
|
|
33
|
+
A refused submission leads with this, as the comment form does: what went
|
|
34
|
+
wrong, and a link to each field to put right. It is an alert and takes
|
|
35
|
+
focus on load; a field it links to is marked `aria-invalid` and names its
|
|
36
|
+
message with `aria-describedby`.
|
|
37
|
+
#}
|
|
38
|
+
{% set problems = contactForm.problems %}
|
|
39
|
+
{% if contactForm.error or problems.name or problems.email or problems.subject or problems.message %}
|
|
40
|
+
<div class="contact-error" role="alert" tabindex="-1" autofocus aria-labelledby="contact-error-heading">
|
|
41
|
+
<h3 id="contact-error-heading">{{ contactForm.error or 'Your message was not sent. Fix the fields marked below.' }}</h3>
|
|
42
|
+
{% if problems.name or problems.email or problems.subject or problems.message %}
|
|
43
|
+
<ul>
|
|
44
|
+
{% for key in ['name', 'email', 'subject', 'message'] %}
|
|
45
|
+
{% if problems[key] %}
|
|
46
|
+
<li><a href="#contact-{{ key }}">{{ problems[key] }}</a></li>
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% endfor %}
|
|
49
|
+
</ul>
|
|
50
|
+
{% endif %}
|
|
51
|
+
</div>
|
|
34
52
|
{% endif %}
|
|
35
53
|
|
|
36
54
|
<form method="post" action="{{ contactForm.action }}">
|
|
@@ -47,36 +65,39 @@
|
|
|
47
65
|
<input id="contact-website" name="{{ contactForm.fields.trap }}" type="text" tabindex="-1" autocomplete="off" value="" />
|
|
48
66
|
</div>
|
|
49
67
|
|
|
68
|
+
{#- Name and email side by side on a wide screen. -#}
|
|
69
|
+
<div class="field-row">
|
|
50
70
|
<p class="contact-field">
|
|
51
71
|
<label for="contact-name">Name</label>
|
|
52
|
-
<input id="contact-name" name="{{ contactForm.fields.name }}" type="text" value="{{ contactForm.values.name }}" maxlength="{{ contactForm.nameLength }}" autocomplete="name" required />
|
|
53
|
-
{% if
|
|
54
|
-
<span class="contact-field-error">{{
|
|
72
|
+
<input id="contact-name" name="{{ contactForm.fields.name }}" type="text" value="{{ contactForm.values.name }}" maxlength="{{ contactForm.nameLength }}" autocomplete="name" required{% if problems.name %} aria-invalid="true" aria-describedby="contact-name-error"{% endif %} />
|
|
73
|
+
{% if problems.name %}
|
|
74
|
+
<span class="contact-field-error" id="contact-name-error">{{ problems.name }}</span>
|
|
55
75
|
{% endif %}
|
|
56
76
|
</p>
|
|
57
77
|
|
|
58
78
|
<p class="contact-field">
|
|
59
79
|
<label for="contact-email">Email</label>
|
|
60
|
-
<input id="contact-email" name="{{ contactForm.fields.email }}" type="email" value="{{ contactForm.values.email }}" autocomplete="email" required />
|
|
61
|
-
{% if
|
|
62
|
-
<span class="contact-field-error">{{
|
|
80
|
+
<input id="contact-email" name="{{ contactForm.fields.email }}" type="email" value="{{ contactForm.values.email }}" autocomplete="email" required{% if problems.email %} aria-invalid="true" aria-describedby="contact-email-error"{% endif %} />
|
|
81
|
+
{% if problems.email %}
|
|
82
|
+
<span class="contact-field-error" id="contact-email-error">{{ problems.email }}</span>
|
|
63
83
|
{% endif %}
|
|
64
84
|
<span class="contact-hint">So there is somewhere to reply to. It is never shown on the site.</span>
|
|
65
85
|
</p>
|
|
86
|
+
</div>
|
|
66
87
|
|
|
67
88
|
<p class="contact-field">
|
|
68
89
|
<label for="contact-subject">Subject <span class="contact-optional">(optional)</span></label>
|
|
69
|
-
<input id="contact-subject" name="{{ contactForm.fields.subject }}" type="text" value="{{ contactForm.values.subject }}" maxlength="{{ contactForm.subjectLength }}" />
|
|
70
|
-
{% if
|
|
71
|
-
<span class="contact-field-error">{{
|
|
90
|
+
<input id="contact-subject" name="{{ contactForm.fields.subject }}" type="text" value="{{ contactForm.values.subject }}" maxlength="{{ contactForm.subjectLength }}"{% if problems.subject %} aria-invalid="true" aria-describedby="contact-subject-error"{% endif %} />
|
|
91
|
+
{% if problems.subject %}
|
|
92
|
+
<span class="contact-field-error" id="contact-subject-error">{{ problems.subject }}</span>
|
|
72
93
|
{% endif %}
|
|
73
94
|
</p>
|
|
74
95
|
|
|
75
96
|
<p class="contact-field">
|
|
76
97
|
<label for="contact-message">Message</label>
|
|
77
|
-
<textarea id="contact-message" name="{{ contactForm.fields.message }}" rows="8" maxlength="{{ contactForm.messageLength }}" required>{{ contactForm.values.message }}</textarea>
|
|
78
|
-
{% if
|
|
79
|
-
<span class="contact-field-error">{{
|
|
98
|
+
<textarea id="contact-message" name="{{ contactForm.fields.message }}" rows="8" maxlength="{{ contactForm.messageLength }}" required{% if problems.message %} aria-invalid="true" aria-describedby="contact-message-error"{% endif %}>{{ contactForm.values.message }}</textarea>
|
|
99
|
+
{% if problems.message %}
|
|
100
|
+
<span class="contact-field-error" id="contact-message-error">{{ problems.message }}</span>
|
|
80
101
|
{% endif %}
|
|
81
102
|
</p>
|
|
82
103
|
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{% macro group(items, label, kind, icon, source=false) %}
|
|
43
43
|
{% if items.length %}
|
|
44
44
|
<div class="reaction-group {{ kind }}">
|
|
45
|
-
<h2 class="reaction-title">{{ label }}
|
|
45
|
+
<h2 class="reaction-title">{{ label }} <span class="reaction-count">{{ items.length }}</span></h2>
|
|
46
46
|
<div class="facepile">
|
|
47
47
|
{%- for item in items %}{{ face(item, icon, (item.url or item.author.url) if source else (item.author.url or item.url)) }}{% endfor %}
|
|
48
48
|
</div>
|
|
@@ -61,6 +61,8 @@
|
|
|
61
61
|
<div class="comment-author vcard p-author h-card">
|
|
62
62
|
{%- if reply.author.avatar %}
|
|
63
63
|
<img class="avatar u-photo" src="{{ reply.author.avatar }}" alt="" width="40" height="40" loading="lazy">
|
|
64
|
+
{%- else %}
|
|
65
|
+
<span class="comment-avatar-empty" aria-hidden="true">{{ reply.author.name | first | upper }}</span>
|
|
64
66
|
{%- endif %}
|
|
65
67
|
<b class="fn p-name">{% if reply.author.url %}<a class="url u-url" href="{{ reply.author.url }}" rel="nofollow ugc noopener noreferrer">{{ reply.author.name }}</a>{% else %}{{ reply.author.name }}{% endif %}</b>
|
|
66
68
|
</div>
|
|
@@ -70,6 +72,11 @@
|
|
|
70
72
|
{%- else %}
|
|
71
73
|
<time class="dt-published" datetime="{{ reply.published | date("iso") }}">{{ reply.published | date }}</time>
|
|
72
74
|
{%- endif %}
|
|
75
|
+
{%- if reply.source == "webmention" %}
|
|
76
|
+
<span class="comment-source">via webmention</span>
|
|
77
|
+
{%- elif reply.source == "activitypub" %}
|
|
78
|
+
<span class="comment-source">via the fediverse</span>
|
|
79
|
+
{%- endif %}
|
|
73
80
|
</div>
|
|
74
81
|
</footer>
|
|
75
82
|
<div class="comment-content e-content">
|
|
@@ -111,8 +118,8 @@
|
|
|
111
118
|
|
|
112
119
|
{#
|
|
113
120
|
And the thread. The title counts the whole of it rather than the top level,
|
|
114
|
-
which is what `counts.replies` is
|
|
115
|
-
|
|
121
|
+
which is what `counts.replies` is. It does not quote the post's title: the
|
|
122
|
+
post is the page it sits under, and a note has no title to quote (TASK-186).
|
|
116
123
|
|
|
117
124
|
"Comments are closed." is printed under a post that has been answered and is
|
|
118
125
|
no longer taking comments. That a post is closed is what the absence of
|
|
@@ -123,7 +130,7 @@
|
|
|
123
130
|
{% if conversation.replies.length %}
|
|
124
131
|
<div id="comments" class="comments-area">
|
|
125
132
|
<h2 class="comments-title">
|
|
126
|
-
{%- if conversation.counts.replies == 1 %}One
|
|
133
|
+
{%- if conversation.counts.replies == 1 %}One reply{% else %}{{ conversation.counts.replies }} replies{% endif -%}
|
|
127
134
|
</h2>
|
|
128
135
|
|
|
129
136
|
<ol class="comment-list">
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{#
|
|
2
|
+
The kicker: what an entry is, said once, small, above it (doc-9). The kind
|
|
3
|
+
word first, then the date, then the categories, each after a middle dot the
|
|
4
|
+
screen reader skips.
|
|
5
|
+
|
|
6
|
+
Import it with the context, because the category links read the archive
|
|
7
|
+
base off `site`:
|
|
8
|
+
|
|
9
|
+
{% import "partials/kicker.njk" as kicker with context %}
|
|
10
|
+
{{ kicker.line("Note", post.date, post.url) }}
|
|
11
|
+
|
|
12
|
+
`kind` is the word printed: Article, Note or Reply for a post (its
|
|
13
|
+
`postType`, capitalised), Page for a page, and whatever names an archive or
|
|
14
|
+
an error page. `date` is the entry's `dt-published`, printed long. With
|
|
15
|
+
`href` the date is the entry's `u-url`, which is how a feed links an entry
|
|
16
|
+
that has no title to link; an entry with no date links `label` instead.
|
|
17
|
+
`categories` are printed as `p-category` links.
|
|
18
|
+
#}
|
|
19
|
+
{% macro line(kind, date=false, href=false, categories=[], label="") %}
|
|
20
|
+
<p class="kicker">
|
|
21
|
+
<span class="kicker-kind">{{ kind }}</span>
|
|
22
|
+
{%- if href %}
|
|
23
|
+
<span class="kicker-dot" aria-hidden="true">·</span>
|
|
24
|
+
<a href="{{ href | url }}" class="u-url">{% if date %}<time class="feed-date dt-published" datetime="{{ date | date("iso") }}">{{ date | date }}</time>{% else %}{{ label }}{% endif %}</a>
|
|
25
|
+
{%- elif date %}
|
|
26
|
+
<span class="kicker-dot" aria-hidden="true">·</span>
|
|
27
|
+
<time class="feed-date dt-published" datetime="{{ date | date("iso") }}">{{ date | date }}</time>
|
|
28
|
+
{%- endif %}
|
|
29
|
+
{%- for category in categories %}
|
|
30
|
+
<span class="kicker-dot" aria-hidden="true">·</span>
|
|
31
|
+
<a href="{{ ("/" + (site.categoryBase or "category") + "/" + (category | urlencode) + "/") | url }}" class="p-category" rel="category">{{ category }}</a>
|
|
32
|
+
{%- endfor %}
|
|
33
|
+
</p>
|
|
34
|
+
{% endmacro %}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{#
|
|
2
|
-
The feed a listing is made of: `h-feed` of `h-entry` items
|
|
3
|
-
design lists them (decision-16).
|
|
2
|
+
The feed a listing is made of: `h-feed` of `h-entry` items (doc-9).
|
|
4
3
|
|
|
5
|
-
Expects `posts`.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
Expects `posts`. Every entry opens on a kicker naming its kind and its date.
|
|
5
|
+
An article is the only entry with a headline: its title as a linked
|
|
6
|
+
`p-name`, its `summary` (the very line the feeds publish) as a `p-summary`
|
|
7
|
+
cut to about 280 characters, and a Continue reading link, with the
|
|
8
|
+
categories it is filed under in its kicker. A note, which has no title, is
|
|
9
|
+
printed whole as its `e-content`, and its date in the kicker is the link to
|
|
10
|
+
it. A reply is a note that first cites what it answers, and a reply with a
|
|
11
|
+
title is an article that does. Tags are deliberately not here: an entry's
|
|
12
|
+
tags belong under the entry, and a feed of twenty posts listing everyone's
|
|
13
|
+
would be a wall of links rather than a list of writing.
|
|
12
14
|
|
|
13
15
|
`feedHeading` is the heading level, which belongs to the layout rather than
|
|
14
16
|
to the list: a listing is a page of entries and heads each one at 2, and a
|
|
15
17
|
front page whose feed sits under a heading of its own sets 3 so the outline
|
|
16
18
|
never skips a level.
|
|
17
19
|
#}
|
|
18
|
-
{% import "partials/
|
|
20
|
+
{% import "partials/kicker.njk" as kicker with context %}
|
|
19
21
|
{% set feedTitleTag = "h" + (feedHeading | default(2)) %}
|
|
20
22
|
{% if posts.length %}
|
|
21
23
|
<div class="feed h-feed">
|
|
@@ -24,43 +26,35 @@
|
|
|
24
26
|
<hr class="feed-separator">
|
|
25
27
|
{% endif %}
|
|
26
28
|
<article class="feed-item h-entry">
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
{% if not post.named %}
|
|
51
|
-
<p>
|
|
52
|
-
<a href="{{ post.url | url }}" class="u-url">{% if post.date %}<time class="feed-date dt-published" datetime="{{ post.date | date("iso") }}">{{ post.date | date }}</time>{% else %}{{ post.label }}{% endif %}</a>
|
|
53
|
-
</p>
|
|
54
|
-
{% elif post.date %}
|
|
55
|
-
<p>
|
|
56
|
-
<time class="feed-date dt-published" datetime="{{ post.date | date("iso") }}">
|
|
57
|
-
{{ post.date | date }}
|
|
58
|
-
</time>
|
|
59
|
-
</p>
|
|
60
|
-
{% endif %}
|
|
61
|
-
{{ taxonomy.categories(post.categories) }}
|
|
62
|
-
</div>
|
|
29
|
+
{% if post.named %}
|
|
30
|
+
{{ kicker.line(post.postType | capitalize, post.date, false, post.categories) }}
|
|
31
|
+
{% else %}
|
|
32
|
+
{{ kicker.line(post.postType | capitalize, post.date, post.url, [], post.label) }}
|
|
33
|
+
{% endif %}
|
|
34
|
+
{#- What a reply answers, from the same partial an entry cites it with. -#}
|
|
35
|
+
{% if post.inReplyTo %}
|
|
36
|
+
{% set inReplyTo = post.inReplyTo %}
|
|
37
|
+
{% set replyContext = post.replyContext %}
|
|
38
|
+
{% include "partials/reply-context.njk" %}
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% if not post.named %}
|
|
41
|
+
<div class="feed-excerpt e-content">{{ post.content | safe }}</div>
|
|
42
|
+
{% else %}
|
|
43
|
+
<{{ feedTitleTag }} class="feed-title p-name">
|
|
44
|
+
<a href="{{ post.url | url }}" class="u-url">{{ post.title }}</a>
|
|
45
|
+
</{{ feedTitleTag }}>
|
|
46
|
+
{% if post.summary %}
|
|
47
|
+
<div class="feed-excerpt p-summary">
|
|
48
|
+
{#- `truncate` cuts at the last space before the length and adds an
|
|
49
|
+
ellipsis, so a description short enough is printed whole and a long
|
|
50
|
+
one never breaks a word. -#}
|
|
51
|
+
<p>{{ post.summary | truncate(280) }}</p>
|
|
63
52
|
</div>
|
|
53
|
+
{% endif %}
|
|
54
|
+
<p class="feed-more">
|
|
55
|
+
<a href="{{ post.url | url }}" aria-label="Continue reading: {{ post.title }}">Continue reading<span aria-hidden="true"> →</span></a>
|
|
56
|
+
</p>
|
|
57
|
+
{% endif %}
|
|
64
58
|
</article>
|
|
65
59
|
{% endfor %}
|
|
66
60
|
</div>
|
|
@@ -3,15 +3,22 @@
|
|
|
3
3
|
`u-in-reply-to h-cite` that always carries the target's URL, so a parser
|
|
4
4
|
reads what this post answers even when nothing more is known about it.
|
|
5
5
|
|
|
6
|
+
Drawn as the citation block (doc-9): a rule on the start side, a sans line
|
|
7
|
+
naming the target, and its words as an italic quote. An entry includes it
|
|
8
|
+
with `inReplyTo` and `replyContext` on the context; a feed sets both from
|
|
9
|
+
the entry it is printing.
|
|
10
|
+
|
|
6
11
|
`replyContext` is what the target said about itself when the reply was saved
|
|
7
12
|
or synced: its name, a short excerpt, its author and its date, each only when
|
|
8
13
|
it said so. It is somebody else's words, so every piece is printed through
|
|
9
|
-
autoescaping and none of it with `safe`.
|
|
14
|
+
autoescaping and none of it with `safe`. With a name, the name is the link;
|
|
15
|
+
with only an author, "a post" is; with neither, the bare URL.
|
|
10
16
|
#}
|
|
11
17
|
{% if inReplyTo %}
|
|
12
|
-
<div class="reply-context u-in-reply-to h-cite">
|
|
13
|
-
<p class="small">In reply to
|
|
18
|
+
<div class="reply-context cite u-in-reply-to h-cite">
|
|
19
|
+
<p class="cite-line small">In reply to
|
|
14
20
|
{%- if replyContext.name %} <a class="u-url p-name" href="{{ inReplyTo }}">{{ replyContext.name }}</a>
|
|
21
|
+
{%- elif replyContext.author %} <a class="u-url" href="{{ inReplyTo }}">a post</a>
|
|
15
22
|
{%- else %} <a class="u-url" href="{{ inReplyTo }}">{{ inReplyTo }}</a>
|
|
16
23
|
{%- endif %}
|
|
17
24
|
{%- if replyContext.author %} by <span class="p-author h-card">
|
|
@@ -19,11 +26,11 @@
|
|
|
19
26
|
{%- else %}<span class="p-name">{{ replyContext.author.name }}</span>{% endif -%}
|
|
20
27
|
</span>
|
|
21
28
|
{%- endif %}
|
|
22
|
-
{%- if replyContext.published %}
|
|
29
|
+
{%- if replyContext.published %}, <time class="dt-published" datetime="{{ replyContext.published | date("iso") }}">{{ replyContext.published | date }}</time>
|
|
23
30
|
{%- endif -%}
|
|
24
31
|
</p>
|
|
25
32
|
{% if replyContext.text %}
|
|
26
|
-
<blockquote class="p-content">{{ replyContext.text }}</blockquote>
|
|
33
|
+
<blockquote class="cite-quote p-content">{{ replyContext.text }}</blockquote>
|
|
27
34
|
{% endif %}
|
|
28
35
|
</div>
|
|
29
36
|
{% endif %}
|