@geekity/cms 0.9.0 → 0.10.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.
Files changed (145) hide show
  1. package/README.md +71 -21
  2. package/admin/components/admin-bar.njk +26 -0
  3. package/admin/components/public-admin-bar.njk +41 -0
  4. package/admin/layouts/base.njk +1 -0
  5. package/admin/layouts/shell.njk +8 -19
  6. package/admin/static/admin-bar.css +137 -0
  7. package/admin/static/admin.css +1 -102
  8. package/dist/admin/comments.js +2 -1
  9. package/dist/admin/comments.js.map +1 -1
  10. package/dist/admin/index.d.ts +1 -1
  11. package/dist/admin/index.d.ts.map +1 -1
  12. package/dist/admin/index.js.map +1 -1
  13. package/dist/admin/personal-data.d.ts.map +1 -1
  14. package/dist/admin/personal-data.js +5 -1
  15. package/dist/admin/personal-data.js.map +1 -1
  16. package/dist/admin/preview.d.ts.map +1 -1
  17. package/dist/admin/preview.js +1 -1
  18. package/dist/admin/preview.js.map +1 -1
  19. package/dist/admin/routes.d.ts.map +1 -1
  20. package/dist/admin/routes.js +2 -1
  21. package/dist/admin/routes.js.map +1 -1
  22. package/dist/admin/store.d.ts +35 -0
  23. package/dist/admin/store.d.ts.map +1 -1
  24. package/dist/admin/store.js +62 -0
  25. package/dist/admin/store.js.map +1 -1
  26. package/dist/admin/templates.d.ts +1 -0
  27. package/dist/admin/templates.d.ts.map +1 -1
  28. package/dist/admin/templates.js +1 -0
  29. package/dist/admin/templates.js.map +1 -1
  30. package/dist/admin/tools.d.ts +2 -0
  31. package/dist/admin/tools.d.ts.map +1 -1
  32. package/dist/admin/tools.js +3 -2
  33. package/dist/admin/tools.js.map +1 -1
  34. package/dist/comments/index.d.ts +2 -2
  35. package/dist/comments/index.d.ts.map +1 -1
  36. package/dist/comments/index.js +1 -1
  37. package/dist/comments/index.js.map +1 -1
  38. package/dist/comments/records.d.ts +43 -2
  39. package/dist/comments/records.d.ts.map +1 -1
  40. package/dist/comments/records.js +201 -54
  41. package/dist/comments/records.js.map +1 -1
  42. package/dist/comments/routes.js +1 -1
  43. package/dist/comments/routes.js.map +1 -1
  44. package/dist/comments/viewer.d.ts +13 -0
  45. package/dist/comments/viewer.d.ts.map +1 -1
  46. package/dist/comments/viewer.js +13 -1
  47. package/dist/comments/viewer.js.map +1 -1
  48. package/dist/config.d.ts +11 -0
  49. package/dist/config.d.ts.map +1 -1
  50. package/dist/config.js +1 -0
  51. package/dist/config.js.map +1 -1
  52. package/dist/env.d.ts +19 -0
  53. package/dist/env.d.ts.map +1 -1
  54. package/dist/federation/delivery.d.ts +3 -1
  55. package/dist/federation/delivery.d.ts.map +1 -1
  56. package/dist/federation/delivery.js +6 -1
  57. package/dist/federation/delivery.js.map +1 -1
  58. package/dist/federation/federation.d.ts +3 -0
  59. package/dist/federation/federation.d.ts.map +1 -1
  60. package/dist/federation/federation.js.map +1 -1
  61. package/dist/federation/inbox.d.ts +7 -5
  62. package/dist/federation/inbox.d.ts.map +1 -1
  63. package/dist/federation/inbox.js +15 -32
  64. package/dist/federation/inbox.js.map +1 -1
  65. package/dist/federation/index.d.ts +2 -0
  66. package/dist/federation/index.d.ts.map +1 -1
  67. package/dist/federation/index.js +1 -0
  68. package/dist/federation/index.js.map +1 -1
  69. package/dist/federation/mount.js +6 -1
  70. package/dist/federation/mount.js.map +1 -1
  71. package/dist/federation/profiles.d.ts +95 -0
  72. package/dist/federation/profiles.d.ts.map +1 -0
  73. package/dist/federation/profiles.js +171 -0
  74. package/dist/federation/profiles.js.map +1 -0
  75. package/dist/federation/relays.d.ts +3 -1
  76. package/dist/federation/relays.d.ts.map +1 -1
  77. package/dist/federation/relays.js +6 -1
  78. package/dist/federation/relays.js.map +1 -1
  79. package/dist/federation/replies.d.ts +13 -4
  80. package/dist/federation/replies.d.ts.map +1 -1
  81. package/dist/federation/replies.js +25 -6
  82. package/dist/federation/replies.js.map +1 -1
  83. package/dist/images/icons.d.ts.map +1 -1
  84. package/dist/images/icons.js +3 -2
  85. package/dist/images/icons.js.map +1 -1
  86. package/dist/images/index.d.ts +2 -2
  87. package/dist/images/index.d.ts.map +1 -1
  88. package/dist/images/markup.d.ts +15 -4
  89. package/dist/images/markup.d.ts.map +1 -1
  90. package/dist/images/markup.js +24 -15
  91. package/dist/images/markup.js.map +1 -1
  92. package/dist/images/paths.d.ts +11 -0
  93. package/dist/images/paths.d.ts.map +1 -1
  94. package/dist/images/paths.js +17 -1
  95. package/dist/images/paths.js.map +1 -1
  96. package/dist/images/variants.d.ts +13 -3
  97. package/dist/images/variants.d.ts.map +1 -1
  98. package/dist/images/variants.js +13 -11
  99. package/dist/images/variants.js.map +1 -1
  100. package/dist/index.d.ts +11 -5
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +50 -11
  103. package/dist/index.js.map +1 -1
  104. package/dist/notifications/routes.js +1 -1
  105. package/dist/notifications/routes.js.map +1 -1
  106. package/dist/privacy/erase.d.ts.map +1 -1
  107. package/dist/privacy/erase.js +2 -3
  108. package/dist/privacy/erase.js.map +1 -1
  109. package/dist/privacy/retention.js +1 -1
  110. package/dist/privacy/retention.js.map +1 -1
  111. package/dist/web/admin-bar.d.ts +21 -0
  112. package/dist/web/admin-bar.d.ts.map +1 -0
  113. package/dist/web/admin-bar.js +145 -0
  114. package/dist/web/admin-bar.js.map +1 -0
  115. package/dist/web/assets.d.ts +29 -1
  116. package/dist/web/assets.d.ts.map +1 -1
  117. package/dist/web/assets.js +48 -3
  118. package/dist/web/assets.js.map +1 -1
  119. package/dist/web/compression.d.ts +19 -0
  120. package/dist/web/compression.d.ts.map +1 -0
  121. package/dist/web/compression.js +160 -0
  122. package/dist/web/compression.js.map +1 -0
  123. package/dist/web/context.d.ts +5 -1
  124. package/dist/web/context.d.ts.map +1 -1
  125. package/dist/web/context.js +5 -2
  126. package/dist/web/context.js.map +1 -1
  127. package/dist/web/conversation.d.ts.map +1 -1
  128. package/dist/web/conversation.js +13 -12
  129. package/dist/web/conversation.js.map +1 -1
  130. package/dist/web/render.d.ts +27 -0
  131. package/dist/web/render.d.ts.map +1 -1
  132. package/dist/web/render.js +77 -38
  133. package/dist/web/render.js.map +1 -1
  134. package/dist/web/routes.d.ts.map +1 -1
  135. package/dist/web/routes.js +63 -28
  136. package/dist/web/routes.js.map +1 -1
  137. package/dist/web/templates.d.ts.map +1 -1
  138. package/dist/web/templates.js +9 -2
  139. package/dist/web/templates.js.map +1 -1
  140. package/dist/webmention/service.d.ts.map +1 -1
  141. package/dist/webmention/service.js +5 -1
  142. package/dist/webmention/service.js.map +1 -1
  143. package/package.json +1 -1
  144. package/themes/default/README.md +143 -19
  145. package/themes/default/layouts/base.njk +51 -10
