jekyll-theme-zer0 0.10.6 → 0.15.2

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +428 -0
  3. data/README.md +79 -31
  4. data/_data/README.md +419 -17
  5. data/_data/generate_statistics.rb +216 -9
  6. data/_data/generate_statistics.sh +106 -0
  7. data/_data/github-actions-example.yml +210 -0
  8. data/_data/navigation/about.yml +39 -11
  9. data/_data/navigation/docs.yml +53 -23
  10. data/_data/navigation/home.yml +27 -9
  11. data/_data/navigation/main.yml +27 -8
  12. data/_data/navigation/posts.yml +22 -6
  13. data/_data/navigation/quickstart.yml +19 -6
  14. data/_data/posts_organization.yml +153 -0
  15. data/_data/prerequisites.yml +112 -0
  16. data/_data/statistics_config.yml +203 -0
  17. data/_data/ui-text.yml +321 -0
  18. data/_data/update_statistics.sh +126 -0
  19. data/_includes/README.md +2 -0
  20. data/_includes/components/js-cdn.html +4 -1
  21. data/_includes/components/post-card.html +2 -11
  22. data/_includes/components/preview-image.html +32 -0
  23. data/_includes/content/intro.html +9 -10
  24. data/_includes/core/header.html +14 -0
  25. data/_includes/navigation/sidebar-categories.html +20 -9
  26. data/_includes/navigation/sidebar-folders.html +8 -7
  27. data/_includes/navigation/sidebar-right.html +16 -10
  28. data/_layouts/blog.html +15 -45
  29. data/_layouts/category.html +4 -24
  30. data/_layouts/collection.html +2 -12
  31. data/_layouts/default.html +1 -1
  32. data/_layouts/journals.html +2 -12
  33. data/_layouts/notebook.html +296 -0
  34. data/_sass/core/_docs.scss +1 -1
  35. data/_sass/custom.scss +54 -17
  36. data/_sass/notebooks.scss +458 -0
  37. data/assets/images/notebooks/test-notebook_files/test-notebook_4_0.png +0 -0
  38. data/assets/js/sidebar.js +511 -0
  39. data/scripts/README.md +131 -105
  40. data/scripts/analyze-commits.sh +9 -311
  41. data/scripts/bin/build +22 -22
  42. data/scripts/build +7 -111
  43. data/scripts/convert-notebooks.sh +415 -0
  44. data/scripts/features/validate_preview_urls.py +500 -0
  45. data/scripts/fix-markdown-format.sh +8 -262
  46. data/scripts/generate-preview-images.sh +7 -787
  47. data/scripts/install-preview-generator.sh +8 -528
  48. data/scripts/lib/README.md +5 -5
  49. data/scripts/lib/changelog.sh +89 -57
  50. data/scripts/lib/gem.sh +19 -7
  51. data/scripts/release +7 -236
  52. data/scripts/setup.sh +9 -153
  53. data/scripts/test/lib/run_tests.sh +1 -2
  54. data/scripts/test-auto-version.sh +7 -256
  55. data/scripts/test-mermaid.sh +7 -287
  56. data/scripts/test.sh +9 -154
  57. metadata +16 -10
  58. data/scripts/features/preview_generator.py +0 -646
  59. data/scripts/lib/test/run_tests.sh +0 -140
  60. data/scripts/lib/test/test_changelog.sh +0 -87
  61. data/scripts/lib/test/test_gem.sh +0 -68
  62. data/scripts/lib/test/test_git.sh +0 -82
  63. data/scripts/lib/test/test_validation.sh +0 -72
  64. data/scripts/lib/test/test_version.sh +0 -96
  65. data/scripts/version.sh +0 -178
