datalog-theme 0.10.0 → 0.11.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +133 -0
  3. data/CITATION.cff +2 -2
  4. data/README.md +4 -2
  5. data/_data/i18n/en.yml +55 -3
  6. data/_data/i18n/es.yml +55 -3
  7. data/_data/i18n/pt.yml +55 -3
  8. data/_includes/analytics/dashboard.html +24 -9
  9. data/_includes/components/academic-dashboard.html +168 -133
  10. data/_includes/components/author-bio.html +7 -5
  11. data/_includes/components/author-list.html +6 -5
  12. data/_includes/components/citation-tools.html +2 -1
  13. data/_includes/components/content-provenance.html +19 -1
  14. data/_includes/components/correction-report.html +15 -8
  15. data/_includes/components/hero.html +22 -15
  16. data/_includes/components/package-index.html +121 -0
  17. data/_includes/components/package-install.html +27 -50
  18. data/_includes/components/post-list.html +14 -0
  19. data/_includes/components/responsive-image.html +15 -21
  20. data/_includes/footer.html +1 -1
  21. data/_includes/head.html +70 -11
  22. data/_includes/layouts/default/article.html +76 -46
  23. data/_includes/meta/dataset-json.html +171 -0
  24. data/_includes/meta/math-config.html +15 -1
  25. data/_includes/meta/package-json.html +69 -0
  26. data/_includes/meta/person-json.html +36 -13
  27. data/_includes/meta/publisher.html +15 -0
  28. data/_includes/meta/schema.html +108 -23
  29. data/_includes/meta/scholarly.html +10 -5
  30. data/_includes/post/related-posts.html +17 -43
  31. data/_includes/scripts.html +2 -2
  32. data/_includes/search/index-data.json +10 -1
  33. data/_includes/search/page.html +11 -6
  34. data/_layouts/archive.html +67 -0
  35. data/_layouts/dataset.html +2 -1
  36. data/_layouts/default.html +13 -11
  37. data/_layouts/docs.html +53 -0
  38. data/_layouts/home.html +30 -1
  39. data/_layouts/notebook.html +5 -1
  40. data/_layouts/package.html +38 -21
  41. data/_layouts/portfolio.html +1 -0
  42. data/_layouts/post.html +21 -62
  43. data/_layouts/project.html +2 -1
  44. data/_layouts/research.html +14 -2
  45. data/_plugins/analytics_dashboard.rb +4 -1
  46. data/_plugins/archive.rb +114 -0
  47. data/_plugins/authors.rb +50 -1
  48. data/_plugins/citation_exports.rb +12 -1
  49. data/_plugins/config_validator.rb +77 -2
  50. data/_plugins/correction_fallback.rb +110 -0
  51. data/_plugins/image_optimizer.rb +129 -22
  52. data/_plugins/math_preprocessor.rb +7 -48
  53. data/_plugins/notebook_converter.rb +44 -5
  54. data/_plugins/packages.rb +84 -0
  55. data/_plugins/page_dates.rb +37 -0
  56. data/_plugins/references.rb +16 -2
  57. data/_plugins/related_posts.rb +149 -0
  58. data/_plugins/search_sections.rb +110 -0
  59. data/_plugins/site_identity.rb +42 -0
  60. data/_plugins/social_cards.rb +17 -0
  61. data/_sass/_academic-dashboard.scss +18 -30
  62. data/_sass/_base.scss +2 -0
  63. data/_sass/_components.scss +21 -23
  64. data/_sass/_docs.scss +143 -0
  65. data/_sass/_features.scss +6 -0
  66. data/_sass/_layout.scss +65 -6
  67. data/_sass/_mathematical.scss +61 -19
  68. data/_sass/_notebooks.scss +1 -32
  69. data/_sass/_package-docs.scss +6 -9
  70. data/_sass/_post-components.scss +23 -23
  71. data/_sass/_print.scss +1 -1
  72. data/_sass/_search-page.scss +75 -0
  73. data/_sass/_search.scss +17 -26
  74. data/_sass/_syntax-highlighting.scss +23 -1
  75. data/_sass/_theme.scss +1 -0
  76. data/_sass/_utilities.scss +87 -40
  77. data/_sass/_variables.scss +31 -29
  78. data/assets/css/main.scss +2 -1
  79. data/assets/js/dist/academic.js +1 -1
  80. data/assets/js/dist/analytics-dashboard.js +1 -1
  81. data/assets/js/dist/chunks/chunk-TNVD6UAM.js +1 -0
  82. data/assets/js/dist/comments.js +1 -1
  83. data/assets/js/dist/contact.js +1 -1
  84. data/assets/js/dist/core.js +1 -1
  85. data/assets/js/dist/corrections.js +5 -1
  86. data/assets/js/dist/loader.js +1 -1
  87. data/assets/js/dist/math.js +2 -1
  88. data/assets/js/dist/moderation.js +1 -1
  89. data/assets/js/dist/reactions.js +1 -1
  90. data/assets/js/dist/search.js +1 -1
  91. data/assets/js/dist/sources.json +20 -16
  92. data/assets/js/dist/subscriptions.js +1 -1
  93. data/assets/js/loader.js +6 -2
  94. data/datalog-theme.gemspec +1 -1
  95. data/lib/datalog/audit/checks.rb +269 -0
  96. data/lib/datalog/audit/known_keys.rb +61 -0
  97. data/lib/datalog/audit/site_reader.rb +118 -0
  98. data/lib/datalog/audit/source_file.rb +85 -0
  99. data/lib/datalog/audit.rb +145 -0
  100. data/lib/datalog/citations/bibtex.rb +200 -0
  101. data/lib/datalog/citations/entry.rb +333 -0
  102. data/lib/datalog/citations/markup.rb +79 -0
  103. data/lib/datalog/cli.rb +164 -89
  104. data/lib/datalog/critical_css.rb +126 -21
  105. data/lib/datalog/latex_speech/words.json +88 -0
  106. data/lib/datalog/latex_speech.rb +355 -0
  107. data/lib/datalog/packages/command.rb +51 -0
  108. data/lib/datalog/packages/refresh.rb +187 -0
  109. data/lib/datalog/packages.rb +177 -0
  110. data/lib/datalog/plugin_system.rb +5 -0
  111. data/lib/datalog/plugins/citations.rb +325 -109
  112. data/lib/datalog/site_config.rb +26 -0
  113. data/lib/datalog/social_cards/font.rb +286 -0
  114. data/lib/datalog/social_cards/fonts/IBMPlexSans-Regular.ttf +0 -0
  115. data/lib/datalog/social_cards/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
  116. data/lib/datalog/social_cards/fonts/OFL.txt +92 -0
  117. data/lib/datalog/social_cards/geometry.rb +51 -0
  118. data/lib/datalog/social_cards/outline.rb +54 -0
  119. data/lib/datalog/social_cards/plain_text.rb +258 -0
  120. data/lib/datalog/social_cards/template.rb +322 -0
  121. data/lib/datalog/social_cards/template.svg +17 -0
  122. data/lib/datalog/social_cards/typesetter.rb +178 -0
  123. data/lib/datalog/social_cards.rb +369 -0
  124. data/lib/datalog/theme/updater.rb +307 -0
  125. data/lib/datalog/theme/version.rb +1 -1
  126. metadata +46 -9
  127. data/_includes/components/enhanced-code-block.html +0 -212
  128. data/_includes/components/performance-monitor.html +0 -170
  129. data/_includes/components/viz-table-fallback.html +0 -19
  130. data/_plugins/datalog_bibliography.rb +0 -21
  131. data/assets/js/dist/chunks/chunk-WIRUK3OZ.js +0 -1