@@ -93,7 +93,11 @@ reason rather than quietly left out.
93
93
  Resolution order for any template is then that theme first, this directory
94
94
  second, one file at a time. The theme above replaces the post layout and keeps
95
95
  receiving updates to every other template, and `static/style.css` replaces the
96
- stylesheet the same way — assets under `/theme/` resolve in the same order. A
96
+ stylesheet the same way — assets under `/theme/` resolve in the same order.
97
+ Link a theme file with the `asset` filter rather than a literal `/theme/`
98
+ path: `{{ "style.css" | asset }}` puts a hash of the file in the URL, so a
99
+ reader's browser keeps it for a year and still fetches the new one the moment
100
+ it changes. The plain `/theme/style.css` keeps working, cached for an hour. A
97
101
  site that has chosen no theme reads this one and nothing else, and a theme
98
102
  sitting in `themes/` that the setting does not name is never on the path at
99
103
  all. A `theme` naming a folder that is not there, or is not a theme, falls back
@@ -117,8 +121,9 @@ packaged ones by name:
117
121
  {% endblock %}
118
122
  ```
119
123
 
120
- `layouts/base.njk` defines the blocks `title`, `head`, `alternates`, `header`,
121
- `content`, `footer` and `scripts`, so most sites never have to copy it.
124
+ `layouts/base.njk` defines the blocks `title`, `speculationRules`,
125
+ `viewTransitions`, `head`, `alternates`, `header`, `content`, `footer` and
126
+ `scripts`, so most sites never have to copy it.
122
127
 
123
128
  Two layouts are named for the pages the Reading settings pick:
124
129
 
@@ -188,6 +193,71 @@ block:
188
193
  {% endblock %}
189
194
  ```
