jekyll-theme-zer0 0.22.5 → 0.22.20

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +196 -0
  3. data/README.md +66 -19
  4. data/_data/navigation/admin.yml +53 -0
  5. data/_data/theme_backgrounds.yml +121 -0
  6. data/_includes/components/admin-tabs.html +59 -0
  7. data/_includes/components/analytics-dashboard.html +232 -0
  8. data/_includes/components/background-customizer.html +159 -0
  9. data/_includes/components/background-settings.html +137 -0
  10. data/_includes/components/collection-manager.html +151 -0
  11. data/_includes/components/component-showcase.html +452 -0
  12. data/_includes/components/config-editor.html +207 -0
  13. data/_includes/components/config-viewer.html +479 -0
  14. data/_includes/components/cookie-consent.html +35 -100
  15. data/_includes/components/env-dashboard.html +154 -0
  16. data/_includes/components/feature-card.html +94 -0
  17. data/_includes/components/info-section.html +172 -149
  18. data/_includes/components/js-cdn.html +4 -1
  19. data/_includes/components/nanobar.html +117 -0
  20. data/_includes/components/nav-editor.html +99 -0
  21. data/_includes/components/setup-banner.html +28 -0
  22. data/_includes/components/setup-check.html +53 -0
  23. data/_includes/components/svg-background.html +42 -0
  24. data/_includes/components/theme-customizer.html +46 -0
  25. data/_includes/content/seo.html +68 -135
  26. data/_includes/core/footer.html +10 -12
  27. data/_includes/core/head.html +7 -27
  28. data/_includes/core/header.html +30 -17
  29. data/_includes/landing/landing-install-cards.html +18 -7
  30. data/_includes/navigation/admin-nav.html +95 -0
  31. data/_includes/navigation/navbar.html +45 -6
  32. data/_includes/navigation/sidebar-left.html +1 -1
  33. data/_includes/setup/wizard.html +330 -0
  34. data/_layouts/admin.html +166 -0
  35. data/_layouts/landing.html +15 -9
  36. data/_layouts/root.html +12 -6
  37. data/_layouts/setup.html +73 -0
  38. data/_plugins/preview_image_generator.rb +26 -12
  39. data/_sass/core/_navbar.scss +8 -2
  40. data/_sass/custom.scss +65 -10
  41. data/_sass/theme/_background-mixins.scss +95 -0
  42. data/_sass/theme/_backgrounds.scss +156 -0
  43. data/_sass/theme/_color-modes.scss +2 -1
  44. data/assets/backgrounds/gradients/air.svg +15 -0
  45. data/assets/backgrounds/gradients/aqua.svg +15 -0
  46. data/assets/backgrounds/gradients/contrast.svg +15 -0
  47. data/assets/backgrounds/gradients/dark.svg +15 -0
  48. data/assets/backgrounds/gradients/dirt.svg +15 -0
  49. data/assets/backgrounds/gradients/mint.svg +15 -0
  50. data/assets/backgrounds/gradients/neon.svg +15 -0
  51. data/assets/backgrounds/gradients/plum.svg +15 -0
  52. data/assets/backgrounds/gradients/sunrise.svg +15 -0
  53. data/assets/backgrounds/noise/air.svg +8 -0
  54. data/assets/backgrounds/noise/aqua.svg +8 -0
  55. data/assets/backgrounds/noise/contrast.svg +8 -0
  56. data/assets/backgrounds/noise/dark.svg +8 -0
  57. data/assets/backgrounds/noise/dirt.svg +8 -0
  58. data/assets/backgrounds/noise/mint.svg +8 -0
  59. data/assets/backgrounds/noise/neon.svg +8 -0
  60. data/assets/backgrounds/noise/plum.svg +8 -0
  61. data/assets/backgrounds/noise/sunrise.svg +8 -0
  62. data/assets/backgrounds/patterns/air.svg +7 -0
  63. data/assets/backgrounds/patterns/aqua.svg +7 -0
  64. data/assets/backgrounds/patterns/contrast.svg +4 -0
  65. data/assets/backgrounds/patterns/dark.svg +5 -0
  66. data/assets/backgrounds/patterns/dirt.svg +5 -0
  67. data/assets/backgrounds/patterns/mint.svg +6 -0
  68. data/assets/backgrounds/patterns/neon.svg +6 -0
  69. data/assets/backgrounds/patterns/plum.svg +6 -0
  70. data/assets/backgrounds/patterns/sunrise.svg +5 -0
  71. data/assets/js/background-customizer.js +73 -0
  72. data/assets/js/code-copy.js +18 -47
  73. data/assets/js/config-utility.js +307 -0
  74. data/assets/js/nanobar-init.js +63 -0
  75. data/assets/js/nav-editor.js +39 -0
  76. data/assets/js/palette-generator.js +415 -0
  77. data/assets/js/search-modal.js +31 -11
  78. data/assets/js/setup-wizard.js +306 -0
  79. data/assets/js/skin-editor.js +645 -0
  80. data/assets/js/theme-customizer.js +102 -0
  81. data/assets/js/ui-enhancements.js +2 -24
  82. data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
  83. data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
  84. data/scripts/README.md +45 -0
  85. data/scripts/features/generate-preview-images +297 -7
  86. data/scripts/features/install-preview-generator +51 -33
  87. data/scripts/fork-cleanup.sh +92 -19
  88. data/scripts/github-setup.sh +284 -0
  89. data/scripts/init_setup.sh +0 -1
  90. data/scripts/lib/frontmatter.sh +543 -0
  91. data/scripts/lib/migrate.sh +265 -0
  92. data/scripts/lib/preview_generator.py +607 -32
  93. data/scripts/lint-pages +508 -0
  94. data/scripts/migrate.sh +201 -0
  95. data/scripts/platform/setup-linux.sh +244 -0
  96. data/scripts/platform/setup-macos.sh +187 -0
  97. data/scripts/platform/setup-wsl.sh +196 -0
  98. metadata +73 -6