@@ -13,10 +13,24 @@ Provides rich structured data for:
13
13
  - Publisher: a Person or an Organization, from `publisher` in _config.yml
14
14
  - Images, keywords, and content metadata
15
15
 
16
- Defaults to TechArticle for posts, WebPage for other pages.
16
+ Defaults to TechArticle for posts, WebPage for other pages. A dataset page
17
+ adds a Dataset node, and a package page a SoftwareSourceCode node. `schema_type:
18
+ ProfilePage` describes a page about a person or an organization: its
19
+ `mainEntity` is the page's first author (the site's author by default), or the
20
+ publisher with `main_entity: publisher`.
21
+
22
+ The site's author and the publisher carry stable `@id`s under the site's root
23
+ (`#person`, `#organization`), so every page names the same entities. With
24
+ `site_identity` in _config.yml, the homepage also describes the site itself: a
25
+ `WebSite` node (`#website`) with its name and other names, and the publisher's
26
+ full node, which search engines read as the site's name and owner.
17
27
  {%- endcomment -%}
18
28
  {%- assign schema_page = include.page -%}
19
29
  {%- include meta/publisher.html -%}
30
+ {%- assign schema_identity = site | site_identity -%}
31
+ {%- assign website_id = identity_root | append: '#website' -%}
32
+ {%- assign is_home = false -%}
33
+ {%- if schema_page.url == '/' or schema_page.url == '/index.html' -%}{%- assign is_home = true -%}{%- endif -%}
20
34
  {%- assign is_post = false -%}
21
35
  {%- if schema_page.layout == 'post' or schema_page.collection == 'posts' -%}
22
36
  {%- assign is_post = true -%}
@@ -27,6 +41,10 @@ Defaults to TechArticle for posts, WebPage for other pages.
27
41
  {%- endif -%}
28
42
  {%- assign schema_type = include.type | default: schema_page.schema_type | default: layout.schema_type | default: default_type -%}
29
43
  {%- if schema_type == 'TechnicalArticle' -%}{%- assign schema_type = 'TechArticle' -%}{%- endif -%}
44
+ {%- assign is_profile_page = false -%}
45
+ {%- if schema_type == 'ProfilePage' -%}{%- assign is_profile_page = true -%}{%- endif -%}
46
+ {%- assign part_of_website = false -%}
47
+ {%- if schema_identity -%}{%- if is_home or is_profile_page -%}{%- assign part_of_website = true -%}{%- endif -%}{%- endif -%}
30
48
  {%- assign csp_registry = site.data.csp | default: {} -%}
31
49
  {%- assign nonce_lookup = csp_registry.nonces | default: {} -%}
32
50
  {%- assign schema_nonce = schema_page.csp_nonce | default: page.csp_nonce | default: nonce_lookup[schema_page.path] | default: nonce_lookup[page.path] | default: nonce_lookup[page.url] -%}
@@ -36,28 +54,67 @@ Defaults to TechArticle for posts, WebPage for other pages.
36
54
  {%- assign schema_authors = schema_page | page_authors -%}
37
55
  {%- assign schema_contributors = schema_page | page_contributors -%}
38
56
  {%- assign word_count = schema_page.content | strip_html | number_of_words -%}