190
195
 
196
+ ### The admin bar
197
+
198
+ When somebody signed in to the admin reads the site, the CMS puts the admin bar
199
+ across the top of every HTML page: the site title linking to the dashboard,
200
+ **+ New**, **Edit Post** or **Edit Page** on a post or a page, and the account
201
+ menu. A theme draws none of it and needs no template for it. The CMS inserts a
202
+ `<geekity-admin-bar>` element straight after the page's `<body>` tag, and an
203
+ anonymous reader's page is unchanged, byte for byte.
204
+
205
+ The bar keeps its markup and its stylesheet in a shadow root, so a theme's CSS
206
+ cannot restyle it and its CSS cannot restyle the theme. The element carries an
207
+ inline style that resets it and fixes it to the top of the window, full width,
208
+ above the page. Padding or margins on `<body>`, and rules such as `body > *` or
209
+ a grid on `<body>`, do not move or resize it. It stays in place while the page
210
+ scrolls.
211
+
212
+ The bar takes no room by itself, so the CMS makes room for it from outside. On
213
+ a page that carries the bar:
214
+
215
+ - `<html>` has the class `geekity-admin-bar`.
216
+ - A stylesheet in `<head>` declares `--geekity-admin-bar-height` on `:root`
217
+ and sets `margin-top: var(--geekity-admin-bar-height) !important` on
218
+ `<html>`, which pushes the whole page down below the bar. The height is
219
+ 40px, or 80px on a screen 600px wide or less, where the bar can wrap onto a
220
+ second line. A script in the bar then measures it and sets the real height
221
+ on `<html>`, so the push matches the bar at any width, and is 0 when a theme
222
+ hides the bar.
223
+ - The same stylesheet sets `scroll-padding-top` on `<html>` to the bar's
224
+ height, so a link to an anchor on the page, such as a skip link or a
225
+ footnote, scrolls its target to below the bar. It is declared with no
226
+ specificity, so a theme that sets its own `scroll-padding-top` keeps it.
227
+
228
+ A theme with a sticky or fixed header of its own offsets it by the bar's
229
+ height. The fallback of `0` applies on every page the bar is not on:
230
+
231
+ ```css
232
+ .site-header {
233
+ position: sticky;
234
+ top: var(--geekity-admin-bar-height, 0);
235
+ }
236
+ ```
237
+
238
+ A theme that sets its own `scroll-padding-top` adds the bar to it the same
239
+ way, for example `calc(var(--geekity-admin-bar-height, 0px) + 4rem)`. Use the
240
+ `geekity-admin-bar` class for any other change, for example
241
+ `.geekity-admin-bar .site-header { ... }`.
242
+
243
+ A theme that must not show the bar can hide it:
244
+
245
+ ```css
246
+ geekity-admin-bar {
247
+ display: none !important;
248
+ }
249
+ ```
250
+
251
+ The `!important` is needed because it has to beat the element's inline style.
252
+ With script on, the bar then measures 0 and the page moves back up. For a
253
+ reader without script, the theme also removes the margin:
254
+
255
+ ```css
256
+ html.geekity-admin-bar {
257
+ margin-top: 0 !important;
258
+ }
259
+ ```
260
+
191
261
  ### An entry