@@ -0,0 +1,32 @@
1
+ {% comment %}
2
+ PREVIEW IMAGE - Simple, Consistent Image Rendering
3
+
4
+ Usage:
5
+ {%- include components/preview-image.html
6
+ src=post.preview
7
+ alt=post.title
8
+ class="card-img-top"
9
+ style="height: 180px; object-fit: cover;"
10
+ -%}
11
+
12
+ Convention: All preview paths should be absolute (e.g., /assets/images/preview.png)
13
+ External URLs (https://...) are also supported.
14
+ {% endcomment %}
15
+ {% assign img = include.src | default: site.teaser %}
16
+ {% assign alt = include.alt | default: "Preview image" %}
17
+ {% assign class = include.class | default: "card-img-top" %}
18
+ {% assign style = include.style %}
19
+ {% assign loading = include.loading | default: "lazy" %}
20
+
21
+ {% comment %} External URL or internal path {% endcomment %}
22
+ {% if img contains '://' %}
23
+ {% assign final_src = img %}
24
+ {% else %}
25
+ {% assign final_src = img | relative_url %}
26
+ {% endif %}
27
+
28
+ <img src="{{ final_src }}"
29
+ class="{{ class }}"
30
+ alt="{{ alt | escape }}"
31
+ {% if style %}style="{{ style }}"{% endif %}
32
+ loading="{{ loading }}">
@@ -16,19 +16,18 @@
16
16
  <!-- Intro Section -->
17
17
 
18
18
  {% comment %}
19
- Determine the preview image path. If page.preview starts with '/' or 'http',
20
- use it directly. Otherwise, prepend the public folder path.
19
+ Determine the preview image path. All preview paths should be absolute
20
+ starting with /assets/ or external URLs starting with ://
21
21
  {% endcomment %}
22
22
  {% assign preview_image = page.preview | default: site.info_banner %}
23
- {% assign preview_first_char = preview_image | slice: 0 %}
24
- {% if preview_image contains '://' or preview_first_char == '/' %}
23
+ {% if preview_image contains '://' %}
25
24
  {% assign preview_path = preview_image %}
26
25
  {% else %}
27
- {% assign preview_path = '/' | append: site.public_folder | append: '/' | append: preview_image %}
26
+ {% assign preview_path = preview_image | relative_url %}
28
27
  {% endif %}
29
28
 
30
29
  <div class="bd-intro pt-5 ps-lg-2 position-relative" style="
31
- background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('{{ site.baseurl }}{{ preview_path }}') no-repeat center center / cover;
30
+ background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('{{ preview_path | relative_url }}') no-repeat center center / cover;
32
31
  color: #fff;">
33
32
 
34
33
  <br>
@@ -46,23 +45,23 @@
46
45
  </button>
47
46
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="shareDropdownBottom">
48
47
  <li>
49
- <a class="dropdown-item" href="https://reddit.com/submit?url={{ page.url | absolute_url | url_encode }}&amp;title={{ page.title | url_encode }}" target="_blank">
48
+ <a class="dropdown-item" href="https://reddit.com/submit?url={{ site.url | append: page.url | url_encode }}&amp;title={{ page.title | url_encode }}" target="_blank">
50
49
  <i class="bi bi-reddit me-2"></i>Share on Reddit
51
50
  </a>
52
51
  </li>
53
52
  <li>
54
- <a class="dropdown-item" href="https://www.linkedin.com/sharing/share-offsite/?url={{ page.url | absolute_url | url_encode }}" target="_blank">
53
+ <a class="dropdown-item" href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.url | append: page.url | url_encode }}" target="_blank">
55
54
  <i class="bi bi-linkedin me-2"></i>Share on LinkedIn
56
55
  </a>
57
56
  </li>
58
57
  <li>
59
- <a class="dropdown-item" href="https://twitter.com/intent/tweet?url={{ page.url | absolute_url | url_encode }}&text={{ page.title | url_encode }}" target="_blank">
58
+ <a class="dropdown-item" href="https://twitter.com/intent/tweet?url={{ site.url | append: page.url | url_encode }}&text={{ page.title | url_encode }}" target="_blank">
60
59
  <i class="bi bi-twitter me-2"></i>Share on Twitter
61
60
  </a>
62
61
  </li>
63
62
  <li><hr class="dropdown-divider"></li>
64
63
  <li>
65
- <button class="dropdown-item" onclick='navigator.clipboard.writeText("{{ page.url | absolute_url }}"); alert("Link copied to clipboard!");'>
64
+ <button class="dropdown-item" onclick='navigator.clipboard.writeText("{{ site.url | append: page.url }}"); alert("Link copied to clipboard!");'>
66
65
  <i class="bi bi-clipboard me-2"></i>Copy Link
67
66
  </button>
68
67
  </li>
@@ -13,6 +13,7 @@
13
13
  - Offcanvas sidebars for mobile navigation
14
14
  - Brand logo and home navigation integration
15
15
  - Settings modal and navigation toggles
16
+ - Skip-to-content link for accessibility
16
17
 
17
18
  Dependencies:
