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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +196 -0
- data/README.md +66 -19
- data/_data/navigation/admin.yml +53 -0
- data/_data/theme_backgrounds.yml +121 -0
- data/_includes/components/admin-tabs.html +59 -0
- data/_includes/components/analytics-dashboard.html +232 -0
- data/_includes/components/background-customizer.html +159 -0
- data/_includes/components/background-settings.html +137 -0
- data/_includes/components/collection-manager.html +151 -0
- data/_includes/components/component-showcase.html +452 -0
- data/_includes/components/config-editor.html +207 -0
- data/_includes/components/config-viewer.html +479 -0
- data/_includes/components/cookie-consent.html +35 -100
- data/_includes/components/env-dashboard.html +154 -0
- data/_includes/components/feature-card.html +94 -0
- data/_includes/components/info-section.html +172 -149
- data/_includes/components/js-cdn.html +4 -1
- data/_includes/components/nanobar.html +117 -0
- data/_includes/components/nav-editor.html +99 -0
- data/_includes/components/setup-banner.html +28 -0
- data/_includes/components/setup-check.html +53 -0
- data/_includes/components/svg-background.html +42 -0
- data/_includes/components/theme-customizer.html +46 -0
- data/_includes/content/seo.html +68 -135
- data/_includes/core/footer.html +10 -12
- data/_includes/core/head.html +7 -27
- data/_includes/core/header.html +30 -17
- data/_includes/landing/landing-install-cards.html +18 -7
- data/_includes/navigation/admin-nav.html +95 -0
- data/_includes/navigation/navbar.html +45 -6
- data/_includes/navigation/sidebar-left.html +1 -1
- data/_includes/setup/wizard.html +330 -0
- data/_layouts/admin.html +166 -0
- data/_layouts/landing.html +15 -9
- data/_layouts/root.html +12 -6
- data/_layouts/setup.html +73 -0
- data/_plugins/preview_image_generator.rb +26 -12
- data/_sass/core/_navbar.scss +8 -2
- data/_sass/custom.scss +65 -10
- data/_sass/theme/_background-mixins.scss +95 -0
- data/_sass/theme/_backgrounds.scss +156 -0
- data/_sass/theme/_color-modes.scss +2 -1
- data/assets/backgrounds/gradients/air.svg +15 -0
- data/assets/backgrounds/gradients/aqua.svg +15 -0
- data/assets/backgrounds/gradients/contrast.svg +15 -0
- data/assets/backgrounds/gradients/dark.svg +15 -0
- data/assets/backgrounds/gradients/dirt.svg +15 -0
- data/assets/backgrounds/gradients/mint.svg +15 -0
- data/assets/backgrounds/gradients/neon.svg +15 -0
- data/assets/backgrounds/gradients/plum.svg +15 -0
- data/assets/backgrounds/gradients/sunrise.svg +15 -0
- data/assets/backgrounds/noise/air.svg +8 -0
- data/assets/backgrounds/noise/aqua.svg +8 -0
- data/assets/backgrounds/noise/contrast.svg +8 -0
- data/assets/backgrounds/noise/dark.svg +8 -0
- data/assets/backgrounds/noise/dirt.svg +8 -0
- data/assets/backgrounds/noise/mint.svg +8 -0
- data/assets/backgrounds/noise/neon.svg +8 -0
- data/assets/backgrounds/noise/plum.svg +8 -0
- data/assets/backgrounds/noise/sunrise.svg +8 -0
- data/assets/backgrounds/patterns/air.svg +7 -0
- data/assets/backgrounds/patterns/aqua.svg +7 -0
- data/assets/backgrounds/patterns/contrast.svg +4 -0
- data/assets/backgrounds/patterns/dark.svg +5 -0
- data/assets/backgrounds/patterns/dirt.svg +5 -0
- data/assets/backgrounds/patterns/mint.svg +6 -0
- data/assets/backgrounds/patterns/neon.svg +6 -0
- data/assets/backgrounds/patterns/plum.svg +6 -0
- data/assets/backgrounds/patterns/sunrise.svg +5 -0
- data/assets/js/background-customizer.js +73 -0
- data/assets/js/code-copy.js +18 -47
- data/assets/js/config-utility.js +307 -0
- data/assets/js/nanobar-init.js +63 -0
- data/assets/js/nav-editor.js +39 -0
- data/assets/js/palette-generator.js +415 -0
- data/assets/js/search-modal.js +31 -11
- data/assets/js/setup-wizard.js +306 -0
- data/assets/js/skin-editor.js +645 -0
- data/assets/js/theme-customizer.js +102 -0
- data/assets/js/ui-enhancements.js +2 -24
- data/assets/vendor/bootstrap/css/bootstrap.min.css +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +1 -0
- data/scripts/README.md +45 -0
- data/scripts/features/generate-preview-images +297 -7
- data/scripts/features/install-preview-generator +51 -33
- data/scripts/fork-cleanup.sh +92 -19
- data/scripts/github-setup.sh +284 -0
- data/scripts/init_setup.sh +0 -1
- data/scripts/lib/frontmatter.sh +543 -0
- data/scripts/lib/migrate.sh +265 -0
- data/scripts/lib/preview_generator.py +607 -32
- data/scripts/lint-pages +508 -0
- data/scripts/migrate.sh +201 -0
- data/scripts/platform/setup-linux.sh +244 -0
- data/scripts/platform/setup-macos.sh +187 -0
- data/scripts/platform/setup-wsl.sh +196 -0
- 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 →</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>
|
data/_includes/content/seo.html
CHANGED
|
@@ -1,142 +1,75 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
{%-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
{%- assign
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{%-
|
|
24
|
-
{%-
|
|
25
|
-
|
|
26
|
-
{%-
|
|
27
|
-
{%-
|
|
28
|
-
{%-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{%-
|
|
32
|
-
{%-
|
|
33
|
-
{%-
|
|
34
|
-
{%-
|
|
35
|
-
|
|
36
|
-
{%-
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{%-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{
|
|
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
|
-
|
|
74
|
+
{% endif %}
|
|
75
|
+
<!-- end _includes/content/seo.html -->
|
data/_includes/core/footer.html
CHANGED
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
===================================================================
|
|
31
31
|
-->
|
|
32
32
|
|
|
33
|
-
<footer class="bd-footer
|
|
33
|
+
<footer class="bd-footer border-top" role="contentinfo">
|
|
34
34
|
<!-- Powered by Row -->
|
|
35
|
-
<div class="container
|
|
36
|
-
<ul class="nav
|
|
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
|
© {{ 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="
|
|
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="
|
|
72
|
-
<div class="container
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
</div>
|
|
165
|
+
<!-- Bottom: Copyright -->
|
|
166
|
+
<div class="text-center">
|
|
167
|
+
<p class="mb-0">© {{ site.time | date: "%Y" }} {{ site.title | default: site.name }}. All Rights Reserved.</p>
|
|
170
168
|
</div>
|
|
171
169
|
</div>
|
|
172
170
|
</div>
|