192
262
 
193
263
  `layouts/post.njk` and `layouts/page.njk` draw the same thing, the source
@@ -541,6 +611,59 @@ A site that wants a different graph — more types, an `Organization` publisher,
541
611
  nothing at all — writes its own `partials/jsonld.njk` and that file replaces
542
612
  this one, like any other partial.
543
613
 
614
+ ### Prefetching and page transitions
615
+
616
+ Two tags near the top of `<head>` make moving between pages feel faster. Both
617
+ are progressive enhancement: a browser that does not support one ignores it.
618
+
619
+ **Speculation rules.** The `speculationRules` block prints one `<script
620
+ type="speculationrules">` that asks the browser to prefetch a page the reader
621
+ is about to open. It covers links to this site's own pages, at `moderate`
622
+ eagerness, which in Chrome is a hover of about 200 milliseconds or the start of
623
+ a tap. It never prefetches:
624
+
625
+ - `/admin` and anything under `/admin/`, which includes `/admin/logout`.
626
+ - Any path with `logout` in it.
627
+ - A feed: `/feed/`, `/feed/atom/`, `/feed/json/`, and any other path that has
628
+ a `/feed/` segment, such as a tag's feed or a post's comments feed.
629
+ - A file under `/uploads/`.
630
+ - A URL with a query, such as `/search/?q=words`.
631
+
632
+ Every pattern is written through the `url` filter, so a site served from a
633
+ subdirectory prefetches only its own pages.
634
+
635
+ **No rules reach a signed-in reader.** The CMS removes every `<script
636
+ type="speculationrules">` from a page it draws for somebody signed in, the same
637
+ page that carries [the admin bar](#the-admin-bar). That page is drawn for one
638
+ person and never stored, so a prefetch of it is wasted work. A site theme that
639
+ writes its own rules does not have to check who is reading.
640
+
641
+ **View transitions.** The `viewTransitions` block prints a `<style>` that opts
642
+ into cross-document view transitions. The browser cross-fades from one page of
643
+ the site to the next, with no JavaScript. The rule is inside `@media
644
+ (prefers-reduced-motion: no-preference)`, so a reader who asks for reduced
645
+ motion gets no transition:
646
+
647
+ ```css
648
+ @media (prefers-reduced-motion: no-preference) {
649
+ @view-transition {
650
+ navigation: auto;
651
+ }
652
+ }
653
+ ```
654
+
655
+ A site theme that wants a different animation adds `::view-transition-*` rules
656
+ to its own stylesheet.
657
+
658
+ **Turning either off.** Override its block with nothing in it:
659
+
660
+ ```njk
661
+ {% extends "layouts/base.njk" %}
662
+
663
+ {% block speculationRules %}{% endblock %}
664
+ {% block viewTransitions %}{% endblock %}
665
+ ```
666
+
544
667
  ### Colours
545
668
 
546
669
  `static/style.css` is the source design: a serif body and sans headings at an
@@ -1100,22 +1223,22 @@ the thread, because it is not an answer.
1100
1223
 
1101
1224
  Each entry — a reply, a like or a boost — is:
1102
1225
 
1103
- | Key | What it holds |
1104
- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1105
- | `id` | What it is called: a reply's own note id, which is what an answer to it names. |
1106
- | `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here. |
1107
- | `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`. A `repost` comes from a webmention; a `mention` is a webmention or an approved fediverse quote. |
1108
- | `author.name` | The best name available: their display name, else their handle, else their id. For a webmention, the source's `h-card` name, else its host. |
1109
- | `author.handle` | `@user@host`, or `null`. Taken from the follower profile the site holds, else guessed from the actor URL. `null` for a native comment. |
1110
- | `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link. |
1111
- | `author.avatar` | Their avatar, or `null`. The site knows one for an actor that follows it and for a webmention whose `h-card` carried a `u-photo`. Always a same-origin `/_geekity/avatars/…` path, never the remote URL: see [Avatars](#avatars). |
1112
- | `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment. |
1113
- | `url` | Where it can be read: the remote note's `url` for a fediverse reply or quote, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment. |
1114
- | `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost. |
1115
- | `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter. |
1116
- | `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction. |
1117
- | `status` | `"published"`. On the record for the sources that moderate. |
1118
- | `replies` | The replies to this one, oldest first, nested as deep as the site has seen. |
1226
+ | Key | What it holds |
1227
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1228
+ | `id` | What it is called: a reply's own note id, which is what an answer to it names. |
1229
+ | `source` | `"activitypub"` for a fediverse reply, `"comment"` for one left on the page, `"webmention"` for another page linking here. |
1230
+ | `kind` | `"reply"`, `"like"`, `"boost"`, `"repost"` or `"mention"`. A `repost` comes from a webmention; a `mention` is a webmention or an approved fediverse quote. |
1231
+ | `author.name` | The best name available: their display name, else their handle, else their server, else their id. For a webmention, the source's `h-card` name, else its host. |
1232
+ | `author.handle` | `@user@host`, or `null`. Taken from the follower or actor profile the site holds, else guessed from the actor URL unless it ends in a number. `null` for a native comment. |
1233
+ | `author.url` | Their profile page, the website a commenter typed, or a webmention author's `u-url`. May be `null`, so guard the link. |
1234
+ | `author.avatar` | Their avatar, or `null`. The site knows one for a fediverse actor whose profile it holds and for a webmention whose `h-card` carried a `u-photo`. Always a same-origin `/_geekity/avatars/…` path, never the remote URL: see [Avatars](#avatars). |
1235
+ | `author.actorId` | Their id, which is what identifies them however they are named. `null` for a native comment. |
1236
+ | `url` | Where it can be read: the remote note's `url` for a fediverse reply or quote, the source page for a webmention, and `{permalink}#comment-{id}` — this page's own anchor — for a native comment. |
1237
+ | `content` | What it says, **already sanitised**, so print it with `\| safe`. Empty for a like or a boost. |
1238
+ | `published` | A `Date`: when it was published, or when it arrived if it did not say. Use the `date` filter. |
1239
+ | `inReplyTo` | What it answers — the post's ActivityPub id, or another reply's — and `null` for a reaction. |
1240
+ | `status` | `"published"`. On the record for the sources that moderate. |
1241
+ | `replies` | The replies to this one, oldest first, nested as deep as the site has seen. |
1119
1242
 
1120
1243
  Three rules decide what is in the thread, and they are the CMS's rather than a
1121
1244
  theme's: a reply whose author deleted it is gone, and its own answers move up to
@@ -1357,6 +1480,7 @@ the import above carries `with context`: a macro imported without it cannot see
1357
1480
  | `date(format, zone)` | Formats a `Date` or a date string. `readable` (the default) gives `2 September 2026`, `html` gives `2026-09-02` for a `<time datetime>`, `year` gives `2026`, `month` gives `September 2026`, `iso` gives the full ISO 8601 instant. A date in a file is a UTC instant; every format but `iso` is rendered in the site's `timezone` setting, and `iso` stays the instant. Pass `zone` — an IANA name — to override the setting for one call. A value that is not a date renders as the empty string. |
1358
1481
  | `url` | Prefixes a root-relative path with the base URL's path, so a site served from a subdirectory links correctly. Eleventy's filter of the same name. |
1359
1482
  | `absoluteUrl` | The same path as a fully qualified URL against the site's `baseUrl`. |
1483
+ | `asset` | The URL of a file in the theme's `static/` directory, with the base path in front and a hash of the file's bytes on the end: `{{ "style.css" \| asset }}` gives `/theme/style.css?v=3f2a9c01b7d4`. The file is found the way `/theme/` finds it, the chosen theme first. A hashed URL is served with `Cache-Control: public, max-age=31536000, immutable`, and editing the file changes the hash on the next render. A file no theme has gets its plain `/theme/` URL. |
1360
1484
 
1361
1485
  `date` reads one word rather than parsing it: `{{ "now" | date("year") }}` is
1362
1486
  the year at the moment the page is rendered, in the site's own timezone. It is
@@ -13,14 +13,19 @@
13
13
  off an account. The search form is on the search page and nowhere else.
14
14
 
15
15
  Blocks a site can override without copying the whole file:
16
- title the contents of <title>
17
- head the cards, the icons and the structured data, at the end of
18
- <head>. Call `super()` from an override that only adds to it.
19
- alternates <link rel="alternate"> tags, where the feeds go
20
- header the site header, with the site menu
21
- content the page body
22
- footer the site footer
23
- scripts scripts at the end of <body>
16
+ title the contents of <title>
17
+ speculationRules the <script type="speculationrules"> that prefetches the
18
+ site's own pages; empty it to prefetch nothing
19
+ viewTransitions the <style> that opts into cross-document view
20
+ transitions; empty it to turn them off
21
+ head the cards, the icons and the structured data, at the end
22
+ of <head>. Call `super()` from an override that only
23
+ adds to it.
24
+ alternates <link rel="alternate"> tags, where the feeds go
25
+ header the site header, with the site menu
26
+ content the page body
27
+ footer the site footer
28
+ scripts scripts at the end of <body>
24
29
  #}{% import "partials/feeds.njk" as feeds %}
25
30
  {%- import "partials/menu.njk" as nav %}
26
31
  {#- Whether this is the front page, which is the one page the header and the
@@ -55,7 +60,43 @@
55
60
  <meta name="description" content="{{ metaDescription }}">
56
61
  {% endif %}
57
62
  <link rel="canonical" href="{{ (page.url or "/") | absoluteUrl }}">
58
- <link rel="stylesheet" href="{{ "/theme/style.css" | url }}">
63
+ <link rel="stylesheet" href="{{ "style.css" | asset }}">
64
+ {% block speculationRules %}
65
+ {#
66
+ Prefetch the page behind a link the reader is about to follow: a hover
67
+ or a press on one of this site's own pages (TASK-140). Not the admin,
68
+ logout, a feed, an upload or a URL with a query, which are not pages to
69
+ read or not safe to fetch unasked. Every pattern goes through `url`, so
70
+ a site in a subdirectory prefetches only its own pages. The CMS drops
71
+ this script from a page drawn for somebody signed in.
72
+ #}
73
+ {% set speculationRules = { "prefetch": [{
74
+ "source": "document",
75
+ "eagerness": "moderate",
76
+ "where": { "and": [
77
+ { "href_matches": "/*" | url },
78
+ { "not": { "href_matches": "/admin" | url } },
79
+ { "not": { "href_matches": "/admin/*" | url } },
80
+ { "not": { "href_matches": "/*logout*" | url } },
81
+ { "not": { "href_matches": "/feed/*" | url } },
82
+ { "not": { "href_matches": "/*/feed/*" | url } },
83
+ { "not": { "href_matches": "/uploads/*" | url } },
84
+ { "not": { "href_matches": "/*\\?(.+)" | url } }
85
+ ] }
86
+ }] } %}
87
+ <script type="speculationrules">{{ speculationRules | dump | safe }}</script>
88
+ {% endblock %}
89
+ {% block viewTransitions %}
90
+ {#
91
+ Cross-document view transitions: the default cross-fade between two of
92
+ this site's pages, and none for a reader who asks for reduced motion.
93
+ #}
94
+ <style>
95
+ @media (prefers-reduced-motion: no-preference) {
96
+ @view-transition { navigation: auto; }
97
+ }
98
+ </style>
99
+ {% endblock %}
59
100
  {% block alternates %}
60
101
  {{ feeds.feedLinks("/", site.title) }}
61
102
  {{ feeds.commentsFeedLink("/comments/feed/", site.title + " comments") }}
@@ -184,7 +225,7 @@
184
225
  See the theme README.
185
226
  #}
186
227
  {% if content and "language-" in content %}
187
- <script src="{{ "/theme/highlight.js" | url }}" defer></script>
228
+ <script src="{{ "highlight.js" | asset }}" defer></script>
188
229
  {% endif %}
189
230
  {% endblock %}
190
231
  </body>