39
- {%- assign modified = schema_page.last_modified_at | default: schema_page.updated | default: schema_page.date -%}
57
+ {%- comment -%} An undated document's date is the build's: it is no publication date (#411). {%- endcomment -%}
58
+ {%- assign schema_published = schema_page | published_date -%}
59
+ {%- assign modified = schema_page.last_modified_at | default: schema_page.updated | default: schema_published -%}
60
+ {%- capture publisher_json -%}
61
+ {
62
+ "@type": {{ publisher_type | jsonify_script }},
63
+ "@id": {{ publisher_id | jsonify_script }},
64
+ "name": {{ publisher_name | jsonify_script }},
65
+ "url": {{ publisher_url | jsonify_script }}
66
+ {%- if publisher_logo -%},
67
+ "logo": {
68
+ "@type": "ImageObject",
69
+ "url": {{ publisher_logo | jsonify_script }}
70
+ {%- if publisher_logo_default -%},
71
+ "width": 512,
72
+ "height": 512
73
+ {%- endif -%}
74
+ }
75
+ {%- endif -%}
76
+ }
77
+ {%- endcapture -%}
40
78
  <script type="application/ld+json" nonce="{{ schema_nonce }}">
41
79
  {
42
80
  "@context": "https://schema.org",
43
81
  "@type": {{ schema_type | jsonify_script }},
82
+ {%- comment -%} A ProfilePage is the page itself, about its mainEntity; any other type is the page's main entity. {%- endcomment -%}
83
+ {%- if is_profile_page %}
84
+ "@id": {{ page_url | jsonify_script }},
85
+ "name": {{ schema_page.title | default: site.title | jsonify_script }},
86
+ {%- assign profile_entity = schema_authors | first -%}
87
+ "mainEntity": {% if schema_page.main_entity == 'publisher' or profile_entity == nil %}{{ publisher_json }}{% else %}{% include meta/person-json.html person=profile_entity %}{% endif %},
88
+ {%- else %}
44
89
  "mainEntityOfPage": {
45
90
  "@type": "WebPage",
46
91
  "@id": {{ page_url | jsonify_script }}
47
92
  },
93
+ {%- endif %}
94
+ {%- if part_of_website %}
95
+ "isPartOf": { "@id": {{ website_id | jsonify_script }} },
96
+ {%- endif %}
48
97
  "headline": {{ schema_page.title | default: site.title | truncate: 110 | jsonify_script }},
49
98
  {%- comment -%} Values the page doesn't have are left out: "description": "" and "dateModified": "" are invalid structured data. {%- endcomment -%}
50
99
  {%- if description and description != "" -%}
51
100
  "description": {{ description | jsonify_script }},
52
101
  {%- endif -%}
53
102
  "url": {{ page_url | jsonify_script }},
54
- {%- if schema_page.image or schema_page.og_image -%}
103
+ {%- assign schema_image = schema_page.image | strip | default: schema_page.og_image | strip -%}
104
+ {%- if schema_image != "" -%}
55
105
  "image": {
56
106
  "@type": "ImageObject",
57
- "url": {{ schema_page.image | default: schema_page.og_image | absolute_url | jsonify_script }},
107
+ "url": {{ schema_image | absolute_url | jsonify_script }},
58
108
  "width": {{ schema_page.image_width | default: 1200 }},
59
109
  "height": {{ schema_page.image_height | default: 630 }}
60
110
  },
111
+ {%- elsif schema_page.datalog_social_card -%}
112
+ "image": {
113
+ "@type": "ImageObject",
114
+ "url": {{ schema_page.datalog_social_card.url | absolute_url | jsonify_script }},
115
+ "width": {{ schema_page.datalog_social_card.width }},
116
+ "height": {{ schema_page.datalog_social_card.height }}
117
+ },
61
118
  {%- elsif site.social.default_image -%}
62
119
  "image": {
63
120
  "@type": "ImageObject",
@@ -66,8 +123,8 @@ Defaults to TechArticle for posts, WebPage for other pages.
66
123
  "height": 630
67
124
  },
68
125
  {%- endif -%}
69
- {%- if schema_page.date -%}
70
- "datePublished": "{{ schema_page.date | date_to_xmlschema }}",
126
+ {%- if schema_published -%}
127
+ "datePublished": "{{ schema_published | date_to_xmlschema }}",
71
128
  {%- endif -%}
72
129
  {%- if modified -%}
73
130
  "dateModified": "{{ modified | date_to_xmlschema }}",
@@ -99,21 +156,7 @@ Defaults to TechArticle for posts, WebPage for other pages.
99
156
  {%- endfor -%}
100
157
  ],
101
158
  {%- endif -%}
102
- "publisher": {
103
- "@type": {{ publisher_type | jsonify_script }},
104
- "name": {{ publisher_name | jsonify_script }},
105
- "url": {{ publisher_url | jsonify_script }}
106
- {%- if publisher_logo -%},
107
- "logo": {
108
- "@type": "ImageObject",
109
- "url": {{ publisher_logo | jsonify_script }}
110
- {%- if publisher_logo_default -%},
111
- "width": 512,
112
- "height": 512
113
- {%- endif -%}
114
- }
115
- {%- endif -%}
116
- }
159
+ "publisher": {{ publisher_json }}
117
160
  {%- if schema_page.doi -%},
118
161
  "identifier": {
119
162
  "@type": "PropertyValue",
@@ -121,8 +164,9 @@ Defaults to TechArticle for posts, WebPage for other pages.
121
164
  "value": {{ schema_page.doi | jsonify_script }}
122
165
  }
123
166
  {%- endif -%}
124
- {%- if schema_page.citation_key -%},
125
- "citation": {{ schema_page.citation_key | jsonify_script }}
167
+ {%- comment -%} The works the page cites with {% raw %}{% cite %}{% endraw %}; its own citation key is not one of them {%- endcomment -%}
168
+ {%- if schema_page.datalog_cited_works and schema_page.datalog_cited_works.size > 0 -%},
169
+ "citation": {{ schema_page.datalog_cited_works | map: 'json_ld' | jsonify_script }}
126
170
  {%- endif -%}
127
171
  {%- comment -%} The licence of the text and figures, and who holds the copyright. {%- endcomment -%}
128
172
  {%- assign content_license = schema_page | page_license -%}
@@ -161,6 +205,47 @@ Defaults to TechArticle for posts, WebPage for other pages.
161
205
  {%- endif -%}
162
206
  }