@@ -0,0 +1,117 @@
1
+ <!--
2
+ ===================================================================
3
+ NANOBAR - Config-Driven Page Loading Progress Bar
4
+ ===================================================================
5
+
6
+ File: nanobar.html
7
+ Path: _includes/components/nanobar.html
8
+ Purpose: Visual loading indicator shown on every page load,
9
+ fully configurable via site.nanobar in _config.yml
10
+
11
+ Template Logic:
12
+ - Conditionally renders only when site.nanobar.enabled != false
13
+ - Injects CSS custom properties from _config.yml values
14
+ - Loads nanobar.min.js (library) and nanobar-init.js (initializer)
15
+ - Passes config to JS via window.zer0Nanobar object
16
+
17
+ Dependencies:
18
+ - assets/js/nanobar.min.js — third-party Nanobar library
19
+ - assets/js/nanobar-init.js — theme initializer script
20
+ - _includes/core/header.html — #top-progress-target mount point
21
+ (rendered only when position == "navbar")
22
+
23
+ Configuration (_config.yml → nanobar):
24
+ enabled : true | false # master switch
25
+ color : CSS color value # bar fill colour
26
+ background : CSS color value # track background
27
+ height : CSS length # bar thickness
28
+ position : top | bottom | navbar # placement mode
29
+ z_index : integer # stacking order
30
+ steps : [int, …] # progress percentages
31
+ step_delay_ms : integer # ms between steps
32
+ classname : string # CSS class on wrapper
33
+ id : string # DOM id
34
+ target : CSS selector | "" # explicit mount target
35
+
36
+ Performance Notes:
37
+ - Both scripts loaded with `defer` to avoid blocking rendering
38
+ - CSS custom properties keep the inline <style> minimal
39
+ - Library injects its own baseline CSS; theme overrides via
40
+ specificity and !important where needed
41
+
42
+ Library: https://github.com/jacoborus/nanobar
43
+ ===================================================================
44
+ -->
45
+
46
+ {%- assign nb = site.nanobar | default: empty -%}
47
+ {%- if nb == empty or nb.enabled != false -%}
48
+
49
+ {%- comment -%} ── Resolve config with safe defaults ── {%- endcomment -%}
50
+ {%- assign nb_color = nb.color | default: "var(--bs-primary)" -%}
51
+ {%- assign nb_bg = nb.background | default: "transparent" -%}
52
+ {%- assign nb_height = nb.height | default: "3px" -%}
53
+ {%- assign nb_position = nb.position | default: "top" -%}
54
+ {%- assign nb_zindex = nb.z_index | default: 9999 -%}
55
+ {%- assign nb_classname = nb.classname | default: "nanobar" -%}
56
+ {%- assign nb_id = nb.id | default: "top-progress-bar" -%}
57
+ {%- assign nb_target = nb.target | default: "" -%}
58
+ {%- assign nb_step_delay = nb.step_delay_ms | default: 0 -%}
59
+ {%- assign nb_steps = nb.steps -%}
60
+ {%- if nb_steps == nil or nb_steps == empty -%}
61
+ {%- assign nb_steps = "30,76,100" | split: "," -%}
62
+ {%- endif -%}
63
+
64
+ <!-- ── Nanobar theme CSS (config-driven custom properties) ── -->
65
+ <style id="nanobar-theme">
66
+ :root {
67
+ --nanobar-color: {{ nb_color }};
68
+ --nanobar-bg: {{ nb_bg }};
69
+ --nanobar-height: {{ nb_height }};
70
+ --nanobar-z: {{ nb_zindex }};
71
+ }
72
+
73
+ /* Override library defaults with theme values */
74
+ .{{ nb_classname }} {
75
+ height: var(--nanobar-height) !important;
76
+ background: var(--nanobar-bg);
77
+ z-index: var(--nanobar-z);
78
+ }
79
+ .{{ nb_classname }} .bar {
80
+ background: var(--nanobar-color) !important;
81
+ transition: width .2s ease, height .3s ease;
82
+ }
83
+
84
+ /* Position modifiers */
85
+ .{{ nb_classname }}--bottom { top: auto !important; bottom: 0; }
86
+
87
+ /* Navbar mount: inline strip under the header */
88
+ .nanobar-mount {
89
+ position: relative;
90
+ width: 100%;
91
+ height: var(--nanobar-height);
92
+ overflow: hidden;
93
+ }
94
+ .nanobar-mount > .{{ nb_classname }}--navbar {
95
+ position: absolute !important;
96
+ top: 0; left: 0; right: 0;
97
+ width: 100%;
98
+ }
99
+ </style>
100
+
101
+ <!-- ── Nanobar scripts ── -->
102
+ <script defer src="{{ '/assets/js/nanobar.min.js' | relative_url }}"></script>
103
+ <script defer src="{{ '/assets/js/nanobar-init.js' | relative_url }}"></script>
104
+
105
+ <!-- ── Bridge _config.yml values to JS ── -->
106
+ <script>
107
+ window.zer0Nanobar = {
108
+ classname: {{ nb_classname | jsonify }},
109
+ id: {{ nb_id | jsonify }},
110
+ position: {{ nb_position | jsonify }},
111
+ target: {{ nb_target | jsonify }},
112
+ steps: [{% for s in nb_steps %}{{ s | strip | plus: 0 }}{% unless forloop.last %},{% endunless %}{% endfor %}],
113
+ stepDelay: {{ nb_step_delay | plus: 0 }}
114
+ };
115
+ </script>
116
+
117
+ {%- endif -%}
@@ -0,0 +1,99 @@
1
+ <!--
2
+ ===================================================================
3
+ NAV EDITOR — Read-only navigation tree view
4
+ ===================================================================
5
+
6
+ File: nav-editor.html
7
+ Path: _includes/components/nav-editor.html
8
+ Purpose: Renders all navigation data files as expandable card trees.
9
+ Each nav file becomes a card; items with children are nested.
10
+
11
+ Dependencies: Bootstrap 5 card/list-group, Bootstrap Icons,
12
+ _data/navigation/*.yml
13
+ Used by: pages/_about/settings/navigation.md
14
+ ===================================================================
15
+ -->
16
+
17
+ {% assign nav_files = "main,home,about,docs,posts,quickstart,admin" | split: "," %}
18
+
19
+ <div class="row g-3 mb-3">
20
+ <div class="col-lg-4">
21
+ <div class="card text-center border-primary">
22
+ <div class="card-body py-3">
23
+ <i class="bi bi-signpost-2 fs-3 text-primary"></i>
24
+ <div class="fw-semibold mt-1">{{ nav_files | size }}</div>
25
+ <small class="text-body-secondary">Navigation Files</small>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <div class="col-lg-4">
30
+ <div class="card text-center border-primary">
31
+ <div class="card-body py-3">
32
+ <i class="bi bi-list-nested fs-3 text-primary"></i>
33
+ {% assign total_items = 0 %}
34
+ {% for nf in nav_files %}
35
+ {% assign items = site.data.navigation[nf] %}
36
+ {% if items %}{% assign total_items = total_items | plus: items.size %}{% endif %}
37
+ {% endfor %}
38
+ <div class="fw-semibold mt-1">{{ total_items }}</div>
39
+ <small class="text-body-secondary">Top-Level Items</small>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ <div class="col-lg-4">
44
+ <div class="card text-center border-primary">
45
+ <div class="card-body py-3">
46
+ <i class="bi bi-folder2-open fs-3 text-primary"></i>
47
+ <div class="fw-semibold mt-1">_data/navigation/</div>
48
+ <small class="text-body-secondary">Source Directory</small>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <div class="accordion" id="navAccordion">
55
+ {% for nf in nav_files %}
56
+ {% assign items = site.data.navigation[nf] %}
57
+ {% assign nf_id = nf | replace: '.', '-' %}
58
+ <div class="accordion-item">
59
+ <h2 class="accordion-header">
60
+ <button class="accordion-button {% unless forloop.first %}collapsed{% endunless %}" type="button"
61
+ data-bs-toggle="collapse" data-bs-target="#navAcc-{{ nf_id }}">
62
+ <i class="bi bi-file-earmark-text me-2"></i>
63
+ <strong>{{ nf }}.yml</strong>
64
+ {% if items %}
65
+ <span class="badge bg-secondary ms-2">{{ items | size }} items</span>
66
+ {% else %}
67
+ <span class="badge bg-warning ms-2">not found</span>
68
+ {% endif %}
69
+ </button>
70
+ </h2>
71
+ <div id="navAcc-{{ nf_id }}" class="accordion-collapse collapse {% if forloop.first %}show{% endif %}"
72
+ data-bs-parent="#navAccordion">
73
+ <div class="accordion-body p-0">
74
+ {% if items %}
75
+ <ul class="list-group list-group-flush">
76
+ {% for item in items %}
77
+ <li class="list-group-item">
78
+ <div class="d-flex align-items-center">
79
+ {% if item.icon %}<i class="{{ item.icon }} me-2 text-primary"></i>{% endif %}
80
+ <div>
81
+ <strong>{{ item.title }}</strong>
82
+ {% if item.url %}<code class="ms-2 small">{{ item.url }}</code>{% endif %}
83
+ </div>
84
+ {% if item.external %}<span class="badge bg-info ms-auto">external</span>{% endif %}
85
+ </div>
86
+ {% if item.children %}
87
+ <small class="text-body-secondary ms-4">{{ item.children | size }} sub-items</small>
88
+ {% endif %}
89
+ </li>
90
+ {% endfor %}
91
+ </ul>
92
+ {% else %}
93
+ <div class="p-3 text-body-secondary small">No data file found for <code>{{ nf }}.yml</code></div>
94
+ {% endif %}
95
+ </div>
96
+ </div>
97
+ </div>
98
+ {% endfor %}
99
+ </div>
@@ -0,0 +1,28 @@
1
+ {% comment %}
2
+ ===================================================================
3
+ SETUP BANNER - Persistent notification for unconfigured sites
4
+ ===================================================================
5
+
6
+ File: setup-banner.html
7
+ Path: _includes/components/setup-banner.html
8
+ Purpose: Shows a dismissible top-bar when the site still needs
9
+ configuration. Disappears once site_configured is true.
10
+
11
+ Usage (add to root.html or any layout):
12
+ include components/setup-banner.html
13
+
14
+ Dependencies: components/setup-check.html (auto-included)
15
+ ===================================================================
16
+ {% endcomment %}
17
+
18
+ {% include components/setup-check.html %}
19
+
20
+ {% if site_needs_setup %}
21
+ <div class="alert alert-primary alert-dismissible fade show rounded-0 mb-0 py-2 text-center" role="alert" id="setup-banner">
22
+ <i class="bi bi-gear-wide-connected me-1"></i>
23
+ <strong>Almost there!</strong>
24
+ Your site is running with default settings.
25
+ <a href="{{ '/404.html' | relative_url }}" class="alert-link">Complete setup &rarr;</a>
26
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
27
+ </div>
28
+ {% endif %}
@@ -0,0 +1,53 @@
1
+ {% comment %}
2
+ ===================================================================
3
+ SETUP CHECK - Detect unconfigured site state
4
+ ===================================================================
5
+
6
+ File: setup-check.html
7
+ Path: _includes/components/setup-check.html
8
+ Purpose: Provides Liquid variables to detect whether the site has been
9
+ configured by the user or is still running on theme defaults.
10
+
11
+ Usage:
12
+ {% include components/setup-check.html %}
13
+ {% if site_needs_setup %}
14
+ ... show setup guidance ...
15
+ {% endif %}
16
+
17
+ Detection Logic:
18
+ The include sets site_needs_setup to true when the site appears
19
+ to be running with default/unconfigured values. It checks:
20
+ 1. Explicit site_configured: true flag (fastest path)
21
+ 2. Whether the title is still the theme default
22
+ 3. Whether founder/name are still theme defaults
23
+
24
+ Output Variables:
25
+ - site_needs_setup : true if the site hasn't been personalised
26
+ - site_is_user_repo : true if this appears to be a <user>.github.io repo
27
+ ===================================================================
28
+ {% endcomment %}
29
+
30
+ {% comment %}
31
+ ── Determine if this is a user/org site (username.github.io) ──
32
+ User sites have an empty baseurl and a url ending in .github.io
33
+ {% endcomment %}
34
+ {% assign site_is_user_repo = false %}
35
+ {% if site.baseurl == "" or site.baseurl == nil %}
36
+ {% if site.url contains ".github.io" %}
37
+ {% assign _url_path = site.url | split: "/" | last %}
38
+ {% unless _url_path contains "/" %}
39
+ {% assign site_is_user_repo = true %}
40
+ {% endunless %}
41
+ {% endif %}
42
+ {% endif %}
43
+
44
+ {% comment %}
45
+ ── Check if the site has been configured ──
46
+ Priority 1: Explicit flag in _config.yml
47
+ Priority 2: Heuristic — title/founder still match theme defaults
48
+ {% endcomment %}
49
+ {% assign site_needs_setup = false %}
50
+
51
+ {% if site.site_configured == false %}
52
+ {% assign site_needs_setup = true %}
53
+ {% endif %}
@@ -0,0 +1,42 @@
1
+ <!--
2
+ ===================================================================
3
+ SVG-BACKGROUND — Apply fffuel-style SVG background to a zone
4
+ ===================================================================
5
+
6
+ File: svg-background.html
7
+ Path: _includes/components/svg-background.html
8
+ Purpose: Adds a CSS class for the background zone and injects
9
+ per-skin CSS custom-property overrides from _config.yml.
10
+
11
+ Parameters:
12
+ zone — "hero" | "body" | "surface" | "footer" (default: "body")
13
+
14
+ Usage:
15
+ include components/svg-background.html zone="hero"
16
+ <div class="zer0-bg-hero"> ... </div>
17
+
18
+ Dependencies:
19
+ - _sass/theme/_backgrounds.scss (utility classes)
20
+ - _data/theme_backgrounds.yml (asset paths)
21
+ - _config.yml theme_background (opacity / blend settings)
22
+ ===================================================================
23
+ -->
24
+
25
+ {% assign zone = include.zone | default: "body" %}
26
+ {% assign skin = site.theme_skin | default: "dark" %}
27
+ {% assign bg = site.theme_background %}
28
+
29
+ {% comment %}
30
+ Inject runtime overrides from _config.yml into inline <style> once per page.
31
+ These override the SCSS defaults so site owners can tune via config alone.
32
+ {% endcomment %}
33
+ {% if bg.enabled != false %}
34
+ <style data-zer0-bg-overrides>
35
+ [data-theme-skin="{{ skin }}"] {
36
+ {% if bg.gradient_opacity %}--zer0-bg-gradient-opacity: {{ bg.gradient_opacity }};{% endif %}
37
+ {% if bg.texture_opacity %}--zer0-bg-texture-opacity: {{ bg.texture_opacity }};{% endif %}
38
+ {% if bg.pattern_opacity %}--zer0-bg-pattern-opacity: {{ bg.pattern_opacity }};{% endif %}
39
+ {% if bg.blend_mode %}--zer0-bg-blend: {{ bg.blend_mode }};{% endif %}
40
+ }
41
+ </style>
42
+ {% endif %}
@@ -0,0 +1,46 @@
1
+ <!--
2
+ ===================================================================
3
+ THEME CUSTOMIZER — Skin preview grid with live class swapping
4
+ ===================================================================
5
+
6
+ File: theme-customizer.html
7
+ Path: _includes/components/theme-customizer.html
8
+ Purpose: Displays all available theme skins in a card grid.
9
+ Clicking a card applies the skin class and highlights it.
10
+ Current skin comes from site.theme_skin in _config.yml.
11
+
12
+ Dependencies: Bootstrap 5 grid/card, Bootstrap Icons
13
+ Used by: pages/_about/settings/theme.md (Skin Preview tab)
14
+ ===================================================================
15
+ -->
16
+
17
+ {% assign current_skin = site.theme_skin | default: "dark" %}
18
+ {% assign skins = "air,aqua,contrast,dark,dirt,neon,mint,plum,sunrise" | split: "," %}
19
+
20
+ <p class="text-body-secondary mb-3">
21
+ Current skin: <strong>{{ current_skin }}</strong>. Click a card to preview (page-level only — does not persist).
22
+ </p>
23
+
24
+ <div class="row g-3" id="skin-grid">
25
+ {% for skin in skins %}
26
+ <div class="col-6 col-md-4 col-lg-3">
27
+ <div class="card h-100 skin-card {% if skin == current_skin %}border-primary{% else %}border-secondary{% endif %}"
28
+ data-skin="{{ skin }}" role="button" tabindex="0" style="cursor:pointer">
29
+ <div class="card-body text-center py-4">
30
+ <i class="bi bi-circle-fill fs-3 mb-2 d-block {% if skin == current_skin %}text-primary{% else %}text-body-secondary{% endif %}"></i>
31
+ <h6 class="card-title mb-1 text-capitalize">{{ skin }}</h6>
32
+ {% if skin == current_skin %}
33
+ <span class="badge bg-primary"><i class="bi bi-check-circle me-1"></i>Active</span>
34
+ {% else %}
35
+ <small class="text-body-tertiary">Click to preview</small>
36
+ {% endif %}
37
+ </div>
38
+ </div>
39
+ </div>
40
+ {% endfor %}
41
+ </div>
42
+
43
+ <div class="alert alert-info mt-3 small" role="alert">
44
+ <i class="bi bi-info-circle me-1"></i>
45
+ To permanently change the skin, update <code>theme_skin</code> in <code>_config.yml</code> or use the <strong>Export YAML</strong> tab.
46
+ </div>
@@ -1,142 +1,75 @@
1
- <!--
2
- file: seo.html
3
- path: _includes/seo.html
4
- includes: none
5
- description: SEO meta tags for the site
6
- purpose: This file is used to generate the SEO meta tags for the site. It is included in the head section of the HTML document.
1
+ <!--
2
+ ===================================================================
3
+ SEO SUPPLEMENT - Additional meta tags not covered by jekyll-seo-tag
4
+ ===================================================================
5
+
6
+ File: seo.html
7
+ Path: _includes/content/seo.html
8
+ Purpose: Outputs ONLY meta tags that the jekyll-seo-tag plugin does
9
+ not generate. The bulk of SEO (title, description, og:*,
10
+ twitter:*, canonical, JSON-LD) is handled by seo tags in
11
+ _layouts/root.html.
12
+
13
+ What this file adds:
14
+ - Custom og:image with preview_images.assets_prefix path normalisation
15
+ for the theme-specific page.preview and page.header.og_image keys.
16
+ When page.image is set, jekyll-seo-tag handles og:image and this
17
+ file skips its own og:image output to avoid duplicate tags.
18
+ - Non-Google site verification tags (Bing, Yandex, Naver, Baidu)
19
+
20
+ Dependencies:
21
+ - jekyll-seo-tag plugin (loaded in _layouts/root.html via seo tags)
22
+ - site.preview_images config in _config.yml
23
+ ===================================================================
7
24
  -->
8
25
 
9
- <!-- -->
10
-
11
- {%- assign seo_url = site.url | append: site.baseurl -%}
12
- {%- assign canonical_url = site.url %}
13
- {% assign title_separator = site.title_separator | default: '-' %}
14
- {%- assign seo_title = page.title | default: site.title | append: " " | append: title_separator | append: " " | append: site.title -%}
15
- {%- assign seo_description = page.description | default: site.description -%}
16
-
17
- {%- assign author = page.author | default: site.author -%}
18
-
19
- <!-- TODO: build author data set for SEO -->
20
-
21
- {%- assign author_twitter = site.author.twitter_username %}
22
-
23
- {%- assign page_large_image = page.header.og_image | default: page.header.overlay_image | default: page.header.image | absolute_url -%}
24
- {%- assign page_large_image = page_large_image | escape -%}
25
-
26
- {%- comment -%} Handle preview image with configurable assets prefix {%- endcomment -%}
27
- {%- assign page_teaser_image = page.preview | default: site.og_image -%}
28
- {%- assign assets_prefix = site.preview_images.assets_prefix | default: '/assets' -%}
29
- {%- assign auto_prefix = site.preview_images.auto_prefix | default: true -%}
30
-
31
- {%- if page_teaser_image contains '://' -%}
32
- {%- comment -%} External URL - use as-is {%- endcomment -%}
33
- {%- elsif auto_prefix and page_teaser_image != blank -%}
34
- {%- unless page_teaser_image contains assets_prefix -%}
35
- {%- assign page_teaser_image = assets_prefix | append: page_teaser_image -%}
36
- {%- endunless -%}
37
- {%- endif -%}
38
- {%- assign page_teaser_image = page_teaser_image | absolute_url | escape -%}
39
-
40
- {%- assign site_og_image = site.og_image | absolute_url -%}
41
- {%- assign site_og_image = site_og_image | escape -%}
42
-
43
- {%- if page.date -%}
44
- {%- assign og_type = "article" -%}
45
- {%- else -%}
46
- {%- assign og_type = "website" -%}
47
- {%- endif -%}
48
-
49
- <title>{{ seo_title | default: site.title }} {% if paginator %}{% unless paginator.page == 1 %} {{ site.data.ui-text[site.locale].page | default: "Page" }} {{ paginator.page }}{% endunless %}{% endif %}</title>
50
- <meta name="description" content="{{ seo_description }}">
51
- <meta name="author" content="{{ author.name | default: author }}">
52
-
53
- {% if og_type == "article" %}
54
- <meta property="article:author" content="{{ author.name | default: author }}">
55
- {% endif %}
56
-
57
- <meta property="og:type" content="{{ og_type }}">
58
- <meta property="og:locale" content="{{ site.locale | replace: "-", "_" | default: "en_US" }}">
59
- <meta property="og:site_name" content="{{ site.title }}">
60
- <meta property="og:title" content="{{ page.title | default: site.title | markdownify | strip_html | strip_newlines | escape_once }}">
61
- <meta property="og:url" content="{{ canonical_url }}">
62
-
63
- <meta property="og:description" content="{{ seo_description }}">
64
-
65
-
66
- {% if page_large_image %}
67
- <meta property="og:image" content="{{ page_large_image }}">
68
- {% elsif page_teaser_image %}
69
- <meta property="og:image" content="{{ page_teaser_image }}">
70
- {% endif %}
71
-
72
- {% if site.twitter.username %}
73
- <meta name="twitter:site" content="@{{ site.twitter.username | replace: "@", "" }}">
74
- <meta name="twitter:title" content="{{ page.title | default: site.title | markdownify | strip_html | strip_newlines | escape_once }}">
75
- <meta name="twitter:description" content="{{ seo_description }}">
76
- <meta name="twitter:url" content="{{ canonical_url }}">
77
-
78
- {% if page_large_image %}
79
- <meta name="twitter:card" content="summary_large_image">
80
- <meta name="twitter:image" content="{{ page_large_image }}">
81
- {% else %}
82
- <meta name="twitter:card" content="summary">
83
- {% if page_teaser_image %}
84
- <meta name="twitter:image" content="{{ page_teaser_image }}">
85
- {% endif %}
86
- {% endif %}
87
-
88
- {% if author_twitter %}
89
- <meta name="twitter:creator" content="@{{ author_twitter }}">
90
- {% endif %}
91
- {% endif %}
92
-
93
- <meta property="article:published_time" content="{{ page.date | default: page.lastmod | date_to_xmlschema }}">
94
-
95
- {% if og_type == "article" and page.last_modified_at %}
96
- <meta property="article:modified_time" content="{{ page.last_modified_at | date_to_xmlschema }}">
97
- {% endif %}
98
-
99
- {% if site.facebook %}
100
- {% if site.facebook.publisher %}
101
- <meta property="article:publisher" content="{{ site.facebook.publisher }}">
102
- {% endif %}
103
-
104
- {% if site.facebook.app_id %}
105
- <meta property="fb:app_id" content="{{ site.facebook.app_id }}">
106
- {% endif %}
107
- {% endif %}
108
-
109
- <link rel="canonical" href="{{ canonical_url }}">
110
-
111
- {% if paginator.previous_page %}
112
- <link rel="prev" href="{{ paginator.previous_page_path | absolute_url }}">
113
- {% endif %}
114
- {% if paginator.next_page %}
115
- <link rel="next" href="{{ paginator.next_page_path | absolute_url }}">
116
- {% endif %}
117
-
118
- <script type="application/ld+json">
119
- {
120
- "@context": "https://schema.org",
121
- {% if site.social.type == "Organization" %}
122
- "@type": "Organization",
123
- "url": {{ '/' | absolute_url | jsonify }}{% if site.og_image %},
124
- "logo": {{ site_og_image | jsonify }}{% endif %}
125
- {% else %}
126
- "@type": "Person",
127
- "name": {{ site.social.name | default: site.name | jsonify }},
128
- "url": {{ '/' | absolute_url |jsonify }}{% if site.social.links %},
129
- "sameAs": {{ site.social.links | jsonify }}{% endif %}
130
- {% endif %}
131
- }
132
- </script>
133
-
134
- <!-- Site verification -->
135
-
136
- <meta name="google-site-verification" content="{{ site.google_site_verification }}" />
26
+ {%- comment -%}
27
+ Custom og:image handling: supports page.preview and page.header.og_image
28
+ with automatic assets_prefix path normalisation from _config.yml.
29
+ jekyll-seo-tag handles page.image / page.image.path, so we only emit
30
+ our own og:image when those standard keys are absent.
31
+ {%- endcomment -%}
32
+
33
+ {%- unless page.image -%}
34
+ {%- assign page_large_image = page.header.og_image | default: page.header.overlay_image | default: page.header.image -%}
35
+ {%- assign page_teaser_image = page.preview | default: site.og_image -%}
36
+ {%- assign assets_prefix = site.preview_images.assets_prefix | default: '/assets' -%}
37
+ {%- assign auto_prefix = site.preview_images.auto_prefix | default: true -%}
38
+
39
+ {%- if page_teaser_image contains '://' -%}
40
+ {%- comment -%} External URL - use as-is {%- endcomment -%}
41
+ {%- elsif auto_prefix and page_teaser_image != blank -%}
42
+ {%- unless page_teaser_image contains assets_prefix -%}
43
+ {%- assign page_teaser_image = assets_prefix | append: page_teaser_image -%}
44
+ {%- endunless -%}
45
+ {%- endif -%}
46
+
47
+ {%- if page_large_image != blank -%}
48
+ {%- assign seo_image = page_large_image | absolute_url | escape -%}
49
+ {%- elsif page_teaser_image != blank -%}
50
+ {%- assign seo_image = page_teaser_image | absolute_url | escape -%}
51
+ {%- endif -%}
52
+
53
+ {%- if seo_image -%}
54
+ <meta property="og:image" content="{{ seo_image }}">
55
+ <meta name="twitter:image" content="{{ seo_image }}">
56
+ {%- endif -%}
57
+ {%- endunless -%}
58
+
59
+ <!-- Site verification (non-Google; Google is handled by jekyll-seo-tag) -->
60
+ {% if site.bing_site_verification %}
137
61
  <meta name="msvalidate.01" content="{{ site.bing_site_verification }}">
62
+ {% endif %}
63
+ {% if site.alexa_site_verification %}
138
64
  <meta name="alexaVerifyID" content="{{ site.alexa_site_verification }}">
65
+ {% endif %}
66
+ {% if site.yandex_site_verification %}
139
67
  <meta name="yandex-verification" content="{{ site.yandex_site_verification }}">
68
+ {% endif %}
69
+ {% if site.naver_site_verification %}
140
70
  <meta name="naver-site-verification" content="{{ site.naver_site_verification }}">
71
+ {% endif %}
72
+ {% if site.baidu_site_verification %}
141
73
  <meta name="baidu-site-verification" content="{{ site.baidu_site_verification }}">
142
- <!-- end _includes/seo.html -->
74
+ {% endif %}
75
+ <!-- end _includes/content/seo.html -->
@@ -30,10 +30,10 @@
30
30
  ===================================================================
31
31
  -->
32
32
 
33
- <footer class="bd-footer container-xl border-top" role="contentinfo">
33
+ <footer class="bd-footer border-top" role="contentinfo">
34
34
  <!-- Powered by Row -->
35
- <div class="container row my-3">
36
- <ul class="nav col-sm justify-content-end list-unstyled d-flex align-items-center" aria-label="Powered by technologies">
35
+ <div class="container-xl my-3">
36
+ <ul class="nav justify-content-end list-unstyled d-flex align-items-center flex-wrap" aria-label="Powered by technologies">
37
37
  <span class="align-start">
38
38
  &copy; {{ site.time | date: "%Y" }} {{ site.name | default: site.title }} — Powered by:
39
39
  </span>
@@ -56,7 +56,7 @@
56
56
  <!-- Theme Info Button -->
57
57
  <li class="btn align-items-end">
58
58
  <a href="#"
59
- data-bs-toggle="modal"
59
+ data-bs-toggle="offcanvas"
60
60
  data-bs-target="#info-section"
61
61
  aria-label="View theme information and system details"
62
62
  title="Theme Info">
@@ -67,10 +67,9 @@
67
67
  </ul>
68
68
  </div>
69
69
 
70
- <!-- Branding and Navigation Block -->
71
- <div class="container row">
72
- <div class="container bg-dark text-light py-5 rounded-3">
73
- <div class="container">
70
+ <!-- Branding and Navigation Block — full-width dark background -->
71
+ <div class="bg-dark text-light py-5">
72
+ <div class="container-xl">
74
73
  <!-- Top: Site info + Quick Links + Social -->
75
74
  <div class="row mb-4">
76
75
  <!-- Site Info -->
@@ -163,10 +162,9 @@
163
162
  </div>
164
163
  </div>
165
164
 
166
- <!-- Bottom: Copyright -->
167
- <div class="text-center">
168
- <p class="mb-0">&copy; {{ site.time | date: "%Y" }} {{ site.title | default: site.name }}. All Rights Reserved.</p>
169
- </div>
165
+ <!-- Bottom: Copyright -->
166
+ <div class="text-center">
167
+ <p class="mb-0">&copy; {{ site.time | date: "%Y" }} {{ site.title | default: site.name }}. All Rights Reserved.</p>
170
168
  </div>
171
169
  </div>
172
170
  </div>