18
19
  - branding.html: Site title and logo display
@@ -24,10 +25,23 @@
24
25
  - Mobile: Collapsed navigation with hamburger menu
25
26
  - Offcanvas panels for sidebar and navigation
26
27
 
28
+ Accessibility:
29
+ - Skip-to-content link for keyboard navigation
30
+ - ARIA labels for screen readers
31
+ - Focus management for offcanvas
32
+
27
33
  TODO: Fix Nanobar progress bar positioning and animation
28
34
  ===================================================================
29
35
  -->
30
36
 
37
+ <!-- ================================ -->
38
+ <!-- SKIP TO CONTENT LINK -->
39
+ <!-- ================================ -->
40
+ <!-- Accessibility: allows keyboard users to skip navigation -->
41
+ <a href="#main-content" class="visually-hidden-focusable position-absolute top-0 start-0 z-3 m-3 btn btn-primary">
42
+ Skip to main content
43
+ </a>
44
+
31
45
  <header id="navbar" class="container-fluid text-center fixed-top" role="navigation">
32
46
 
33
47
  <!-- ================================ -->
@@ -1,18 +1,29 @@
1
1
  <!-- sidebar-categories.html -->
2
- <div class="d-flex flex-column flex-shrink-0" style="width: 280px;">
3
- <a href="/" class="d-flex align-items-center text-decoration-none">
2
+ <div class="d-flex flex-column flex-shrink-0 w-100">
3
+ <a href="/" class="d-flex align-items-center text-decoration-none mb-3">
4
+ <i class="bi bi-folder2-open me-2 fs-4"></i>
4
5
  <span class="fs-4">Categories</span>
5
6
  </a>
6
- <hr>
7
+ <hr class="my-2">
7
8
  {% for category in site.categories %}
8
- <div class="">
9
- <button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#{{category[0]}}-list" aria-expanded="false">
10
- {{ category[0] }}
9
+ <div class="mb-1">
10
+ <button class="btn btn-toggle d-flex align-items-center w-100 rounded collapsed"
11
+ data-bs-toggle="collapse"
12
+ data-bs-target="#{{category[0] | slugify}}-list"
13
+ aria-expanded="false"
14
+ aria-controls="{{category[0] | slugify}}-list">
15
+ <i class="bi bi-folder me-2"></i>
16
+ <span>{{ category[0] }}</span>
11
17
  </button>
12
- <div class="collapse" id="{{category[0]}}-list">
13
- <ul class="btn-toggle-nav list-unstyled fw-normal small">
18
+ <div class="collapse" id="{{category[0] | slugify}}-list">
19
+ <ul class="btn-toggle-nav list-unstyled fw-normal small ps-4">
14
20
  {% for post in category[1] %}
15
- <li class="link-light"><a href="{{ post.url }}" class=" rounded">{{ post.title }}</a></li>
21
+ <li class="py-1">
22
+ <a href="{{ post.url | relative_url }}" class="text-decoration-none rounded d-block py-1 px-2{% if page.url == post.url %} active{% endif %}">
23
+ <i class="bi bi-file-earmark-text me-1"></i>
24
+ {{ post.title }}
25
+ </a>
26
+ </li>
16
27
  {% endfor %}
17
28
  </ul>
18
29
  </div>
@@ -11,12 +11,13 @@
11
11
  - Gets the current page's collection
12
12
  - Sorts documents by path to maintain folder hierarchy
13
13
  - Displays folder names and document links
14
- - Uses Bootstrap list-group for styling
14
+ - Uses Bootstrap list-group for styling with Bootstrap Icons
15
15
 
16
16
  Dependencies:
17
17
  - page.collection: Current collection name from page front matter
18
18
  - site.collections: Jekyll collections configuration
19
19
  - Bootstrap 5 list-group component
20
+ - Bootstrap Icons for folder/file indicators
20
21
 
21
22
  Usage:
22
23
  - Include in sidebar when page.sidebar.nav == "dynamic"
@@ -27,8 +28,8 @@
27
28
  {% assign root_folder = site.collections | where: "label", page.collection | first %}
28
29
 
29
30
  {% if root_folder %}
30
- <h2 class="h5 mb-3">
31
- <i class="bi bi-folder me-2"></i>
31
+ <h2 class="h5 mb-3 d-flex align-items-center">
32
+ <i class="bi bi-folder2-open me-2"></i>
32
33
  {{ root_folder.label | capitalize }}