163
207
  </script>
208
+ {%- comment -%}
209
+ The homepage describes the site itself, once, for every other page's `@id`
210
+ to point at: the WebSite, its names and its publisher (#322).
211
+ {%- endcomment -%}
212
+ {%- if schema_identity and is_home -%}
213
+ {%- assign identity_person = site | site_author_record -%}
214
+ {%- assign site_language = site.lang | default: site.locale | default: 'en' | split: '_' | first -%}
215
+ <script type="application/ld+json" nonce="{{ schema_nonce }}">
216
+ {
217
+ "@context": "https://schema.org",
218
+ "@graph": [
219
+ {
220
+ "@type": "WebSite",
221
+ "@id": {{ website_id | jsonify_script }},
222
+ "url": {{ identity_root | jsonify_script }},
223
+ "name": {{ schema_identity.name | jsonify_script }}
224
+ {%- if schema_identity.alternate_names.size > 0 -%},
225
+ "alternateName": {% if schema_identity.alternate_names.size == 1 %}{{ schema_identity.alternate_names.first | jsonify_script }}{% else %}{{ schema_identity.alternate_names | jsonify_script }}{% endif %}
226
+ {%- endif -%}
227
+ {%- if site.description -%},
228
+ "description": {{ site.description | strip_html | normalize_whitespace | jsonify_script }}
229
+ {%- endif -%},
230
+ "inLanguage": {{ site_language | jsonify_script }},
231
+ "publisher": { "@id": {{ publisher_id | jsonify_script }} }
232
+ },
233
+ {% if publisher_is_site_author and identity_person %}{% include meta/person-json.html person=identity_person %}{% else %}{{ publisher_json }}{% endif %}
234
+ ]
235
+ }
236
+ </script>
237
+ {%- endif -%}
238
+ {%- comment -%}
239
+ A dataset page also describes the dataset itself. Google Dataset Search
240
+ indexes `Dataset` and nothing else, so the WebPage above does not reach it.
241
+ {%- endcomment -%}
242
+ {%- if schema_page.collection == 'datasets' or schema_type == 'Dataset' -%}
243
+ {%- include meta/dataset-json.html page=schema_page -%}
244
+ {%- endif -%}
245
+ {%- comment -%} A package page also describes the package: its repository, release and registries. {%- endcomment -%}
246
+ {%- if schema_page.collection == 'packages' -%}
247
+ {%- include meta/package-json.html page=schema_page -%}
248
+ {%- endif -%}
164
249
  {%- comment -%} Add BreadcrumbList schema for posts {%- endcomment -%}
165
250
  {%- if is_post -%}
166
251
  <script type="application/ld+json" nonce="{{ schema_nonce }}">
@@ -9,7 +9,8 @@ Dublin Core equivalents. Only a page the `scholarly` filter accepts gets them
9
9
  or every post when _config.yml sets `scholarly: true`. A field the page
10
10
  lacks leaves its tag out. The authors are the page's (page_authors), as in
11
11
  the byline, the JSON-LD and the citation exports; `citation_authors` names
12
- the indexed authors alone.
12
+ the indexed authors alone. Each work the page cites with {% raw %}{% cite %}{% endraw %} is
13
+ a citation_reference (lib/datalog/plugins/citations.rb).
13
14
  {%- endcomment -%}
14
15
  {%- assign is_scholarly = page | scholarly -%}
15
16
  {%- if is_scholarly and page.title -%}
@@ -54,8 +55,9 @@ the indexed authors alone.
54
55
  {%- endif %}
55
56
  {%- endfor %}
56
57
  {%- endif %}
57
- {%- if page.date %}
58
- <meta name="citation_publication_date" content="{{ page.date | date: '%Y/%m/%d' }}" />
58
+ {%- assign scholarly_published = page | published_date %}
59
+ {%- if scholarly_published %}
60
+ <meta name="citation_publication_date" content="{{ scholarly_published | date: '%Y/%m/%d' }}" />
59
61
  {%- endif %}
60
62
  <meta name="citation_public_url" content="{{ scholarly_url }}" />
61
63
  <meta name="citation_fulltext_html_url" content="{{ scholarly_url }}" />
@@ -95,13 +97,16 @@ the indexed authors alone.
95
97
  {%- if scholarly_keywords and scholarly_keywords.size > 0 %}
96
98
  <meta name="citation_keywords" content="{{ scholarly_keywords | join: '; ' | escape }}" />
97
99
  {%- endif %}
100
+ {%- for work in page.datalog_cited_works %}
101
+ <meta name="citation_reference" content="{{ work.reference | escape }}" />
102
+ {%- endfor %}
98
103
  <link rel="schema.DC" href="http://purl.org/dc/elements/1.1/" />
99
104
  <meta name="DC.title" content="{{ scholarly_title | escape }}" />
100
105
  {%- for name in scholarly_names %}
101
106
  <meta name="DC.creator" content="{{ name | strip | escape }}" />
102
107
  {%- endfor %}
103
- {%- if page.date %}
104
- <meta name="DC.date" content="{{ page.date | date: '%Y-%m-%d' }}" />
108
+ {%- if scholarly_published %}
109
+ <meta name="DC.date" content="{{ scholarly_published | date: '%Y-%m-%d' }}" />
105
110
  {%- endif %}
106
111
  {%- if page.doi %}
107
112
  {%- assign scholarly_identifier = page.doi -%}
@@ -3,57 +3,31 @@
3
3
  required:
4
4
  - page
5
5
  optional:
6
- - limit
7
6
  - date_format
8
7
  ---
9
- Builds a list of related posts based on overlapping tags or categories.
8
+ Renders the posts selected at build time by _plugins/related_posts.rb.
10
9
  Falls back to a friendly message when no related content is available.
11
10
  {%- endcomment -%}
12
- {%- assign related_limit = include.limit | default: 3 -%}
13
- {%- comment -%} site.posts is already newest first. {%- endcomment -%}
14
- {%- assign rendered_count = 0 -%}
15
11
  <section class="post-related" aria-labelledby="post-related-heading">
16
12
  <h2 id="post-related-heading">Related posts</h2>
17
13
  <ul class="post-related-list">
18
- {%- for candidate in site.posts -%}
19
- {%- if rendered_count >= related_limit -%}{%- break -%}{%- endif -%}
20
- {%- if candidate.url != include.page.url -%}
21
- {%- assign overlap = 0 -%}
22
- {%- if include.page.tags and candidate.tags -%}
23
- {%- for tag in include.page.tags -%}
24
- {%- if candidate.tags contains tag -%}
25
- {%- assign overlap = overlap | plus: 1 -%}
26
- {%- endif -%}
27
- {%- endfor -%}
28
- {%- endif -%}
29
- {%- assign category_match = false -%}
30
- {%- if include.page.categories and candidate.categories -%}
31
- {%- for category in include.page.categories -%}
32
- {%- if candidate.categories contains category -%}
33
- {%- assign category_match = true -%}
34
- {%- endif -%}
35
- {%- endfor -%}
36
- {%- endif -%}
37
- {%- if overlap > 0 or category_match -%}
38
- {%- assign rendered_count = rendered_count | plus: 1 -%}
39
- <li>
40
- <article class="related-post">
41
- {%- if candidate.teaser -%}
42
- <a class="related-post__media" href="{{ candidate.url | relative_url }}" tabindex="-1" aria-hidden="true"><img src="{{ candidate.teaser | relative_url }}" alt="" loading="lazy" decoding="async" /></a>
43
- {%- endif -%}
44
- <h3><a href="{{ candidate.url | relative_url }}">{{ candidate.title | escape }}</a></h3>
45
- <p class="related-post-meta">
46
- {% include helpers/date-format.html date=candidate.date format=include.date_format %} · {{ candidate.excerpt | strip_html | default: '' | truncate: 140 }}
47
- </p>
48
- {%- if candidate.tags -%}
49
- <p class="related-post-tags">Topics: {% include helpers/array-to-sentence.html items=candidate.tags %}</p>
50
- {%- endif -%}
51
- </article>
52
- </li>
53
- {%- endif -%}
54
- {%- endif -%}
14
+ {%- for candidate in include.page.related -%}
15
+ <li>
16
+ <article class="related-post">
17
+ {%- if candidate.teaser -%}
18
+ <a class="related-post__media" href="{{ candidate.url | relative_url }}" tabindex="-1" aria-hidden="true"><img src="{{ candidate.teaser | relative_url }}" alt="" loading="lazy" decoding="async" /></a>
19
+ {%- endif -%}
20
+ <h3><a href="{{ candidate.url | relative_url }}">{{ candidate.title | escape }}</a></h3>
21
+ <p class="related-post-meta">
22
+ {% include helpers/date-format.html date=candidate.date format=include.date_format %} · {{ candidate.excerpt | strip_html | default: '' | truncate: 140 }}
23
+ </p>
24
+ {%- if candidate.tags -%}
25
+ <p class="related-post-tags">Topics: {% include helpers/array-to-sentence.html items=candidate.tags %}</p>
26
+ {%- endif -%}
27
+ </article>
28
+ </li>
55
29
  {%- endfor -%}
56
- {%- if rendered_count == 0 -%}
30
+ {%- if include.page.related.size == 0 -%}
57
31
  <li>No related posts yet. Explore the archive for more content.</li>
58
32
  {%- endif -%}
59
33
  </ul>
@@ -37,9 +37,9 @@
37
37
  {% assign reading_state_flag = 'auto' %}
38
38
  {% endif %}
39
39
 
40
- {%- comment -%} The correction-report form needs a backend with the feature on (components/correction-report.html). {%- endcomment -%}
40
+ {%- comment -%} Posts may have the backend form or a static correction link. {%- endcomment -%}
41
41
  {% assign corrections_flag = 'false' %}
42
- {% if site.dynamic_services.base_url and site.dynamic_services.base_url != '' and site.dynamic_services.features.corrections != false and site.corrections.enabled != false %}
42
+ {% if page.layout == 'post' and page.corrections != false and site.corrections.enabled != false %}
43
43
  {% assign corrections_flag = 'auto' %}
44
44
  {% endif %}
45
45
 
@@ -70,7 +70,9 @@
70
70
  {% endfor %}
71
71
  {% endif %}
72
72
  {% assign raw_content = doc.content | default: '' %}
73
- {% assign content_text = raw_content | markdownify | strip_html | normalize_whitespace %}
73
+ {% assign rendered_content = raw_content | markdownify %}
74
+ {% assign content_text = rendered_content | strip_html | normalize_whitespace %}
75
+ {% assign sections = rendered_content | search_sections %}
74
76
  {% assign summary = doc.summary | default: doc.excerpt | default: content_text | truncate: 280, '' %}
75
77
  {% assign inline_math_segments = raw_content | split: '$' %}
76
78
  {% assign display_math_segments = raw_content | split: '$$' %}
@@ -89,6 +91,13 @@
89
91
  "type": {{ doc_type | strip | jsonify }},
90
92
  "summary": {{ summary | strip | jsonify }},
91
93
  "content": {{ content_text | jsonify }},
94
+ {%- comment -%}
95
+ The passages of the page, each under the heading it belongs to, so a
96
+ result can link to the section rather than the top of the article. The
97
+ text is repeated here, which costs little once the file is served
98
+ compressed: the duplicate compresses almost entirely away.
99
+ {%- endcomment %}
100
+ "sections": {{ sections | jsonify }},
92
101
  "code": {{ doc.search_code | default: no_code | jsonify }},
93
102
  "math": [
94
103
  {% assign math_counter = 0 %}
@@ -14,7 +14,6 @@
14
14
  spellcheck="false"
15
15
  data-search-input
16
16
  aria-describedby="search-shortcuts"
17
- aria-activedescendant=""
18
17
  />
19
18
  <button type="submit" class="search-app__submit">Search</button>
20
19
  </div>
@@ -84,9 +83,9 @@
84
83
  <p aria-live="polite" data-search-live="status" hidden></p>
85
84
  </div>
86
85
  </header>
87
- <ol class="search-app__results-list" data-search-results role="listbox" aria-label="Search results"></ol>
86
+ <ol class="search-app__results-list" data-search-results aria-label="Search results"></ol>
88
87
  <template id="search-result-template">
89
- <li class="search-result" role="option" data-result-item>
88
+ <li class="search-result" data-result-item>
90
89
  <article class="search-result__card">
91
90
  <header class="search-result__header">
92
91
  <p class="search-result__meta">
@@ -98,16 +97,22 @@
98
97
  <p class="search-result__summary" data-result-summary></p>
99
98
  </header>
100
99
  <div class="search-result__body">
101
- <pre class="search-result__code language-none line-numbers" data-result-code><code></code></pre>
100
+ <pre class="search-result__code language-none line-numbers" data-result-code tabindex="0"
101
+ role="group" aria-label="Code sample"><code></code></pre>
102
102
  <div class="search-result__math" data-result-math hidden>
103
- <div class="search-result__math-preview" data-result-math-preview aria-hidden="true"></div>
104
- <pre class="search-result__math-source"><code data-result-math-code></code></pre>
103
+ <div class="search-result__math-preview" data-result-math-preview aria-hidden="true" inert></div>
104
+ <pre class="search-result__math-source" tabindex="0" role="group"
105
+ aria-label="LaTeX source"><code data-result-math-code></code></pre>
105
106
  <button type="button" class="search-result__math-copy" data-result-math-copy>
106
107
  <span aria-hidden="true">⧉</span>
107
108
  <span class="visually-hidden">Copy LaTeX</span>
108
109
  </button>
109
110
  </div>
110
111
  <p class="search-result__excerpt" data-result-excerpt></p>
112
+ <div class="search-result__sections" data-result-sections hidden>
113
+ <p class="search-result__sections-label" data-result-sections-label></p>
114
+ <ul class="search-result__sections-list"></ul>
115
+ </div>
111
116
  </div>
112
117
  <footer class="search-result__footer">
113
118
  <ul class="search-result__tags" data-result-tags></ul>
@@ -0,0 +1,67 @@
1
+ ---
2
+ layout: default
3
+ article_meta: false # an archive is a list, written by no one in particular
4
+ ---
5
+ {%- assign archive = page | archive_data -%}
6
+ <div class="archive-section">
7
+ {{ content }}
8
+ {%- if page.archive == 'topic' %}
9
+ {%- if archive.series.size > 0 %}
10
+ <section aria-labelledby="archive-series">
11
+ <h2 id="archive-series">{% t 'archive.series' %}</h2>
12
+ <ul class="archive-list">
13
+ {%- for item in archive.series %}
14
+ <li><a href="{{ item.url | relative_url }}">{{ item.title | escape }}</a> <span class="archive-meta">{% t 'archive.parts' count: item.count %}</span></li>
15
+ {%- endfor %}
16
+ </ul>
17
+ </section>
18
+ {%- endif %}
19
+ {%- if archive.posts.size > 0 %}
20
+ <section aria-labelledby="archive-articles">
21
+ <h2 id="archive-articles">{% t 'archive.articles' %}</h2>
22
+ {% include components/post-list.html posts=archive.posts variant='teaser' %}
23
+ {%- if archive.remaining.size > 0 %}
24
+ <details><summary>{% t 'archive.show_all' count: archive.remaining.size %}</summary>
25
+ {% include components/post-list.html posts=archive.remaining variant='teaser' %}
26
+ </details>
27
+ {%- endif %}
28
+ </section>
29
+ {%- endif %}
30
+ {%- if archive.count == 0 %}<p>{% t 'archive.empty' %}</p>{%- endif %}
31
+ {%- else %}
32
+ {%- if archive.size > 0 %}
33
+ <nav aria-label="{% t 'archive.jump' %}">
34
+ <ul class="archive-list">
35
+ {%- for group in archive %}
36
+ <li><a href="#{{ group.slug }}">{{ group.name | escape }}</a> <span class="archive-meta">{% t 'archive.count' count: group.count %}</span></li>
37
+ {%- endfor %}
38
+ </ul>
39
+ </nav>
40
+ {%- for group in archive %}
41
+ <section aria-labelledby="{{ group.slug }}">
42
+ <h2 id="{{ group.slug }}">{{ group.name | escape }} <small>{% t 'archive.count' count: group.count %}</small></h2>
43
+ {%- if group.months %}
44
+ {%- for month in group.months %}
45
+ <section aria-labelledby="{{ month.slug }}">
46
+ <h3 id="{{ month.slug }}">{{ month.name | append: '-01' | localize_date: 'month' | escape }} <small>{% t 'archive.count' count: month.count %}</small></h3>
47
+ {% include components/post-list.html posts=month.posts variant='dated' %}
48
+ {%- if month.remaining.size > 0 %}
49
+ <details><summary>{% t 'archive.show_all' count: month.remaining.size %}</summary>
50
+ {% include components/post-list.html posts=month.remaining variant='dated' %}
51
+ </details>
52
+ {%- endif %}
53
+ </section>
54
+ {%- endfor %}
55
+ {%- else %}
56
+ {% include components/post-list.html posts=group.posts variant='dated' %}
57
+ {%- if group.remaining.size > 0 %}
58
+ <details><summary>{% t 'archive.show_all' count: group.remaining.size %}</summary>
59
+ {% include components/post-list.html posts=group.remaining variant='dated' %}
60
+ </details>
61
+ {%- endif %}
62
+ {%- endif %}
63
+ </section>
64
+ {%- endfor %}
65
+ {%- else %}<p>{% t 'archive.empty' %}</p>{%- endif %}
66
+ {%- endif %}
67
+ </div>
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  layout: default
3
3
  show_title: false # this layout renders the page's <h1>
4
+ article_meta: false # the default heading adds no date or byline above this layout's own header
4
5
  ---
5
6
  {% assign localization_config = site.theme_options.localization %}
6
7
  {% assign date_format = page.date_format | default: localization_config.date_format | default: 'long' %}
@@ -47,7 +48,7 @@ show_title: false # this layout renders the page's <h1>
47
48
  {% endif %}
48
49
  {% assign citation_key = page.citation_key | default: page.slug | default: page.title | slugify %}
49
50
  {% assign citation_authors = page | page_authors %}
50
- {% assign citation_date = page.date | default: page.updated %}
51
+ {% assign citation_date = page | published_date | default: page.updated %}
51
52
  {% assign citation_url = page.url | absolute_url %}
52
53
  {% include components/citation-tools.html title=page.title authors=citation_authors date=citation_date url=citation_url citation_key=citation_key citation_type='dataset' doi=page.doi %}
53
54
  {% include components/open-science-badges.html %}
@@ -11,21 +11,23 @@
11
11
  {% include meta/schema.html page=page %}
12
12
  </head>
13
13
  {% capture body_classes %}
14
- site-body{% if page.layout %} layout-{{ page.layout | replace: ' ', '-' }}{% endif %}{% if page.page_class %} page-{{ page.page_class | replace: ' ', '-' | downcase }}{% endif %}{% if page.classes %} {{ page.classes | join: ' ' }}{% endif %}{% if site.features.dark_mode_toggle %} has-dark-toggle{% endif %}{% if html_dir == 'rtl' %} rtl{% endif %}
14
+ site-body{% if page.layout %} layout-{{ page.layout | replace: ' ', '-' }}{% endif %}{% if page.page_class %} page-{{ page.page_class | replace: ' ', '-' | downcase }}{% endif %}{% if page.classes %} {{ page.classes | join: ' ' }}{% endif %}{% if site.features.dark_mode_toggle %} has-dark-toggle{% endif %}{% if html_dir == 'rtl' %} rtl{% endif %}{% if math_display_style == 'card' %} math-card{% endif %}
15
15
  {% endcapture %}
16
- <body class="{{ body_classes | strip }}" data-theme="{{ page.theme | default: 'system' }}">
16
+ {% assign default_color_mode = page.theme | default: site.theme_options.color_scheme.default | default: 'dark' %}
17
+ <body class="{{ body_classes | strip }}{% if default_color_mode == 'dark' %} dark-mode{% endif %}" data-default-theme="{{ default_color_mode }}" data-theme="{{ default_color_mode }}">
17
18
  {%- comment -%}
18
- Applies the saved or system color scheme before anything is drawn. core/dark-mode.js
19
- applies the same rule, but only once the script bundles have loaded, so readers who
20
- chose dark saw the light theme first; keep the two in step.
19
+ Applies the saved choice or site default before anything is drawn. The
20
+ core bundle applies the same rule when it loads; keep the two in step.
21
21
  {%- endcomment %}
22
22
  <script nonce="{{ page.csp_nonce }}">
23
- try {
24
- var mode = localStorage.getItem("datalog-color-mode");
25
- if (mode === "dark" || (mode !== "light" && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
26
- document.body.classList.add("dark-mode");
27
- }
28
- } catch (error) {}
23
+ var mode;
24
+ try { mode = localStorage.getItem("datalog-color-mode"); } catch (error) {}
25
+ var siteDefault = document.body.dataset.defaultTheme;
26
+ var isDark = mode === "dark" || (mode !== "light" &&
27
+ (siteDefault === "dark" || (siteDefault === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches)));
28
+ document.body.classList.toggle("dark-mode", isDark);
29
+ document.body.dataset.theme = isDark ? "dark" : "light";
30
+ document.documentElement.dataset.theme = document.body.dataset.theme;
29
31
  </script>
30
32
  {%- comment -%} The skip link and the footer take nothing from the page, so each is rendered once per build (the skip link once per language). {%- endcomment -%}
31
33
  {% include_cached skip-link.html lang=page.lang %}
@@ -0,0 +1,53 @@
1
+ ---
2
+ layout: page
3
+ ---
4
+ {% assign docs_groups = site.data.navigation.docs %}
5
+ {% assign guide_pages = '' | split: '' %}
6
+ {% for group in docs_groups %}
7
+ {% for link in group.links %}
8
+ {% if link.url contains '/guides/' %}
9
+ {% assign guide_pages = guide_pages | push: link %}
10
+ {% endif %}
11
+ {% endfor %}
12
+ {% endfor %}
13
+
14
+ <div class="docs-shell">
15
+ <nav class="docs-sidebar" aria-label="Documentation sections">
16
+ <a class="docs-sidebar__home" href="{{ '/' | relative_url }}">Documentation home</a>
17
+ {% for group in docs_groups %}
18
+ <h2 class="docs-sidebar__heading">{{ group.title | escape }}</h2>
19
+ <ul>
20
+ {% for link in group.links %}
21
+ <li><a href="{{ link.url | relative_url }}"{% if page.url == link.url %} aria-current="page"{% endif %}>{{ link.title | escape }}</a></li>
22
+ {% endfor %}
23
+ </ul>
24
+ {% endfor %}
25
+ <a class="docs-sidebar__repo" href="https://github.com/DiogoRibeiro7/analytics-blog-jekyll">View on GitHub</a>
26
+ </nav>
27
+
28
+ <div class="docs-main">
29
+ <div class="docs-content">{{ content }}</div>
30
+ <nav class="docs-pagination" aria-label="Guide navigation">
31
+ {% for link in guide_pages %}
32
+ {% if page.url == link.url %}
33
+ {% assign previous_index = forloop.index0 | minus: 1 %}
34
+ {% assign next_index = forloop.index0 | plus: 1 %}
35
+ {% if previous_index >= 0 %}
36
+ {% assign previous_guide = guide_pages[previous_index] %}
37
+ <a href="{{ previous_guide.url | relative_url }}"><span>Previous</span>{{ previous_guide.title | escape }}</a>
38
+ {% endif %}
39
+ {% if next_index < guide_pages.size %}
40
+ {% assign next_guide = guide_pages[next_index] %}
41
+ <a href="{{ next_guide.url | relative_url }}"><span>Next</span>{{ next_guide.title | escape }}</a>
42
+ {% endif %}
43
+ {% endif %}
44
+ {% endfor %}
45
+ </nav>
46
+ <a class="docs-back-to-top" href="#main-content">Back to top ↑</a>
47
+ </div>
48
+
49
+ {% capture page_contents %}{% include components/enhanced-toc.html html=content heading="On this page" show_progress=false %}{% endcapture %}
50
+ {% if page_contents contains 'enhanced-toc' %}
51
+ <aside class="docs-on-this-page">{{ page_contents }}</aside>
52
+ {% endif %}
53
+ </div>
data/_layouts/home.html CHANGED
@@ -1,8 +1,36 @@
1
1
  ---
2
2
  layout: default
3
+ article_meta: false # the home page is the hero and its sections
3
4
  ---
4
5
  {% assign localization_config = site.theme_options.localization %}
5
- {% include components/hero.html %}
6
+ {% include components/hero.html contained=true %}
7
+ {% assign home_features = page.home_features %}
8
+ {% if home_features and home_features.size > 0 %}
9
+ {% capture features_default_heading %}{% t 'home.features' %}{% endcapture %}
10
+ <section class="section home-features" aria-labelledby="home-features-heading">
11
+ <div class="container">
12
+ <h2 id="home-features-heading" class="section-title">{{ page.home_features_heading | default: features_default_heading | strip | escape }}</h2>
13
+ <div class="card-grid">
14
+ {% for feature in home_features %}
15
+ {% if feature.title %}
16
+ <article class="card home-feature-card">
17
+ <h3>
18
+ {% if feature.url %}<a href="{{ feature.url | relative_url }}">{{ feature.title | escape }}</a>
19
+ {% else %}{{ feature.title | escape }}{% endif %}
20
+ </h3>
21
+ {% if feature.description %}<p>{{ feature.description | escape }}</p>{% endif %}
22
+ </article>
23
+ {% endif %}
24
+ {% endfor %}
25
+ </div>
26
+ </div>
27
+ </section>
28
+ {% endif %}
29
+ {% assign home_body = content | strip %}
30
+ {% if home_body != '' %}
31
+ <div class="home-body">{{ content }}</div>
32
+ {% endif %}
33
+ {% if site.posts and site.posts.size > 0 %}
6
34
  <section class="section section-highlight">
7
35
  <div class="container">
8
36
  <h2 class="section-title">{% t 'home.latest_research' %}</h2>
@@ -31,6 +59,7 @@ layout: default
31
59
  </div>
32
60
  </div>
33
61
  </section>
62
+ {% endif %}
34
63
  {% comment %}
35
64
  The portfolio collection is optional: a site that has not declared it gets
36
65
  nil here, and sorting nil raises rather than rendering an empty section.