jekyll-theme-zer0 1.26.0 → 1.28.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +558 -5
- data/README.md +10 -27
- data/_data/authors.yml +4 -3
- data/_data/backlog.yml +28 -0
- data/_data/consumers.yml +147 -0
- data/_data/features.yml +142 -23
- data/_data/i18n/fr.yml +98 -0
- data/_data/i18n/languages.yml +36 -0
- data/_data/i18n/manifest.yml +1502 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +6 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +348 -268
- data/_data/ui-text.yml +36 -246
- data/_includes/README.md +25 -2
- data/_includes/analytics/google-tag-manager-body.html +11 -2
- data/_includes/analytics/google-tag-manager-head.html +16 -6
- data/_includes/analytics/posthog.html +33 -4
- data/_includes/components/abc-letter.html +43 -0
- data/_includes/components/author-avatar-url.html +4 -2
- data/_includes/components/book-card.html +42 -0
- data/_includes/components/book-nav.html +80 -0
- data/_includes/components/book-plate.html +31 -0
- data/_includes/components/book-toc.html +47 -0
- data/_includes/components/bookshelf.html +68 -0
- data/_includes/components/card-grid.html +60 -0
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/env-switcher.html +3 -1
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/language-toggle.html +81 -0
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +1 -1
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/components/translation-notice.html +27 -0
- data/_includes/content/intro.html +16 -15
- data/_includes/content/seo.html +9 -3
- data/_includes/core/color-mode-init.html +13 -4
- data/_includes/core/favicon.html +46 -0
- data/_includes/core/footer.html +9 -4
- data/_includes/core/head.html +18 -0
- data/_includes/core/header.html +9 -4
- data/_includes/core/hreflang.html +33 -0
- data/_includes/core/i18n.html +36 -0
- data/_includes/custom/body-end.html +18 -0
- data/_includes/custom/body-start.html +17 -0
- data/_includes/custom/footer.html +18 -0
- data/_includes/custom/head.html +18 -0
- data/_includes/navigation/breadcrumbs.html +1 -1
- data/_includes/navigation/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/navbar.html +5 -4
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/sidebar-right.html +3 -2
- data/_includes/navigation/unified-drawer.html +9 -3
- data/_includes/obsidian/full-graph.html +165 -136
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +2 -0
- data/_layouts/article.html +7 -1
- data/_layouts/book-abc.html +106 -0
- data/_layouts/book-story.html +91 -0
- data/_layouts/book.html +113 -0
- data/_layouts/collection.html +17 -5
- data/_layouts/default.html +5 -3
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +159 -33
- data/_layouts/root.html +29 -9
- data/_layouts/section.html +61 -17
- data/_sass/components/_book.scss +423 -0
- data/_sass/core/_navbar.scss +13 -0
- data/_sass/core/_obsidian.scss +295 -7
- data/_sass/layouts/_navbar-extras.scss +6 -1
- data/_sass/theme/_backgrounds.scss +21 -8
- data/assets/css/main.scss +1 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/obsidian-graph.js +707 -265
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/search-modal.js +4 -1
- data/scripts/README.md +20 -26
- data/scripts/bin/audit-consumer +1 -1
- data/scripts/bin/manifest +31 -5
- data/scripts/bin/sync-plugins +0 -1
- data/scripts/bin/validate +5 -1
- data/scripts/dev/rasterize-svg.js +65 -0
- data/scripts/features/generate-preview-images +49 -1390
- data/scripts/features/install-preview-generator +55 -33
- data/scripts/install/README.md +55 -25
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- data/scripts/install/ai/prompts/wizard.system.md +8 -17
- data/scripts/install/ai/wizard.sh +10 -5
- data/scripts/install/apply.sh +7 -3
- data/scripts/install/cli.sh +54 -4
- data/scripts/install/config.sh +167 -0
- data/scripts/install/doctor.sh +38 -0
- data/scripts/install/plan.sh +10 -0
- data/scripts/install/spec.sh +15 -7
- data/scripts/install/template.sh +4 -0
- data/scripts/lib/README.md +1 -5
- data/scripts/lib/install/deploy/README.md +3 -9
- data/scripts/lib/preview_generator.py +2261 -1341
- data/scripts/propagate.rb +277 -0
- data/scripts/translate.rb +1204 -0
- metadata +34 -3
- data/_plugins/preview_image_generator.rb +0 -351
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-080 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: book-nav
|
|
4
|
+
Path: _includes/components/book-nav.html
|
|
5
|
+
Purpose: Previous/next story navigation within one book, with a
|
|
6
|
+
"Contents" link back to the book's landing page in the middle.
|
|
7
|
+
Params: book (string, required) — book slug matching chapters' `book:`
|
|
8
|
+
chapter (number, required) — the current chapter number
|
|
9
|
+
Depends on: the `books` collection,
|
|
10
|
+
_sass/components/_post-navigation.scss (.post-nav-card reuse)
|
|
11
|
+
Notes: Ordering comes from the numeric `chapter:` front matter, not
|
|
12
|
+
dates, so books read cover-to-cover. Reuses the article
|
|
13
|
+
post-nav-card styles for a consistent feel.
|
|
14
|
+
{%- endcomment -%}
|
|
15
|
+
{%- if include.book == nil or include.chapter == nil -%}
|
|
16
|
+
{%- comment -%} ERROR: book-nav requires `book` and `chapter` {%- endcomment -%}
|
|
17
|
+
{%- else -%}
|
|
18
|
+
{%- assign _chapters = site.books | where: "book", include.book | where_exp: "doc", "doc.chapter" | sort: "chapter" -%}
|
|
19
|
+
{%- assign _book_page = site.books | where: "book", include.book | where: "layout", "book" | first -%}
|
|
20
|
+
{%- assign _prev = nil -%}
|
|
21
|
+
{%- assign _next = nil -%}
|
|
22
|
+
{%- for _ch in _chapters -%}
|
|
23
|
+
{%- if _ch.chapter == include.chapter -%}
|
|
24
|
+
{%- unless forloop.first -%}
|
|
25
|
+
{%- assign _i = forloop.index0 | minus: 1 -%}
|
|
26
|
+
{%- assign _prev = _chapters[_i] -%}
|
|
27
|
+
{%- endunless -%}
|
|
28
|
+
{%- unless forloop.last -%}
|
|
29
|
+
{%- assign _i = forloop.index0 | plus: 1 -%}
|
|
30
|
+
{%- assign _next = _chapters[_i] -%}
|
|
31
|
+
{%- endunless -%}
|
|
32
|
+
{%- break -%}
|
|
33
|
+
{%- endif -%}
|
|
34
|
+
{%- endfor -%}
|
|
35
|
+
<nav class="book-story-nav my-5 pt-4 border-top" aria-label="Story navigation">
|
|
36
|
+
<div class="row g-3 align-items-stretch">
|
|
37
|
+
<div class="col-md-5">
|
|
38
|
+
{%- if _prev -%}
|
|
39
|
+
<a href="{{ _prev.url | relative_url }}" class="post-nav-card post-nav-card--prev">
|
|
40
|
+
<div class="post-nav-card__body">
|
|
41
|
+
<small class="post-nav-card__label"><i class="bi bi-chevron-left me-1" aria-hidden="true"></i>Previous story</small>
|
|
42
|
+
<span class="post-nav-card__title">{{ _prev.title | truncate: 50 }}</span>
|
|
43
|
+
</div>
|
|
44
|
+
</a>
|
|
45
|
+
{%- else -%}
|
|
46
|
+
<div class="post-nav-card post-nav-card--prev post-nav-card--disabled" aria-disabled="true">
|
|
47
|
+
<div class="post-nav-card__body">
|
|
48
|
+
<small class="post-nav-card__label"><i class="bi bi-chevron-left me-1" aria-hidden="true"></i>Previous story</small>
|
|
49
|
+
<span class="post-nav-card__title text-muted">This is the first story</span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
{%- endif -%}
|
|
53
|
+
</div>
|
|
54
|
+
<div class="col-md-2 d-flex">
|
|
55
|
+
{%- if _book_page -%}
|
|
56
|
+
<a href="{{ _book_page.url | relative_url }}" class="btn btn-outline-secondary w-100 align-self-center" aria-label="Back to {{ _book_page.title | escape }} contents">
|
|
57
|
+
<i class="bi bi-list-ul me-1" aria-hidden="true"></i>Contents
|
|
58
|
+
</a>
|
|
59
|
+
{%- endif -%}
|
|
60
|
+
</div>
|
|
61
|
+
<div class="col-md-5">
|
|
62
|
+
{%- if _next -%}
|
|
63
|
+
<a href="{{ _next.url | relative_url }}" class="post-nav-card post-nav-card--next">
|
|
64
|
+
<div class="post-nav-card__body">
|
|
65
|
+
<small class="post-nav-card__label">Next story<i class="bi bi-chevron-right ms-1" aria-hidden="true"></i></small>
|
|
66
|
+
<span class="post-nav-card__title">{{ _next.title | truncate: 50 }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
</a>
|
|
69
|
+
{%- else -%}
|
|
70
|
+
<div class="post-nav-card post-nav-card--next post-nav-card--disabled" aria-disabled="true">
|
|
71
|
+
<div class="post-nav-card__body">
|
|
72
|
+
<small class="post-nav-card__label">Next story<i class="bi bi-chevron-right ms-1" aria-hidden="true"></i></small>
|
|
73
|
+
<span class="post-nav-card__title text-muted">The end — for now</span>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
{%- endif -%}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</nav>
|
|
80
|
+
{%- endif -%}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-080 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: book-plate
|
|
4
|
+
Path: _includes/components/book-plate.html
|
|
5
|
+
Purpose: Full-width illustration "plate" inside a book story — a figure
|
|
6
|
+
with rounded art, soft shadow, and an optional caption.
|
|
7
|
+
Params: src (string, required) — image path (site-relative or /assets/…)
|
|
8
|
+
alt (string, required) — accessible description of the scene
|
|
9
|
+
caption (string, optional, default: none) — visible caption text
|
|
10
|
+
class (string, optional, default: "") — extra figure classes
|
|
11
|
+
loading (string, optional, default: "lazy")
|
|
12
|
+
Depends on: components/preview-image.html (path prefixing + relative_url),
|
|
13
|
+
_sass/components/_book.scss (.book-plate)
|
|
14
|
+
Notes: Alt text stays on the <img>; the caption renders only when
|
|
15
|
+
explicitly passed so read-aloud story text isn't duplicated.
|
|
16
|
+
{%- endcomment -%}
|
|
17
|
+
{%- if include.src == nil -%}
|
|
18
|
+
{%- comment -%} ERROR: book-plate requires `src` {%- endcomment -%}
|
|
19
|
+
{%- else -%}
|
|
20
|
+
<figure class="book-plate {{ include.class | default: '' }}">
|
|
21
|
+
{%- include components/preview-image.html
|
|
22
|
+
src=include.src
|
|
23
|
+
alt=include.alt
|
|
24
|
+
class="book-plate-img"
|
|
25
|
+
loading=include.loading
|
|
26
|
+
-%}
|
|
27
|
+
{%- if include.caption -%}
|
|
28
|
+
<figcaption>{{ include.caption | escape }}</figcaption>
|
|
29
|
+
{%- endif -%}
|
|
30
|
+
</figure>
|
|
31
|
+
{%- endif -%}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-080 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: book-toc
|
|
4
|
+
Path: _includes/components/book-toc.html
|
|
5
|
+
Purpose: Ordered table of contents for one book — chapter number bubble,
|
|
6
|
+
label (e.g. the month), title, and an optional thumbnail.
|
|
7
|
+
Params: book (string, required) — book slug matching chapters' `book:`
|
|
8
|
+
heading (string, optional, default: "Table of Contents")
|
|
9
|
+
heading_level (string, optional, default: "h2")
|
|
10
|
+
Depends on: the `books` collection,
|
|
11
|
+
_sass/components/_book.scss (.book-toc)
|
|
12
|
+
Notes: Chapters are docs with a numeric `chapter:`; sorted ascending.
|
|
13
|
+
Thumbnails come from each chapter's `preview` and are hidden on
|
|
14
|
+
the smallest screens to keep rows tappable.
|
|
15
|
+
{%- endcomment -%}
|
|
16
|
+
{%- if include.book == nil -%}
|
|
17
|
+
{%- comment -%} ERROR: book-toc requires `book` {%- endcomment -%}
|
|
18
|
+
{%- else -%}
|
|
19
|
+
{%- assign _chapters = site.books | where: "book", include.book | where_exp: "doc", "doc.chapter" | sort: "chapter" -%}
|
|
20
|
+
{%- assign _hl = include.heading_level | default: "h2" -%}
|
|
21
|
+
{%- if _chapters.size > 0 -%}
|
|
22
|
+
<nav class="book-toc" aria-label="Table of contents">
|
|
23
|
+
<{{ _hl }} class="mb-3">{{ include.heading | default: "Table of Contents" }}</{{ _hl }}>
|
|
24
|
+
<ol class="list-unstyled d-flex flex-column gap-2 mb-0">
|
|
25
|
+
{%- for _ch in _chapters -%}
|
|
26
|
+
<li>
|
|
27
|
+
<a class="book-toc-item" href="{{ _ch.url | relative_url }}">
|
|
28
|
+
<span class="book-toc-number" aria-hidden="true">{{ _ch.chapter }}</span>
|
|
29
|
+
{%- if _ch.preview -%}
|
|
30
|
+
<img class="book-toc-thumb d-none d-sm-block" src="{{ _ch.preview | relative_url }}" alt="" loading="lazy" decoding="async">
|
|
31
|
+
{%- endif -%}
|
|
32
|
+
<span class="flex-grow-1">
|
|
33
|
+
{%- if _ch.chapter_label -%}
|
|
34
|
+
<span class="d-block small text-body-secondary">{{ _ch.chapter_label }}</span>
|
|
35
|
+
{%- endif -%}
|
|
36
|
+
<span class="fw-semibold">{{ _ch.title }}</span>
|
|
37
|
+
</span>
|
|
38
|
+
<i class="bi bi-chevron-right text-body-secondary" aria-hidden="true"></i>
|
|
39
|
+
</a>
|
|
40
|
+
</li>
|
|
41
|
+
{%- endfor -%}
|
|
42
|
+
</ol>
|
|
43
|
+
</nav>
|
|
44
|
+
{%- else -%}
|
|
45
|
+
{%- comment -%} book-toc: no chapters found for book "{{ include.book }}" — skipping render {%- endcomment -%}
|
|
46
|
+
{%- endif -%}
|
|
47
|
+
{%- endif -%}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-080 · series grouping ZER0-081 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: bookshelf
|
|
4
|
+
Path: _includes/components/bookshelf.html
|
|
5
|
+
Purpose: Responsive grid of every book in the `books` collection, grouped
|
|
6
|
+
into series/category shelves — drop it on a home or index page.
|
|
7
|
+
Params: heading (string, optional) — overall section heading (h2)
|
|
8
|
+
class (string, optional, default: "") — extra wrapper classes
|
|
9
|
+
Depends on: components/book-card.html, the `books` collection,
|
|
10
|
+
_data/series.yml (per-series display metadata)
|
|
11
|
+
Notes: A "book" is any books-collection doc whose layout is `book`
|
|
12
|
+
(story books) or `book-abc` (alphabet books); chapters are never
|
|
13
|
+
listed. Books group by `series:`; a book with no series falls
|
|
14
|
+
under the default `stories` shelf. Series render in the order
|
|
15
|
+
they appear in _data/series.yml, then any undeclared series.
|
|
16
|
+
{%- endcomment -%}
|
|
17
|
+
{%- assign _story_books = site.books | where: "layout", "book" -%}
|
|
18
|
+
{%- assign _abc_books = site.books | where: "layout", "book-abc" -%}
|
|
19
|
+
{%- assign _all = _story_books | concat: _abc_books -%}
|
|
20
|
+
{%- if _all and _all.size > 0 -%}
|
|
21
|
+
{%- assign _default_series = "stories" -%}
|
|
22
|
+
{%- assign _groups = _all | group_by_exp: "doc", "doc.series | default: _default_series" -%}
|
|
23
|
+
|
|
24
|
+
{%- comment -%} Build the shelf order: declared series (file order) first, then any others. {%- endcomment -%}
|
|
25
|
+
{%- assign _order = "," -%}
|
|
26
|
+
{%- for _pair in site.data.series -%}
|
|
27
|
+
{%- assign _needle = _pair[0] | prepend: "," | append: "," -%}
|
|
28
|
+
{%- unless _order contains _needle -%}
|
|
29
|
+
{%- for _g in _groups -%}{%- if _g.name == _pair[0] -%}{%- assign _order = _order | append: _pair[0] | append: "," -%}{%- endif -%}{%- endfor -%}
|
|
30
|
+
{%- endunless -%}
|
|
31
|
+
{%- endfor -%}
|
|
32
|
+
{%- for _g in _groups -%}
|
|
33
|
+
{%- assign _needle = _g.name | prepend: "," | append: "," -%}
|
|
34
|
+
{%- unless _order contains _needle -%}{%- assign _order = _order | append: _g.name | append: "," -%}{%- endunless -%}
|
|
35
|
+
{%- endfor -%}
|
|
36
|
+
{%- assign _order_list = _order | split: "," -%}
|
|
37
|
+
|
|
38
|
+
<section class="bookshelf {{ include.class | default: '' }}">
|
|
39
|
+
{%- if include.heading -%}
|
|
40
|
+
<h2 class="mb-4">{{ include.heading }}</h2>
|
|
41
|
+
{%- endif -%}
|
|
42
|
+
|
|
43
|
+
{%- for _name in _order_list -%}
|
|
44
|
+
{%- if _name == "" -%}{%- continue -%}{%- endif -%}
|
|
45
|
+
{%- assign _group = _groups | where: "name", _name | first -%}
|
|
46
|
+
{%- if _group == nil -%}{%- continue -%}{%- endif -%}
|
|
47
|
+
{%- assign _meta = site.data.series[_name] -%}
|
|
48
|
+
{%- assign _books = _group.items | sort: "title" -%}
|
|
49
|
+
<div class="book-series mb-5" id="series-{{ _name }}">
|
|
50
|
+
<h3 class="h4 mb-1">
|
|
51
|
+
<i class="bi {{ _meta.icon | default: 'bi-book-half' }} me-2" aria-hidden="true"></i>{{ _meta.title | default: _name | escape }}
|
|
52
|
+
</h3>
|
|
53
|
+
{%- if _meta.tagline -%}
|
|
54
|
+
<p class="text-body-secondary mb-3">{{ _meta.tagline | escape }}</p>
|
|
55
|
+
{%- endif -%}
|
|
56
|
+
<div class="row row-cols-2 row-cols-md-3 row-cols-lg-4 g-4">
|
|
57
|
+
{%- for _book in _books -%}
|
|
58
|
+
<div class="col">
|
|
59
|
+
{%- include components/book-card.html book=_book -%}
|
|
60
|
+
</div>
|
|
61
|
+
{%- endfor -%}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
{%- endfor -%}
|
|
65
|
+
</section>
|
|
66
|
+
{%- else -%}
|
|
67
|
+
{%- comment -%} bookshelf: no books collection or no book landing pages — skipping render {%- endcomment -%}
|
|
68
|
+
{%- endif -%}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
CARD-GRID — responsive grid of data-driven cards
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/components/card-grid.html
|
|
7
|
+
Purpose: Render an array of card hashes (see components/data-card.html
|
|
8
|
+
for the item schema) as a responsive Bootstrap grid. Built for
|
|
9
|
+
org hub dashboards, fleet registries, service grids, and any
|
|
10
|
+
"cards from a _data file" page — the markup consumers have
|
|
11
|
+
been hand-rolling per site.
|
|
12
|
+
|
|
13
|
+
Parameters:
|
|
14
|
+
items (array, required) — card hashes (data-card schema)
|
|
15
|
+
columns (string) — row-cols classes,
|
|
16
|
+
default "row-cols-1 row-cols-md-2 row-cols-lg-3"
|
|
17
|
+
gap (string) — gutter class, default "g-4"
|
|
18
|
+
heading_level (string) — heading tag for card titles, default "h3"
|
|
19
|
+
(pick to fit the page outline, e.g. "h2" under an h1)
|
|
20
|
+
card_class (string) — extra classes for every card
|
|
21
|
+
empty_message (string) — message when items is empty/missing;
|
|
22
|
+
omit to render nothing at all
|
|
23
|
+
id (string) — id for the grid wrapper
|
|
24
|
+
aria_label (string) — aria-label for the grid wrapper
|
|
25
|
+
|
|
26
|
+
Example — an org hub page over a generated registry:
|
|
27
|
+
{% include components/card-grid.html
|
|
28
|
+
items=site.data.hub_index.repos
|
|
29
|
+
columns="row-cols-1 row-cols-md-2"
|
|
30
|
+
heading_level="h2"
|
|
31
|
+
empty_message="No member repositories registered yet." %}
|
|
32
|
+
|
|
33
|
+
Data files map 1:1 to the schema, e.g.:
|
|
34
|
+
- title: "1776"
|
|
35
|
+
url: "https://year-of-ai.github.io/1776/"
|
|
36
|
+
external: true
|
|
37
|
+
icon: bi-journal-richtext
|
|
38
|
+
description: "The revolutionary year."
|
|
39
|
+
badge: live
|
|
40
|
+
badge_variant: success
|
|
41
|
+
meta: [ { icon: bi-files, text: "42 pages" } ]
|
|
42
|
+
===================================================================
|
|
43
|
+
{%- endcomment -%}
|
|
44
|
+
{%- assign _cg_items = include.items -%}
|
|
45
|
+
{%- if _cg_items and _cg_items.size > 0 -%}
|
|
46
|
+
<div class="row {{ include.columns | default: 'row-cols-1 row-cols-md-2 row-cols-lg-3' }} {{ include.gap | default: 'g-4' }} zer0-card-grid"
|
|
47
|
+
{%- if include.id %} id="{{ include.id }}"{% endif %}
|
|
48
|
+
{%- if include.aria_label %} role="list" aria-label="{{ include.aria_label | escape }}"{% endif %}>
|
|
49
|
+
{%- for _cg_item in _cg_items %}
|
|
50
|
+
<div class="col"{% if include.aria_label %} role="listitem"{% endif %}>
|
|
51
|
+
{%- include components/data-card.html item=_cg_item heading_level=include.heading_level card_class=include.card_class -%}
|
|
52
|
+
</div>
|
|
53
|
+
{%- endfor %}
|
|
54
|
+
</div>
|
|
55
|
+
{%- elsif include.empty_message -%}
|
|
56
|
+
<div class="text-center py-5 text-body-secondary zer0-card-grid-empty">
|
|
57
|
+
<i class="bi bi-inbox fs-1 d-block mb-2" aria-hidden="true"></i>
|
|
58
|
+
<p class="mb-0">{{ include.empty_message }}</p>
|
|
59
|
+
</div>
|
|
60
|
+
{%- endif -%}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
===================================================================
|
|
3
|
+
DATA-CARD — one generic, data-driven Bootstrap card
|
|
4
|
+
===================================================================
|
|
5
|
+
|
|
6
|
+
Path: _includes/components/data-card.html
|
|
7
|
+
Purpose: Render ONE card from a plain data hash so hub dashboards,
|
|
8
|
+
fleet registries, service grids, and link boards can be
|
|
9
|
+
data-driven instead of hand-written Bootstrap markup.
|
|
10
|
+
Usually invoked via components/card-grid.html.
|
|
11
|
+
|
|
12
|
+
Parameters:
|
|
13
|
+
item (hash, required) — the card data (see schema below)
|
|
14
|
+
heading_level (string) — heading tag for the title, default "h3"
|
|
15
|
+
heading_class (string) — classes for the heading, default "h5"
|
|
16
|
+
card_class (string) — extra classes on the .card element
|
|
17
|
+
|
|
18
|
+
Item schema (every key optional except title):
|
|
19
|
+
title card heading text
|
|
20
|
+
url makes the title a link
|
|
21
|
+
external true → the title link opens in a new tab
|
|
22
|
+
description body copy (plain text or inline HTML)
|
|
23
|
+
icon Bootstrap Icons class shown before the title (e.g. "bi-robot")
|
|
24
|
+
image image path/URL rendered via components/preview-image.html
|
|
25
|
+
image_alt alt text for the image (defaults to title)
|
|
26
|
+
badge short status label rendered next to the title
|
|
27
|
+
badge_variant Bootstrap color suffix for the badge (default "secondary")
|
|
28
|
+
badge_title tooltip for the badge
|
|
29
|
+
meta array of { icon, text } pills rendered under the body
|
|
30
|
+
buttons array of { label, url, icon, variant, external } footer actions
|
|
31
|
+
footer small muted footer text (rendered when no buttons)
|
|
32
|
+
|
|
33
|
+
Example (_data/projects.yml + a page):
|
|
34
|
+
{% for p in site.data.projects %}
|
|
35
|
+
{% include components/data-card.html item=p %}
|
|
36
|
+
{% endfor %}
|
|
37
|
+
===================================================================
|
|
38
|
+
{%- endcomment -%}
|
|
39
|
+
{%- assign _dc = include.item -%}
|
|
40
|
+
{%- assign _dc_heading = include.heading_level | default: "h3" -%}
|
|
41
|
+
{%- assign _dc_heading_class = include.heading_class | default: "h5" -%}
|
|
42
|
+
<div class="card h-100 zer0-data-card {{ include.card_class }}">
|
|
43
|
+
{%- if _dc.image -%}
|
|
44
|
+
{%- assign _dc_img_alt = _dc.image_alt | default: _dc.title -%}
|
|
45
|
+
{%- if _dc.url -%}<a href="{{ _dc.url | relative_url }}" tabindex="-1" aria-hidden="true">{%- endif -%}
|
|
46
|
+
{%- include components/preview-image.html src=_dc.image alt=_dc_img_alt class="card-img-top" style="height: 160px; object-fit: cover;" -%}
|
|
47
|
+
{%- if _dc.url -%}</a>{%- endif -%}
|
|
48
|
+
{%- endif -%}
|
|
49
|
+
<div class="card-body d-flex flex-column">
|
|
50
|
+
<{{ _dc_heading }} class="card-title {{ _dc_heading_class }} mb-2 d-flex align-items-center">
|
|
51
|
+
{%- if _dc.icon %}<i class="bi {{ _dc.icon }} me-2" aria-hidden="true"></i>{% endif -%}
|
|
52
|
+
<span class="flex-grow-1">
|
|
53
|
+
{%- if _dc.url -%}
|
|
54
|
+
<a href="{{ _dc.url | relative_url }}" class="text-decoration-none text-body-emphasis"
|
|
55
|
+
{%- if _dc.external %} target="_blank" rel="noopener"{% endif %}>{{ _dc.title }}</a>
|
|
56
|
+
{%- else -%}
|
|
57
|
+
{{ _dc.title }}
|
|
58
|
+
{%- endif -%}
|
|
59
|
+
</span>
|
|
60
|
+
{%- if _dc.badge %}
|
|
61
|
+
<span class="badge text-bg-{{ _dc.badge_variant | default: 'secondary' }} ms-2"
|
|
62
|
+
{%- if _dc.badge_title %} title="{{ _dc.badge_title | escape }}"{% endif %}>{{ _dc.badge }}</span>
|
|
63
|
+
{%- endif %}
|
|
64
|
+
</{{ _dc_heading }}>
|
|
65
|
+
{%- if _dc.description %}
|
|
66
|
+
<p class="card-text text-body-secondary mb-2">{{ _dc.description }}</p>
|
|
67
|
+
{%- endif %}
|
|
68
|
+
{%- if _dc.meta and _dc.meta.size > 0 %}
|
|
69
|
+
<div class="d-flex flex-wrap gap-2 mt-auto pt-2">
|
|
70
|
+
{%- for m in _dc.meta %}
|
|
71
|
+
<span class="badge rounded-pill text-bg-light border text-body-secondary fw-normal">
|
|
72
|
+
{%- if m.icon %}<i class="bi {{ m.icon }} me-1" aria-hidden="true"></i>{% endif -%}
|
|
73
|
+
{{ m.text }}
|
|
74
|
+
</span>
|
|
75
|
+
{%- endfor %}
|
|
76
|
+
</div>
|
|
77
|
+
{%- endif %}
|
|
78
|
+
</div>
|
|
79
|
+
{%- if _dc.buttons and _dc.buttons.size > 0 %}
|
|
80
|
+
<div class="card-footer bg-transparent d-flex flex-wrap gap-2">
|
|
81
|
+
{%- for b in _dc.buttons %}
|
|
82
|
+
<a class="btn btn-sm btn-{{ b.variant | default: 'outline-primary' }}"
|
|
83
|
+
href="{{ b.url | relative_url }}"
|
|
84
|
+
{%- if b.external %} target="_blank" rel="noopener"{% endif %}>
|
|
85
|
+
{%- if b.icon %}<i class="bi {{ b.icon }} me-1" aria-hidden="true"></i>{% endif -%}
|
|
86
|
+
{{ b.label }}
|
|
87
|
+
</a>
|
|
88
|
+
{%- endfor %}
|
|
89
|
+
</div>
|
|
90
|
+
{%- elsif _dc.footer %}
|
|
91
|
+
<div class="card-footer bg-transparent">
|
|
92
|
+
<small class="text-body-secondary">{{ _dc.footer }}</small>
|
|
93
|
+
</div>
|
|
94
|
+
{%- endif %}
|
|
95
|
+
</div>
|
|
@@ -132,7 +132,8 @@
|
|
|
132
132
|
</div>
|
|
133
133
|
</li>
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
{% unless is_production %}
|
|
136
|
+
<!-- Development URL (dev builds only — never leak localhost into production HTML) -->
|
|
136
137
|
<li class="list-group-item d-flex justify-content-between align-items-center gap-2">
|
|
137
138
|
<div class="d-flex align-items-center min-w-0">
|
|
138
139
|
<span class="badge bg-warning text-dark me-2">Dev</span>
|
|
@@ -162,6 +163,7 @@
|
|
|
162
163
|
</button>
|
|
163
164
|
</div>
|
|
164
165
|
</li>
|
|
166
|
+
{% endunless %}
|
|
165
167
|
|
|
166
168
|
{% if site.repository %}
|
|
167
169
|
<!-- GitHub Source -->
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
page (settings offcanvas + theme-controls-bar).
|
|
10
10
|
params: class (string, optional, default "") — extra btn-group classes,
|
|
11
11
|
e.g. "w-100" to fill the parent.
|
|
12
|
+
config: site.color_mode_lock: true removes the control entirely — the
|
|
13
|
+
site pins one mode and visitor switching is disabled
|
|
14
|
+
(halfmoon.js + core/color-mode-init.html enforce the same).
|
|
12
15
|
-->
|
|
13
|
-
|
|
16
|
+
{% unless site.color_mode_lock %}
|
|
14
17
|
<div class="btn-group bd-theme-switch {{ include.class | default: '' }}" role="group" aria-label="Color mode">
|
|
15
18
|
<button type="button" class="btn btn-outline-secondary btn-sm d-flex align-items-center justify-content-center gap-1" data-bs-theme-value="light" aria-pressed="false">
|
|
16
19
|
<svg class="bi flex-shrink-0" width="1em" height="1em" fill="currentColor" aria-hidden="true"><use href="#sun-fill"></use></svg>
|
|
@@ -25,3 +28,4 @@
|
|
|
25
28
|
<span>Auto</span>
|
|
26
29
|
</button>
|
|
27
30
|
</div>
|
|
31
|
+
{% endunless %}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-078 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: language-toggle
|
|
4
|
+
Path: _includes/components/language-toggle.html
|
|
5
|
+
Purpose: Navbar dropdown that switches between the English original and
|
|
6
|
+
its machine-generated translations (scripts/translate.rb).
|
|
7
|
+
Shows the source language plus every configured target
|
|
8
|
+
language; targets without a generated translation for the
|
|
9
|
+
current page render as disabled entries.
|
|
10
|
+
Params: none — reads page.lang / page.url / page.translation_source_url
|
|
11
|
+
and the generated _data/i18n/manifest.yml.
|
|
12
|
+
Depends on: site.translation.* config, site.data.i18n.manifest,
|
|
13
|
+
_data/i18n/languages.yml (display names), core/i18n.html,
|
|
14
|
+
Bootstrap 5 dropdown, Bootstrap Icons (bi-translate)
|
|
15
|
+
Notes: Renders nothing when translation is disabled or no target
|
|
16
|
+
languages are configured. The chosen language is remembered in
|
|
17
|
+
localStorage("zer0-lang") for future UX (no auto-redirect).
|
|
18
|
+
Must NOT be include_cached — the menu is page-dependent.
|
|
19
|
+
{%- endcomment -%}
|
|
20
|
+
{%- assign _lt_langs = site.translation.languages -%}
|
|
21
|
+
{%- if site.translation.enabled and _lt_langs and _lt_langs.size > 0 -%}
|
|
22
|
+
{%- include core/i18n.html -%}
|
|
23
|
+
{%- assign _lt_source = site.translation.source_lang | default: 'en' -%}
|
|
24
|
+
{%- assign _lt_current = page.lang | default: _lt_source -%}
|
|
25
|
+
{%- if _lt_current == _lt_source -%}
|
|
26
|
+
{%- assign _lt_src_url = page.url -%}
|
|
27
|
+
{%- else -%}
|
|
28
|
+
{%- assign _lt_src_url = page.translation_source_url -%}
|
|
29
|
+
{%- endif -%}
|
|
30
|
+
{%- assign _lt_entry = site.data.i18n.manifest.pages[_lt_src_url] -%}
|
|
31
|
+
{%- assign _lt_names = site.data.i18n.languages -%}
|
|
32
|
+
{%- assign _lt_source_name = _lt_names[_lt_source].native | default: _lt_source -%}
|
|
33
|
+
<div class="dropdown nav-lang-toggle" id="zer0-lang-toggle">
|
|
34
|
+
<button
|
|
35
|
+
class="btn nav-lang-button dropdown-toggle"
|
|
36
|
+
type="button"
|
|
37
|
+
id="langToggleButton"
|
|
38
|
+
data-bs-toggle="dropdown"
|
|
39
|
+
aria-expanded="false"
|
|
40
|
+
aria-label="{{ ui.lang_toggle_aria | default: 'Change language' }}"
|
|
41
|
+
title="{{ ui.lang_toggle_label | default: 'Language' }}"
|
|
42
|
+
>
|
|
43
|
+
<i class="{{ site.default_icon }} bi-translate" aria-hidden="true"></i>
|
|
44
|
+
<span class="nav-link-text d-none d-xl-inline ms-1 text-uppercase">{{ _lt_current | slice: 0, 2 }}</span>
|
|
45
|
+
</button>
|
|
46
|
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="langToggleButton">
|
|
47
|
+
<li>
|
|
48
|
+
{%- if _lt_current == _lt_source %}
|
|
49
|
+
<span class="dropdown-item active" aria-current="true">{{ _lt_source_name }}</span>
|
|
50
|
+
{%- else %}
|
|
51
|
+
<a class="dropdown-item" data-lang="{{ _lt_source }}" hreflang="{{ _lt_source }}" href="{{ _lt_src_url | default: '/' | relative_url }}">{{ _lt_source_name }}</a>
|
|
52
|
+
{%- endif %}
|
|
53
|
+
</li>
|
|
54
|
+
{%- for _lt_lang in _lt_langs -%}
|
|
55
|
+
{%- assign _lt_name = _lt_names[_lt_lang].native | default: _lt_lang -%}
|
|
56
|
+
{%- assign _lt_t = _lt_entry[_lt_lang] %}
|
|
57
|
+
<li>
|
|
58
|
+
{%- if _lt_current == _lt_lang %}
|
|
59
|
+
<span class="dropdown-item active" aria-current="true">{{ _lt_name }}</span>
|
|
60
|
+
{%- elsif _lt_t %}
|
|
61
|
+
<a class="dropdown-item" data-lang="{{ _lt_lang }}" hreflang="{{ _lt_lang }}" href="{{ _lt_t.url | relative_url }}">{{ _lt_name }}</a>
|
|
62
|
+
{%- else %}
|
|
63
|
+
<span class="dropdown-item disabled" aria-disabled="true" title="{{ ui.lang_not_available | default: 'Not yet translated' }}">{{ _lt_name }} <small class="text-body-secondary">({{ ui.lang_not_available | default: 'Not yet translated' }})</small></span>
|
|
64
|
+
{%- endif %}
|
|
65
|
+
</li>
|
|
66
|
+
{%- endfor %}
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
<script>
|
|
70
|
+
// Remember the visitor's language pick (no auto-redirect — just preference).
|
|
71
|
+
(function () {
|
|
72
|
+
var toggle = document.getElementById('zer0-lang-toggle');
|
|
73
|
+
if (!toggle) return;
|
|
74
|
+
toggle.addEventListener('click', function (event) {
|
|
75
|
+
var item = event.target.closest('a.dropdown-item[data-lang]');
|
|
76
|
+
if (!item) return;
|
|
77
|
+
try { localStorage.setItem('zer0-lang', item.getAttribute('data-lang')); } catch (e) { /* private mode */ }
|
|
78
|
+
});
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
81
|
+
{%- endif -%}
|
|
@@ -103,6 +103,14 @@
|
|
|
103
103
|
<!-- Step 1 — request type -->
|
|
104
104
|
<fieldset class="pf-field pf-types">
|
|
105
105
|
<legend class="pf-legend">What kind of feedback?</legend>
|
|
106
|
+
{%- comment -%}
|
|
107
|
+
_data/feedback_types.yml is optional for consumers (remote_theme
|
|
108
|
+
cannot ship _data). Without it the widget falls back to a minimal
|
|
109
|
+
generic taxonomy (see pf_fallback_types below) instead of rendering
|
|
110
|
+
an empty selector. The fallback entries carry no GitHub labels and
|
|
111
|
+
no agent directive — labels must exist in the target repo or GitHub
|
|
112
|
+
silently drops them from prefilled issue URLs.
|
|
113
|
+
{%- endcomment -%}
|
|
106
114
|
{%- assign pf_group = "" -%}
|
|
107
115
|
{%- for t in site.data.feedback_types -%}
|
|
108
116
|
{%- if t.group != pf_group -%}
|
|
@@ -124,6 +132,54 @@
|
|
|
124
132
|
</span>
|
|
125
133
|
</button>
|
|
126
134
|
{%- if forloop.last -%}</div>{%- endif -%}
|
|
135
|
+
{%- else -%}
|
|
136
|
+
{%- comment -%}
|
|
137
|
+
_data/feedback_types.yml is optional for consumers (remote_theme
|
|
138
|
+
cannot ship _data). Without it, render this minimal generic
|
|
139
|
+
taxonomy instead of an empty selector. Ids/markup must stay in
|
|
140
|
+
sync with the fallback JSON in the #pageFeedbackTypes script
|
|
141
|
+
below. Fallback entries carry no GitHub labels (labels must exist
|
|
142
|
+
in the target repo or GitHub silently drops them) and no agent
|
|
143
|
+
directive.
|
|
144
|
+
{%- endcomment -%}
|
|
145
|
+
<p class="pf-types-group">This page</p>
|
|
146
|
+
<div class="pf-types-grid" role="radiogroup" aria-label="This page">
|
|
147
|
+
<button type="button" class="pf-type" role="radio" aria-checked="false"
|
|
148
|
+
data-pf-type-id="fix-page" tabindex="0">
|
|
149
|
+
<i class="bi bi-bug pf-type-icon" aria-hidden="true"></i>
|
|
150
|
+
<span class="pf-type-text">
|
|
151
|
+
<span class="pf-type-label">Report a problem</span>
|
|
152
|
+
<span class="pf-type-desc">A typo, broken link, wrong information, or something rendering badly</span>
|
|
153
|
+
</span>
|
|
154
|
+
</button>
|
|
155
|
+
<button type="button" class="pf-type" role="radio" aria-checked="false"
|
|
156
|
+
data-pf-type-id="improve-page" tabindex="-1">
|
|
157
|
+
<i class="bi bi-stars pf-type-icon" aria-hidden="true"></i>
|
|
158
|
+
<span class="pf-type-text">
|
|
159
|
+
<span class="pf-type-label">Improve this page</span>
|
|
160
|
+
<span class="pf-type-desc">Polish the copy, structure, or presentation</span>
|
|
161
|
+
</span>
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
<p class="pf-types-group">The site</p>
|
|
165
|
+
<div class="pf-types-grid" role="radiogroup" aria-label="The site">
|
|
166
|
+
<button type="button" class="pf-type" role="radio" aria-checked="false"
|
|
167
|
+
data-pf-type-id="feature" tabindex="-1">
|
|
168
|
+
<i class="bi bi-lightbulb pf-type-icon" aria-hidden="true"></i>
|
|
169
|
+
<span class="pf-type-text">
|
|
170
|
+
<span class="pf-type-label">Suggest a feature</span>
|
|
171
|
+
<span class="pf-type-desc">An idea that would make the site more useful</span>
|
|
172
|
+
</span>
|
|
173
|
+
</button>
|
|
174
|
+
<button type="button" class="pf-type" role="radio" aria-checked="false"
|
|
175
|
+
data-pf-type-id="question" tabindex="-1">
|
|
176
|
+
<i class="bi bi-question-circle pf-type-icon" aria-hidden="true"></i>
|
|
177
|
+
<span class="pf-type-text">
|
|
178
|
+
<span class="pf-type-label">Ask a question</span>
|
|
179
|
+
<span class="pf-type-desc">Something unclear that is not exactly a bug</span>
|
|
180
|
+
</span>
|
|
181
|
+
</button>
|
|
182
|
+
</div>
|
|
127
183
|
{%- endfor -%}
|
|
128
184
|
</fieldset>
|
|
129
185
|
|
|
@@ -219,9 +275,16 @@
|
|
|
219
275
|
}
|
|
220
276
|
</script>
|
|
221
277
|
|
|
222
|
-
<!-- Request-type taxonomy (from _data/feedback_types.yml
|
|
278
|
+
<!-- Request-type taxonomy (from _data/feedback_types.yml; generic fallback
|
|
279
|
+
when the site ships no data file — keep in sync with the fallback markup
|
|
280
|
+
in the pf-types fieldset above) -->
|
|
223
281
|
<script id="pageFeedbackTypes" type="application/json">
|
|
224
|
-
{{ site.data.feedback_types | jsonify }}
|
|
282
|
+
{% if site.data.feedback_types %}{{ site.data.feedback_types | jsonify }}{% else %}[
|
|
283
|
+
{"id":"fix-page","label":"Report a problem","icon":"bi-bug","group":"This page","scope":"page","description":"A typo, broken link, wrong information, or something rendering badly","labels":[],"agent":false,"placeholder":"What is wrong, where on the page, and what did you expect instead?"},
|
|
284
|
+
{"id":"improve-page","label":"Improve this page","icon":"bi-stars","group":"This page","scope":"page","description":"Polish the copy, structure, or presentation","labels":[],"agent":false,"placeholder":"What would make this page clearer, tighter, or more useful?"},
|
|
285
|
+
{"id":"feature","label":"Suggest a feature","icon":"bi-lightbulb","group":"The site","scope":"site","description":"An idea that would make the site more useful","labels":[],"agent":false,"placeholder":"Describe the feature and the problem it would solve."},
|
|
286
|
+
{"id":"question","label":"Ask a question","icon":"bi-question-circle","group":"The site","scope":"site","description":"Something unclear that is not exactly a bug","labels":[],"agent":false,"placeholder":"What would you like to know?"}
|
|
287
|
+
]{% endif %}
|
|
225
288
|
</script>
|
|
226
289
|
|
|
227
290
|
<!-- Live page context (build-time front matter; runtime detail added by JS) -->
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
===================================================================
|
|
19
19
|
-->
|
|
20
20
|
|
|
21
|
-
{%-
|
|
22
|
-
{%- assign search_placeholder =
|
|
21
|
+
{%- include core/i18n.html lang=include.lang -%}
|
|
22
|
+
{%- assign search_placeholder = ui.search_placeholder_text | default: "Enter your search term..." -%}
|
|
23
23
|
{%- comment -%}
|
|
24
24
|
Existence-gate the form action to /sitemap/ only when that page is present
|
|
25
25
|
in the build (mirroring the footer Quick Links guard, and the section-sidebar
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
is a no-op when the element is absent.
|
|
15
15
|
===================================================================
|
|
16
16
|
{%- endcomment -%}
|
|
17
|
-
{%-
|
|
17
|
+
{%- include core/i18n.html lang=include.lang -%}
|
|
18
18
|
<div class="modal fade" id="zer0-shortcuts-modal" tabindex="-1"
|
|
19
19
|
aria-labelledby="zer0-shortcuts-modal-title" aria-hidden="true">
|
|
20
20
|
<div class="modal-dialog modal-dialog-centered">
|
|
@@ -13,16 +13,24 @@
|
|
|
13
13
|
Used by theme settings and theme preview pages.
|
|
14
14
|
===================================================================
|
|
15
15
|
{%- endcomment -%}
|
|
16
|
-
{
|
|
17
|
-
|
|
16
|
+
{%- comment -%}
|
|
17
|
+
_data/theme_skins.yml is optional for consumers (remote_theme cannot ship
|
|
18
|
+
_data), so fall back to the compiled skin registry. Keep this list in sync
|
|
19
|
+
with the [data-theme-skin] blocks in _sass/theme/_skins.scss.
|
|
20
|
+
{%- endcomment -%}
|
|
21
|
+
{% assign _skin_fallback = "air|aqua|dirt|neon|mint|plum|sunrise" | split: "|" %}
|
|
22
|
+
{% assign active_skin = site.theme_skin | default: site.data.theme_skins.default | default: "air" %}
|
|
23
|
+
{% assign skin_order = site.data.theme_skins.order | default: _skin_fallback %}
|
|
18
24
|
|
|
19
25
|
<div class="theme-controls-bar d-flex flex-wrap align-items-center gap-3 mb-4 p-3 bg-body-tertiary rounded-3 border">
|
|
26
|
+
{% unless site.color_mode_lock %}
|
|
20
27
|
<div class="d-flex align-items-center gap-2">
|
|
21
28
|
<i class="bi bi-moon-stars" aria-hidden="true"></i>
|
|
22
29
|
<span class="fw-semibold small">Mode:</span>
|
|
23
30
|
{% include components/halfmoon.html %}
|
|
24
31
|
</div>
|
|
25
32
|
<div class="vr d-none d-sm-block" aria-hidden="true"></div>
|
|
33
|
+
{% endunless %}
|
|
26
34
|
<div class="d-flex align-items-center gap-2 flex-grow-1">
|
|
27
35
|
<i class="bi bi-palette2" aria-hidden="true"></i>
|
|
28
36
|
<span class="fw-semibold small">Skin:</span>
|