33
34
  </h2>
34
35
 
@@ -42,8 +43,8 @@
42
43
  {% if current_path != prev_path %}
43
44
  {% for folder in current_path %}
44
45
  {% if forloop.index != 1 %}
45
- <li class="folder list-group-item bg-body-tertiary fw-semibold">
46
- <i class="bi bi-folder2 me-1"></i>
46
+ <li class="folder list-group-item bg-body-tertiary fw-semibold d-flex align-items-center">
47
+ <i class="bi bi-folder2 me-2"></i>
47
48
  {{ folder }}
48
49
  </li>
49
50
  {% endif %}
@@ -52,8 +53,8 @@
52
53
  {% endif %}
53
54
 
54
55
  <li class="file list-group-item list-group-item-action{% if page.url == doc.url %} active{% endif %}">
55
- <a href="{{ doc.url | relative_url }}" class="text-decoration-none">
56
- <i class="bi bi-file-text me-1"></i>
56
+ <a href="{{ doc.url | relative_url }}" class="text-decoration-none d-flex align-items-center">
57
+ <i class="bi bi-file-earmark-text me-2"></i>
57
58
  {{ doc.title }}
58
59
  </a>
59
60
  </li>
@@ -4,11 +4,17 @@
4
4
  includes: nav_list.html, content/toc.html
5
5
  -->
6
6
 
7
- <!-- TOC button in mobile view -->
8
- <div class="d-flex justify-content-end position-fixed top-50 end-0 translate-middle-y me-3">
9
-
10
- <button class="btn bg-opacity-2 bd-toc-toggle d-lg-none border z-2" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocContents" aria-controls="tocContents">
11
- <i class="bi bi-list-ul"></i>
7
+ <!-- TOC button in mobile view - Floating Action Button pattern -->
8
+ <!-- Positioned above back-to-top button to prevent overlap -->
9
+ <div class="d-lg-none position-fixed bd-toc-fab" style="bottom: 90px; right: 20px; z-index: 1030;">
10
+ <button class="btn btn-primary rounded-circle shadow-lg bd-toc-toggle"
11
+ type="button"
12
+ data-bs-toggle="offcanvas"
13
+ data-bs-target="#tocContents"
14
+ aria-controls="tocContents"
15
+ aria-label="Toggle Table of Contents"
16
+ style="width: 56px; height: 56px;">
17
+ <i class="bi bi-list-ul fs-5"></i>
12
18
  </button>
13
19
  </div>
14
20
 
@@ -20,11 +26,11 @@
20
26
  </div>
21
27
  <div class="offcanvas-body">
22
28
  <!-- Table of contents -->
23
- <nav id="TableOfContents">
24
- <!-- TOC tile -->
25
- <div>
26
- <strong>
27
- <i class="fas fa-file-alt"></i>
29
+ <nav id="TableOfContents" role="navigation" aria-label="Table of Contents">
30
+ <!-- TOC title -->
31
+ <div class="mb-3">
32
+ <strong class="d-flex align-items-center">
33
+ <i class="bi bi-file-text me-2"></i>
28
34
  On this page
29
35
  </strong>
30
36
  </div>
data/_layouts/blog.html CHANGED
@@ -105,15 +105,13 @@ source: "https://getbootstrap.com/docs/5.3/examples/blog/#"
105
105
  </a>
106
106
  </div>
107
107
  <div class="col-lg-6">
108
- {% if hero_post.preview %}
109
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ hero_post.preview }}"
110
- class="w-100 h-100" style="object-fit: cover; min-height: 400px;"
111
- alt="{{ hero_post.title }}" loading="lazy">
112
- {% else %}
113
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.og_image }}"
114
- class="w-100 h-100" style="object-fit: cover; min-height: 400px;"
115
- alt="Featured image" loading="lazy">
116
- {% endif %}
108
+ {% assign hero_img = hero_post.preview | default: site.og_image %}
109
+ {% include components/preview-image.html
110
+ src=hero_img
111
+ alt=hero_post.title
112
+ class="w-100 h-100"
113
+ style="object-fit: cover; min-height: 400px;"
114
+ %}
117
115
  </div>
118
116
  </div>
119
117
  </section>
@@ -162,15 +160,8 @@ source: "https://getbootstrap.com/docs/5.3/examples/blog/#"
162
160
  <i class="bi bi-star-fill me-1"></i>Featured
163
161
  </span>
164
162
  <a href="{{ main_featured.url | relative_url }}">
165
- {% if main_featured.preview %}
166
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ main_featured.preview }}"
167
- class="card-img-top" style="height: 300px; object-fit: cover;"
168
- alt="{{ main_featured.title }}" loading="lazy">
169
- {% else %}
170
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
171
- class="card-img-top" style="height: 300px; object-fit: cover;"
172
- alt="Preview" loading="lazy">
173
- {% endif %}
163
+ {% assign main_img = main_featured.preview | default: site.teaser %}
164
+ {% include components/preview-image.html src=main_img alt=main_featured.title class="card-img-top" style="height: 300px; object-fit: cover;" %}
174
165
  </a>
175
166
  </div>
176
167
  <div class="card-body">
@@ -206,15 +197,8 @@ source: "https://getbootstrap.com/docs/5.3/examples/blog/#"
206
197
  <div class="col">
207
198
  <div class="card h-100 border-0 shadow-sm hover-lift">
208
199
  <a href="{{ fpost.url | relative_url }}">
209
- {% if fpost.preview %}
210
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ fpost.preview }}"
211
- class="card-img-top" style="height: 120px; object-fit: cover;"
212
- alt="{{ fpost.title }}" loading="lazy">
213
- {% else %}
214
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
215
- class="card-img-top" style="height: 120px; object-fit: cover;"
216
- alt="Preview" loading="lazy">
217
- {% endif %}
200
+ {% assign fpost_img = fpost.preview | default: site.teaser %}
201
+ {% include components/preview-image.html src=fpost_img alt=fpost.title class="card-img-top" style="height: 120px; object-fit: cover;" %}
218
202
  </a>
219
203
  <div class="card-body py-3">
220
204
  <h6 class="card-title mb-1">
@@ -254,15 +238,8 @@ source: "https://getbootstrap.com/docs/5.3/examples/blog/#"
254
238
  <div class="col-md-6 col-lg-3">
255
239
  <div class="card h-100 border-0 shadow-sm hover-lift">
256
240
  <a href="{{ cpost.url | relative_url }}">
257
- {% if cpost.preview %}
258
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ cpost.preview }}"
259
- class="card-img-top" style="height: 140px; object-fit: cover;"
260
- alt="{{ cpost.title }}" loading="lazy">
261
- {% else %}
262
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
263
- class="card-img-top" style="height: 140px; object-fit: cover;"
264
- alt="Preview" loading="lazy">
265
- {% endif %}
241
+ {% assign cpost_img = cpost.preview | default: site.teaser %}
242
+ {% include components/preview-image.html src=cpost_img alt=cpost.title class="card-img-top" style="height: 140px; object-fit: cover;" %}
266
243
  </a>
267
244
  <div class="card-body">
268
245
  <h6 class="card-title">
@@ -305,15 +282,8 @@ source: "https://getbootstrap.com/docs/5.3/examples/blog/#"
305
282
  <div class="row g-0">
306
283
  <div class="col-4">
307
284
  <a href="{{ lpost.url | relative_url }}">
308
- {% if lpost.preview %}
309
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ lpost.preview }}"
310
- class="rounded-start h-100 w-100" style="object-fit: cover; min-height: 120px;"
311
- alt="{{ lpost.title }}" loading="lazy">
312
- {% else %}
313
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
314
- class="rounded-start h-100 w-100" style="object-fit: cover; min-height: 120px;"
315
- alt="Preview" loading="lazy">
316
- {% endif %}
285
+ {% assign lpost_img = lpost.preview | default: site.teaser %}
286
+ {% include components/preview-image.html src=lpost_img alt=lpost.title class="rounded-start h-100 w-100" style="object-fit: cover; min-height: 120px;" %}
317
287
  </a>
318
288
  </div>
319
289
  <div class="col-8">
@@ -111,18 +111,8 @@ layout: default
111
111
  </span>
112
112
  {% endif %}
113
113
  <a href="{{ fpost.url | relative_url }}" class="text-decoration-none">
114
- {% if fpost.preview %}
115
- {% if fpost.preview contains '/assets' %}
116
- <img src="{{ site.baseurl }}{{ fpost.preview }}"
117
- class="card-img-top" alt="{{ fpost.title }}" loading="lazy">
118
- {% else %}
119
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ fpost.preview }}"
120
- class="card-img-top" alt="{{ fpost.title }}" loading="lazy">
121
- {% endif %}
122
- {% else %}
123
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
124
- class="card-img-top" alt="Preview" loading="lazy">
125
- {% endif %}
114
+ {% assign fpost_img = fpost.preview | default: site.teaser %}
115
+ {% include components/preview-image.html src=fpost_img alt=fpost.title class="card-img-top" %}
126
116
  </a>
127
117
  </div>
128
118
  <div class="card-body">
@@ -171,18 +161,8 @@ layout: default
171
161
  </span>
172
162
  {% endif %}
173
163
  <a href="{{ cpost.url | relative_url }}" class="text-decoration-none">
174
- {% if cpost.preview %}
175
- {% if cpost.preview contains '/assets' %}
176
- <img src="{{ site.baseurl }}{{ cpost.preview }}"
177
- class="card-img-top" alt="{{ cpost.title }}" loading="lazy">
178
- {% else %}
179
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ cpost.preview }}"
180
- class="card-img-top" alt="{{ cpost.title }}" loading="lazy">
181
- {% endif %}
182
- {% else %}
183
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
184
- class="card-img-top" alt="Preview" loading="lazy">
185
- {% endif %}
164
+ {% assign cpost_img = cpost.preview | default: site.teaser %}
165
+ {% include components/preview-image.html src=cpost_img alt=cpost.title class="card-img-top" %}
186
166
  </a>
187
167
  </div>
188
168
  <div class="card-body d-flex flex-column">
@@ -83,18 +83,8 @@ layout: default
83
83
  <!-- CARD IMAGE SECTION -->
84
84
  <!-- ====================== -->
85
85
  <!-- Display preview image with fallback to site teaser -->
86
- {% if post.preview %}
87
- {% if post.preview contains '/assets' %}
88
- <img src="{{ site.baseurl }}{{ post.preview }}"
89
- class="card-img-top" alt="Preview image for {{ post.title }}">
90
- {% else %}
91
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ post.preview }}"
92
- class="card-img-top" alt="Preview image for {{ post.title }}">
93
- {% endif %}
94
- {% else %}
95
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
96
- class="card-img-top" alt="Default preview image">
97
- {% endif %}
86
+ {% assign coll_img = post.preview | default: site.teaser %}
87
+ {% include components/preview-image.html src=coll_img alt=post.title class="card-img-top" %}
98
88
 
99
89
  <!-- ====================== -->
100
90
  <!-- CARD CONTENT SECTION -->
@@ -61,7 +61,7 @@ layout: root
61
61
  <!-- MAIN CONTENT AREA -->
62
62
  <!-- ================================ -->
63
63
  <!-- Primary content section with scroll spy for table of contents navigation -->
64
- <main class="bd-main order-1" data-spy="scroll" data-bs-target="toc-content" data-offset="0">
64
+ <main class="bd-main order-1" data-bs-spy="scroll" data-bs-target="#TableOfContents" data-bs-offset="100" data-bs-smooth-scroll="true">
65
65
 
66
66
  <!-- Page introduction: title, breadcrumbs, metadata -->
67
67
  {% include content/intro.html %}
@@ -188,18 +188,8 @@ layout: default
188
188
  <div class="col">
189
189
  <div class="card h-100 border-0 shadow-sm">
190
190
  <a href="{{ rpost.url | relative_url }}" class="text-decoration-none">
191
- {% if rpost.preview %}
192
- {% if rpost.preview contains '/assets' %}
193
- <img src="{{ site.baseurl }}{{ rpost.preview }}"
194
- class="card-img-top" alt="{{ rpost.title }}" loading="lazy">
195
- {% else %}
196
- <img src="{{ site.baseurl }}/{{ site.public_folder }}/{{ rpost.preview }}"
197
- class="card-img-top" alt="{{ rpost.title }}" loading="lazy">
198
- {% endif %}
199
- {% else %}
200
- <img src="{{ site.baseurl }}/{{ site.public_folder }}{{ site.teaser }}"
201
- class="card-img-top" alt="Preview" loading="lazy">
202
- {% endif %}
191
+ {% assign rpost_img = rpost.preview | default: site.teaser %}
192
+ {% include components/preview-image.html src=rpost_img alt=rpost.title class="card-img-top" %}
203
193
  </a>
204
194
  <div class="card-body">
205
195
  <h6 class="card-title mb-1">