jekyll-theme-zer0 1.27.0 → 1.29.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 +812 -13
- data/_data/backlog.yml +356 -2
- data/_data/consumers.yml +252 -0
- data/_data/features.yml +234 -5
- data/_data/i18n/fr.yml +103 -0
- data/_data/i18n/manifest.yml +1529 -0
- data/_data/ingredient_densities.yml +122 -0
- data/_data/navigation/docs.yml +2 -0
- data/_data/navigation/main.yml +22 -0
- data/_data/recipe_courses.yml +64 -0
- data/_data/series.yml +19 -0
- data/_data/theme-manifest.yml +387 -277
- data/_data/ui-text.yml +8 -0
- data/_includes/README.md +36 -3
- 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/background-customizer.html +2 -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/cookie-consent.html +4 -4
- data/_includes/components/data-card.html +95 -0
- data/_includes/components/halfmoon.html +5 -1
- data/_includes/components/info-section.html +8 -3
- data/_includes/components/language-toggle.html +168 -21
- data/_includes/components/page-feedback.html +65 -2
- data/_includes/components/page-views-init.html +55 -0
- data/_includes/components/page-views.html +33 -0
- data/_includes/components/recipe-card.html +67 -0
- data/_includes/components/recipe-duration.html +50 -0
- data/_includes/components/recipe-grams.html +58 -0
- data/_includes/components/recipe-index.html +96 -0
- data/_includes/components/recipe-ingredients.html +90 -0
- data/_includes/components/recipe-meta.html +96 -0
- data/_includes/components/recipe-nutrition.html +57 -0
- data/_includes/components/recipe-qty.html +73 -0
- data/_includes/components/recipe-ratio.html +151 -0
- data/_includes/components/recipe-scaler.html +73 -0
- data/_includes/components/recipe-steps.html +86 -0
- data/_includes/components/recipe-temp.html +45 -0
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +3 -0
- data/_includes/components/theme-controls-bar.html +10 -2
- data/_includes/components/theme-customizer.html +8 -2
- data/_includes/components/theme-info.html +8 -1
- data/_includes/content/jsonld-software.html +22 -3
- 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 +18 -16
- data/_includes/core/head.html +21 -0
- data/_includes/core/header.html +7 -4
- 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/local-graph.html +28 -2
- data/_includes/navigation/nav-tree.html +3 -3
- data/_includes/navigation/section-sidebar.html +93 -11
- data/_includes/navigation/sidebar-config.html +21 -0
- data/_includes/navigation/sidebar-left.html +2 -1
- data/_includes/navigation/sidebar-nav.html +4 -0
- data/_includes/navigation/sidebar-pagetree.html +150 -0
- data/_includes/navigation/sidebar-right.html +2 -1
- data/_includes/navigation/unified-drawer.html +8 -2
- data/_includes/obsidian/full-graph.html +165 -136
- data/_includes/setup/wizard.html +173 -86
- data/_includes/stats/stats-metrics.html +2 -2
- data/_layouts/404.html +260 -0
- data/_layouts/README.md +4 -0
- data/_layouts/admin.html +2 -2
- data/_layouts/article.html +5 -0
- 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/cookbook.html +88 -0
- data/_layouts/default.html +4 -4
- data/_layouts/home.html +5 -2
- data/_layouts/landing.html +9 -0
- data/_layouts/news.html +155 -31
- data/_layouts/recipe.html +274 -0
- data/_layouts/root.html +38 -5
- data/_layouts/section.html +96 -25
- data/_sass/components/_book.scss +423 -0
- data/_sass/components/_callout.scss +1 -1
- data/_sass/components/_footer.scss +37 -1
- data/_sass/components/_page-views.scss +36 -0
- data/_sass/components/_recipe.scss +506 -0
- data/_sass/components/_setup-wizard.scss +235 -0
- data/_sass/components/_ui-enhancements.scss +6 -6
- data/_sass/core/_navbar.scss +263 -15
- data/_sass/core/_obsidian.scss +286 -6
- data/_sass/layouts/_landing.scss +2 -2
- data/_sass/theme/_backgrounds.scss +21 -8
- data/_sass/tokens/_color.scss +6 -0
- data/_sass/tokens/_index.scss +2 -0
- data/_sass/tokens/_radius.scss +21 -0
- data/_sass/tokens/_typography.scss +4 -0
- data/_sass/utilities/_focus.scss +14 -0
- data/assets/css/main.scss +4 -0
- data/assets/js/auto-hide-nav.js +5 -1
- data/assets/js/halfmoon.js +26 -0
- data/assets/js/modules/navigation/navbar.js +55 -0
- data/assets/js/obsidian-graph.js +702 -264
- data/assets/js/obsidian-local-graph.js +161 -54
- data/assets/js/page-views.js +372 -0
- data/assets/js/recipe-scaler.js +501 -0
- data/assets/js/search-modal.js +14 -1
- data/assets/js/setup-wizard.js +322 -20
- data/scripts/README.md +29 -0
- data/scripts/bin/audit-consumer +39 -7
- data/scripts/bin/giscus-discussions +213 -14
- data/scripts/bin/manifest +66 -16
- data/scripts/bin/validate +5 -1
- data/scripts/ci/agent_review_result.py +164 -0
- data/scripts/ci/test_agent_review_result.py +172 -0
- data/scripts/design-system-check.rb +170 -0
- data/scripts/install/README.md +47 -6
- data/scripts/install/ai/client.sh +302 -93
- data/scripts/install/ai/prompts/spec.schema.json +1 -1
- 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/audit.sh +42 -2
- data/scripts/lint-liquid-raw.rb +137 -0
- data/scripts/propagate.rb +277 -0
- data/scripts/test/lib/run_tests.sh +2 -1
- data/scripts/test/lib/test_agent_review_result.sh +27 -0
- data/scripts/translate.rb +161 -15
- metadata +56 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-084 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: recipe-ratio
|
|
4
|
+
Path: _includes/components/recipe-ratio.html
|
|
5
|
+
Purpose: The formula behind the recipe — every ingredient reduced to a
|
|
6
|
+
weight and a percentage of one reference ingredient. This is the
|
|
7
|
+
baker's-percentage table bread, pastry and sauce recipes are
|
|
8
|
+
really built on, and it is what lets a reader rebuild the recipe
|
|
9
|
+
at any size instead of only at multiples of the printed one.
|
|
10
|
+
Params: recipe (doc, optional, default: page)
|
|
11
|
+
basis (string, optional, default: page.ratio_basis) — the
|
|
12
|
+
ingredient that counts as 100%; matched as a substring, so
|
|
13
|
+
"flour" covers "bread flour" + "whole-wheat flour"
|
|
14
|
+
heading (string, optional, default: "Ratio")
|
|
15
|
+
heading_level (string, optional, default: "h2")
|
|
16
|
+
Depends on: components/recipe-grams.html, components/recipe-qty.html,
|
|
17
|
+
_data/ingredient_densities.yml, _sass/components/_recipe.scss
|
|
18
|
+
Notes: Percentages are computed at BUILD time, so the table is right
|
|
19
|
+
with JavaScript disabled and never drifts from the ingredient
|
|
20
|
+
list. They are also scale-invariant — only the weight column
|
|
21
|
+
moves when the reader rescales. Ingredients that cannot be
|
|
22
|
+
resolved to a weight (a clove of garlic, "1 egg", a volume with
|
|
23
|
+
no density) are listed without a percentage rather than guessed
|
|
24
|
+
at. Set `ratio: false` on an ingredient to leave it out, or
|
|
25
|
+
author `baker_percent:` to pin a value.
|
|
26
|
+
{%- endcomment -%}
|
|
27
|
+
{%- assign _rr_r = include.recipe | default: page -%}
|
|
28
|
+
{%- assign _rr_basis = include.basis | default: _rr_r.ratio_basis -%}
|
|
29
|
+
{%- assign _rr_ingredients = _rr_r.ingredients -%}
|
|
30
|
+
{%- if _rr_ingredients == nil or _rr_ingredients.size == 0 or _rr_basis == nil -%}
|
|
31
|
+
{%- comment -%} recipe-ratio: needs `ingredients` and a `ratio_basis` — skipping render {%- endcomment -%}
|
|
32
|
+
{%- else -%}
|
|
33
|
+
{%- assign _rr_basis_needle = _rr_basis | downcase | strip -%}
|
|
34
|
+
{%- assign _rr_hl = include.heading_level | default: "h2" -%}
|
|
35
|
+
|
|
36
|
+
{%- comment -%} Flatten grouped ingredients into one list. {%- endcomment -%}
|
|
37
|
+
{%- assign _rr_flat = "" | split: "" -%}
|
|
38
|
+
{%- for _rr_entry in _rr_ingredients -%}
|
|
39
|
+
{%- if _rr_entry.items -%}
|
|
40
|
+
{%- for _rr_i in _rr_entry.items -%}{%- assign _rr_flat = _rr_flat | push: _rr_i -%}{%- endfor -%}
|
|
41
|
+
{%- else -%}
|
|
42
|
+
{%- assign _rr_flat = _rr_flat | push: _rr_entry -%}
|
|
43
|
+
{%- endif -%}
|
|
44
|
+
{%- endfor -%}
|
|
45
|
+
|
|
46
|
+
{%- comment -%} Pass 1: total the basis weight and the batch weight. {%- endcomment -%}
|
|
47
|
+
{%- assign _rr_basis_grams = 0 -%}
|
|
48
|
+
{%- assign _rr_total_grams = 0 -%}
|
|
49
|
+
{%- assign _rr_weighed = 0 -%}
|
|
50
|
+
{%- for _rr_i in _rr_flat -%}
|
|
51
|
+
{%- if _rr_i.ratio == false -%}{%- continue -%}{%- endif -%}
|
|
52
|
+
{%- assign _rr_slug = _rr_i.item | default: "" | downcase | strip | replace: " ", "-" -%}
|
|
53
|
+
{%- assign _rr_gpc = _rr_i.grams_per_cup | default: site.data.ingredient_densities[_rr_slug] -%}
|
|
54
|
+
{%- capture _rr_g -%}{%- include components/recipe-grams.html qty=_rr_i.qty unit=_rr_i.unit grams_per_cup=_rr_gpc -%}{%- endcapture -%}
|
|
55
|
+
{%- assign _rr_g = _rr_g | strip -%}
|
|
56
|
+
{%- if _rr_g != "" -%}
|
|
57
|
+
{%- assign _rr_weighed = _rr_weighed | plus: 1 -%}
|
|
58
|
+
{%- assign _rr_total_grams = _rr_total_grams | plus: _rr_g -%}
|
|
59
|
+
{%- assign _rr_name = _rr_i.item | default: "" | downcase -%}
|
|
60
|
+
{%- if _rr_name contains _rr_basis_needle -%}
|
|
61
|
+
{%- assign _rr_basis_grams = _rr_basis_grams | plus: _rr_g -%}
|
|
62
|
+
{%- endif -%}
|
|
63
|
+
{%- endif -%}
|
|
64
|
+
{%- endfor -%}
|
|
65
|
+
|
|
66
|
+
{%- if _rr_weighed < 2 or _rr_basis_grams <= 0 -%}
|
|
67
|
+
{%- comment -%} recipe-ratio: fewer than two weighable ingredients, or the basis "{{ _rr_basis }}" matched nothing — skipping render {%- endcomment -%}
|
|
68
|
+
{%- else -%}
|
|
69
|
+
<section class="recipe-ratio">
|
|
70
|
+
<{{ _rr_hl }} class="recipe-section-heading">
|
|
71
|
+
<i class="bi bi-percent me-2" aria-hidden="true"></i>{{ include.heading | default: "Ratio" }}
|
|
72
|
+
</{{ _rr_hl }}>
|
|
73
|
+
<p class="recipe-ratio-intro text-body-secondary">
|
|
74
|
+
Every weight below is a percentage of the {{ _rr_basis | escape }}
|
|
75
|
+
{%- if _rr_r.ratio %}, which gives this recipe its <strong>{{ _rr_r.ratio | escape }}</strong> ratio{% endif -%}.
|
|
76
|
+
Rescale the recipe and the weights follow; the percentages are what stay true.
|
|
77
|
+
</p>
|
|
78
|
+
<div class="table-responsive">
|
|
79
|
+
<table class="table table-sm recipe-ratio-table align-middle">
|
|
80
|
+
<thead>
|
|
81
|
+
<tr>
|
|
82
|
+
<th scope="col">Ingredient</th>
|
|
83
|
+
<th scope="col" class="text-end">Weight</th>
|
|
84
|
+
<th scope="col" class="text-end">% of {{ _rr_basis | escape }}</th>
|
|
85
|
+
</tr>
|
|
86
|
+
</thead>
|
|
87
|
+
<tbody>
|
|
88
|
+
{%- for _rr_i in _rr_flat -%}
|
|
89
|
+
{%- if _rr_i.ratio == false -%}{%- continue -%}{%- endif -%}
|
|
90
|
+
{%- assign _rr_slug = _rr_i.item | default: "" | downcase | strip | replace: " ", "-" -%}
|
|
91
|
+
{%- assign _rr_gpc = _rr_i.grams_per_cup | default: site.data.ingredient_densities[_rr_slug] -%}
|
|
92
|
+
{%- capture _rr_g -%}{%- include components/recipe-grams.html qty=_rr_i.qty unit=_rr_i.unit grams_per_cup=_rr_gpc -%}{%- endcapture -%}
|
|
93
|
+
{%- assign _rr_g = _rr_g | strip -%}
|
|
94
|
+
<tr>
|
|
95
|
+
<th scope="row" class="fw-normal">{{ _rr_i.item | escape }}{% if _rr_i.optional %} <span class="text-body-secondary small">(optional)</span>{% endif %}</th>
|
|
96
|
+
<td class="text-end">
|
|
97
|
+
{%- if _rr_g != "" -%}
|
|
98
|
+
{%- include components/recipe-qty.html qty=_rr_g unit="g" -%}
|
|
99
|
+
{%- else -%}
|
|
100
|
+
<span class="text-body-secondary">—</span>
|
|
101
|
+
{%- endif -%}
|
|
102
|
+
</td>
|
|
103
|
+
<td class="text-end">
|
|
104
|
+
{%- if _rr_i.baker_percent -%}
|
|
105
|
+
{{ _rr_i.baker_percent }}%
|
|
106
|
+
{%- elsif _rr_g != "" -%}
|
|
107
|
+
{%- assign _rr_pct = _rr_g | times: 100.0 | divided_by: _rr_basis_grams -%}
|
|
108
|
+
{%- assign _rr_pct_i = _rr_pct | round -%}
|
|
109
|
+
{%- if _rr_pct == _rr_pct_i -%}{{ _rr_pct_i }}{%- else -%}{{ _rr_pct | round: 1 }}{%- endif -%}%
|
|
110
|
+
{%- else -%}
|
|
111
|
+
<span class="text-body-secondary">—</span>
|
|
112
|
+
{%- endif -%}
|
|
113
|
+
</td>
|
|
114
|
+
</tr>
|
|
115
|
+
{%- endfor -%}
|
|
116
|
+
</tbody>
|
|
117
|
+
<tfoot>
|
|
118
|
+
<tr>
|
|
119
|
+
<th scope="row">Total weighed</th>
|
|
120
|
+
<td class="text-end">{%- include components/recipe-qty.html qty=_rr_total_grams unit="g" -%}</td>
|
|
121
|
+
<td class="text-end">
|
|
122
|
+
{%- assign _rr_tpct = _rr_total_grams | times: 100.0 | divided_by: _rr_basis_grams -%}
|
|
123
|
+
{%- assign _rr_tpct_i = _rr_tpct | round -%}
|
|
124
|
+
{%- if _rr_tpct == _rr_tpct_i -%}{{ _rr_tpct_i }}{%- else -%}{{ _rr_tpct | round: 1 }}{%- endif -%}%
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
{%- if _rr_r.yield.amount and _rr_r.yield.amount > 0 -%}
|
|
128
|
+
{%- assign _rr_per_unit = _rr_total_grams | divided_by: _rr_r.yield.amount | round: 1 -%}
|
|
129
|
+
{%- comment -%} Naive singular for the row label: "slices" → "slice". {%- endcomment -%}
|
|
130
|
+
{%- assign _rr_yu = _rr_r.yield.singular | default: _rr_r.yield.unit | default: "serving" | strip -%}
|
|
131
|
+
{%- unless _rr_r.yield.singular -%}
|
|
132
|
+
{%- if _rr_yu.size > 1 -%}
|
|
133
|
+
{%- assign _rr_tail = _rr_yu | slice: -1 -%}
|
|
134
|
+
{%- if _rr_tail == "s" -%}
|
|
135
|
+
{%- assign _rr_cut = _rr_yu.size | minus: 1 -%}
|
|
136
|
+
{%- assign _rr_yu = _rr_yu | slice: 0, _rr_cut -%}
|
|
137
|
+
{%- endif -%}
|
|
138
|
+
{%- endif -%}
|
|
139
|
+
{%- endunless -%}
|
|
140
|
+
<tr>
|
|
141
|
+
<th scope="row">Per {{ _rr_yu | escape }}</th>
|
|
142
|
+
<td class="text-end">{%- include components/recipe-qty.html qty=_rr_per_unit unit="g" scale=false -%}</td>
|
|
143
|
+
<td class="text-end"><span class="text-body-secondary">—</span></td>
|
|
144
|
+
</tr>
|
|
145
|
+
{%- endif -%}
|
|
146
|
+
</tfoot>
|
|
147
|
+
</table>
|
|
148
|
+
</div>
|
|
149
|
+
</section>
|
|
150
|
+
{%- endif -%}
|
|
151
|
+
{%- endif -%}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-084 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: recipe-scaler
|
|
4
|
+
Path: _includes/components/recipe-scaler.html
|
|
5
|
+
Purpose: The control bar that drives every quantity on a recipe page —
|
|
6
|
+
scale the yield up or down, and switch the whole recipe between
|
|
7
|
+
the units it was written in, metric, and US customary.
|
|
8
|
+
Params: base_yield (number, optional, default: page.yield.amount, then 1)
|
|
9
|
+
yield_unit (string, optional, default: page.yield.unit, then
|
|
10
|
+
"servings")
|
|
11
|
+
recipe_id (string, optional, default: page.slug) — key the
|
|
12
|
+
remembered scale is stored under
|
|
13
|
+
units (bool, optional, default: true) — false hides the unit
|
|
14
|
+
switch (for a recipe that is already all-metric, say)
|
|
15
|
+
class (string, optional, default: "")
|
|
16
|
+
Depends on: assets/js/recipe-scaler.js, _sass/components/_recipe.scss
|
|
17
|
+
Notes: Progressive enhancement: with JavaScript off the controls are
|
|
18
|
+
hidden (.recipe-scaler is display:none until the script marks it
|
|
19
|
+
ready) and the page still shows every amount exactly as written.
|
|
20
|
+
The status paragraph is an aria-live region so screen-reader
|
|
21
|
+
users hear "Scaled to 24 cookies" rather than silently changing
|
|
22
|
+
numbers underneath them.
|
|
23
|
+
{%- endcomment -%}
|
|
24
|
+
{%- assign _rsc_base = include.base_yield | default: page.yield.amount | default: 1 -%}
|
|
25
|
+
{%- assign _rsc_unit = include.yield_unit | default: page.yield.unit | default: "servings" -%}
|
|
26
|
+
{%- assign _rsc_id = include.recipe_id | default: page.slug | default: "recipe" -%}
|
|
27
|
+
{%- assign _rsc_show_units = true -%}
|
|
28
|
+
{%- if include.units == false -%}{%- assign _rsc_show_units = false -%}{%- endif -%}
|
|
29
|
+
<div class="recipe-scaler {{ include.class | default: '' }}" data-recipe-scaler data-base-yield="{{ _rsc_base }}" data-yield-unit="{{ _rsc_unit | escape }}" data-recipe-id="{{ _rsc_id | escape }}" hidden>
|
|
30
|
+
<div class="recipe-scaler-group">
|
|
31
|
+
<span class="recipe-scaler-label" id="{{ _rsc_id }}-scale-label">Scale</span>
|
|
32
|
+
<div class="input-group input-group-sm recipe-scaler-stepper">
|
|
33
|
+
<button class="btn btn-outline-secondary" type="button" data-recipe-step="-1" aria-label="Decrease {{ _rsc_unit | escape }}">
|
|
34
|
+
<i class="bi bi-dash-lg" aria-hidden="true"></i>
|
|
35
|
+
</button>
|
|
36
|
+
<input class="form-control text-center" type="number" inputmode="decimal" min="0.25" step="1"
|
|
37
|
+
value="{{ _rsc_base }}" data-recipe-yield-input
|
|
38
|
+
aria-labelledby="{{ _rsc_id }}-scale-label" aria-describedby="{{ _rsc_id }}-yield-unit">
|
|
39
|
+
<span class="input-group-text" id="{{ _rsc_id }}-yield-unit" data-recipe-yield-unit>{{ _rsc_unit | escape }}</span>
|
|
40
|
+
<button class="btn btn-outline-secondary" type="button" data-recipe-step="1" aria-label="Increase {{ _rsc_unit | escape }}">
|
|
41
|
+
<i class="bi bi-plus-lg" aria-hidden="true"></i>
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div class="recipe-scaler-group">
|
|
47
|
+
<div class="btn-group btn-group-sm" role="group" aria-label="Quick scale">
|
|
48
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-multiplier="0.5">½×</button>
|
|
49
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-multiplier="1">1×</button>
|
|
50
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-multiplier="2">2×</button>
|
|
51
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-multiplier="3">3×</button>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
{%- if _rsc_show_units -%}
|
|
56
|
+
<div class="recipe-scaler-group">
|
|
57
|
+
<span class="recipe-scaler-label" id="{{ _rsc_id }}-units-label">Units</span>
|
|
58
|
+
<div class="btn-group btn-group-sm" role="group" aria-labelledby="{{ _rsc_id }}-units-label">
|
|
59
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-units="original" title="Show every amount exactly as the recipe was written">As written</button>
|
|
60
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-units="metric" title="Grams, millilitres and °C — volumes become weights wherever a density is known">Metric</button>
|
|
61
|
+
<button type="button" class="btn btn-outline-secondary" data-recipe-units="us" title="Cups, spoons, ounces and °F">US</button>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
{%- endif -%}
|
|
65
|
+
|
|
66
|
+
<div class="recipe-scaler-group recipe-scaler-group--reset">
|
|
67
|
+
<button type="button" class="btn btn-sm btn-link" data-recipe-reset>
|
|
68
|
+
<i class="bi bi-arrow-counterclockwise me-1" aria-hidden="true"></i>Reset
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<p class="visually-hidden" role="status" aria-live="polite" data-recipe-status></p>
|
|
73
|
+
</div>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-084 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: recipe-steps
|
|
4
|
+
Path: _includes/components/recipe-steps.html
|
|
5
|
+
Purpose: The numbered method for one recipe — optionally split into
|
|
6
|
+
sections ("Day 1", "Assembly"), with per-step timings,
|
|
7
|
+
convertible temperatures, and step photos.
|
|
8
|
+
Params: steps (array, optional, default: page.steps)
|
|
9
|
+
heading (string, optional, default: "Method")
|
|
10
|
+
heading_level (string, optional, default: "h2")
|
|
11
|
+
Depends on: components/recipe-temp.html, components/recipe-duration.html,
|
|
12
|
+
_sass/components/_recipe.scss
|
|
13
|
+
Notes: A step is either a plain string or a hash of
|
|
14
|
+
{text, title, time, temp_f, temp_c, image, image_alt, note}.
|
|
15
|
+
Sections are {section, items} hashes; numbering continues across
|
|
16
|
+
them via the <ol start> attribute so "step 7" means step 7 no
|
|
17
|
+
matter which section it lives in. Step text is markdownified, so
|
|
18
|
+
links and emphasis work. itemprop="recipeInstructions" marks
|
|
19
|
+
each step for schema.org.
|
|
20
|
+
{%- endcomment -%}
|
|
21
|
+
{%- assign _rs_steps = include.steps | default: page.steps -%}
|
|
22
|
+
{%- if _rs_steps == nil or _rs_steps.size == 0 -%}
|
|
23
|
+
{%- comment -%} recipe-steps: no `steps` front matter — skipping render {%- endcomment -%}
|
|
24
|
+
{%- else -%}
|
|
25
|
+
{%- assign _rs_hl = include.heading_level | default: "h2" -%}
|
|
26
|
+
{%- assign _rs_sub = _rs_hl | remove: "h" | plus: 1 -%}
|
|
27
|
+
{%- assign _rs_n = 0 -%}
|
|
28
|
+
<section class="recipe-steps">
|
|
29
|
+
<{{ _rs_hl }} class="recipe-section-heading">
|
|
30
|
+
<i class="bi bi-list-ol me-2" aria-hidden="true"></i>{{ include.heading | default: "Method" }}
|
|
31
|
+
</{{ _rs_hl }}>
|
|
32
|
+
|
|
33
|
+
{%- for _rs_entry in _rs_steps -%}
|
|
34
|
+
{%- if _rs_entry.items -%}
|
|
35
|
+
{%- assign _rs_section = _rs_entry.section | default: _rs_entry.group | default: _rs_entry.title -%}
|
|
36
|
+
{%- assign _rs_loop_steps = _rs_entry.items -%}
|
|
37
|
+
{%- else -%}
|
|
38
|
+
{%- assign _rs_section = nil -%}
|
|
39
|
+
{%- assign _rs_loop_steps = "" | split: "" | push: _rs_entry -%}
|
|
40
|
+
{%- endif -%}
|
|
41
|
+
|
|
42
|
+
{%- if _rs_section -%}
|
|
43
|
+
<h{{ _rs_sub }} class="recipe-steps-section-title">{{ _rs_section | escape }}</h{{ _rs_sub }}>
|
|
44
|
+
{%- endif -%}
|
|
45
|
+
|
|
46
|
+
<ol class="recipe-step-list" start="{{ _rs_n | plus: 1 }}">
|
|
47
|
+
{%- for _rs_s in _rs_loop_steps -%}
|
|
48
|
+
{%- assign _rs_n = _rs_n | plus: 1 -%}
|
|
49
|
+
{%- if _rs_s.text or _rs_s.title or _rs_s.image -%}
|
|
50
|
+
{%- assign _rs_text = _rs_s.text -%}
|
|
51
|
+
{%- else -%}
|
|
52
|
+
{%- assign _rs_text = _rs_s -%}
|
|
53
|
+
{%- endif -%}
|
|
54
|
+
<li class="recipe-step" itemprop="recipeInstructions">
|
|
55
|
+
{%- if _rs_s.title -%}
|
|
56
|
+
<p class="recipe-step-title">{{ _rs_s.title | escape }}</p>
|
|
57
|
+
{%- endif -%}
|
|
58
|
+
{%- if _rs_text -%}
|
|
59
|
+
<div class="recipe-step-text">{{ _rs_text | markdownify }}</div>
|
|
60
|
+
{%- endif -%}
|
|
61
|
+
{%- if _rs_s.time or _rs_s.temp_f or _rs_s.temp_c -%}
|
|
62
|
+
<p class="recipe-step-facts">
|
|
63
|
+
{%- if _rs_s.time -%}
|
|
64
|
+
{%- capture _rs_dur -%}{%- include components/recipe-duration.html minutes=_rs_s.time -%}{%- endcapture -%}
|
|
65
|
+
<span class="recipe-step-fact"><i class="bi bi-stopwatch me-1" aria-hidden="true"></i>{{ _rs_dur | strip }}</span>
|
|
66
|
+
{%- endif -%}
|
|
67
|
+
{%- if _rs_s.temp_f or _rs_s.temp_c -%}
|
|
68
|
+
<span class="recipe-step-fact"><i class="bi bi-thermometer-half me-1" aria-hidden="true"></i>{%- include components/recipe-temp.html f=_rs_s.temp_f c=_rs_s.temp_c -%}</span>
|
|
69
|
+
{%- endif -%}
|
|
70
|
+
</p>
|
|
71
|
+
{%- endif -%}
|
|
72
|
+
{%- if _rs_s.image -%}
|
|
73
|
+
<figure class="recipe-step-figure">
|
|
74
|
+
<img src="{{ _rs_s.image | relative_url }}" alt="{{ _rs_s.image_alt | default: '' | escape }}" loading="lazy" decoding="async" class="img-fluid rounded">
|
|
75
|
+
{%- if _rs_s.caption -%}<figcaption class="small text-body-secondary mt-1">{{ _rs_s.caption | escape }}</figcaption>{%- endif -%}
|
|
76
|
+
</figure>
|
|
77
|
+
{%- endif -%}
|
|
78
|
+
{%- if _rs_s.note -%}
|
|
79
|
+
<p class="recipe-step-note small">{{ _rs_s.note | escape }}</p>
|
|
80
|
+
{%- endif -%}
|
|
81
|
+
</li>
|
|
82
|
+
{%- endfor -%}
|
|
83
|
+
</ol>
|
|
84
|
+
{%- endfor -%}
|
|
85
|
+
</section>
|
|
86
|
+
{%- endif -%}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-084 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: recipe-temp
|
|
4
|
+
Path: _includes/components/recipe-temp.html
|
|
5
|
+
Purpose: A temperature that follows the reader's unit choice — oven
|
|
6
|
+
settings, water temperature, doneness targets. Renders BOTH
|
|
7
|
+
scales by default so the page is complete without JavaScript;
|
|
8
|
+
the scaler then narrows it to whichever system is selected.
|
|
9
|
+
Params: f (number, optional) — degrees Fahrenheit
|
|
10
|
+
c (number, optional) — degrees Celsius
|
|
11
|
+
(give either one; the other is derived)
|
|
12
|
+
label (string, optional, default: "") — prefix, e.g. "Oven"
|
|
13
|
+
class (string, optional, default: "")
|
|
14
|
+
Depends on: assets/js/recipe-scaler.js, _sass/components/_recipe.scss
|
|
15
|
+
Notes: Derived values round to the nearest 5° above 100° — the
|
|
16
|
+
precision an oven dial actually has — and to the nearest degree
|
|
17
|
+
below it. Author both `f:` and `c:` to pin exact values.
|
|
18
|
+
Drop it straight into recipe prose:
|
|
19
|
+
include components/recipe-temp.html f=375
|
|
20
|
+
{%- endcomment -%}
|
|
21
|
+
{%- if include.f == nil and include.c == nil -%}
|
|
22
|
+
{%- comment -%} ERROR: recipe-temp requires `f` or `c` {%- endcomment -%}
|
|
23
|
+
{%- else -%}
|
|
24
|
+
{%- if include.f != nil -%}
|
|
25
|
+
{%- assign _rt_f = include.f | round -%}
|
|
26
|
+
{%- else -%}
|
|
27
|
+
{%- assign _rt_fraw = include.c | times: 9.0 | divided_by: 5.0 | plus: 32 -%}
|
|
28
|
+
{%- if _rt_fraw >= 100 -%}
|
|
29
|
+
{%- assign _rt_f = _rt_fraw | divided_by: 5.0 | round | times: 5 -%}
|
|
30
|
+
{%- else -%}
|
|
31
|
+
{%- assign _rt_f = _rt_fraw | round -%}
|
|
32
|
+
{%- endif -%}
|
|
33
|
+
{%- endif -%}
|
|
34
|
+
{%- if include.c != nil -%}
|
|
35
|
+
{%- assign _rt_c = include.c | round -%}
|
|
36
|
+
{%- else -%}
|
|
37
|
+
{%- assign _rt_craw = include.f | minus: 32 | times: 5.0 | divided_by: 9.0 -%}
|
|
38
|
+
{%- if _rt_craw >= 100 -%}
|
|
39
|
+
{%- assign _rt_c = _rt_craw | divided_by: 5.0 | round | times: 5 -%}
|
|
40
|
+
{%- else -%}
|
|
41
|
+
{%- assign _rt_c = _rt_craw | round -%}
|
|
42
|
+
{%- endif -%}
|
|
43
|
+
{%- endif -%}
|
|
44
|
+
<span class="recipe-temp {{ include.class | default: '' }}" data-recipe-temp data-temp-f="{{ _rt_f }}" data-temp-c="{{ _rt_c }}">{% if include.label %}{{ include.label | escape }} {% endif %}{{ _rt_f }} °F ({{ _rt_c }} °C)</span>
|
|
45
|
+
{%- endif -%}
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
<div class="modal-dialog modal-dialog-centered">
|
|
46
46
|
<div class="modal-content">
|
|
47
47
|
<div class="modal-header">
|
|
48
|
-
<
|
|
48
|
+
<h2 class="modal-title h5" id="siteSearchModalLabel">
|
|
49
49
|
<i class="bi bi-search me-2"></i>Search
|
|
50
|
-
</
|
|
50
|
+
</h2>
|
|
51
51
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
52
52
|
</div>
|
|
53
53
|
<div class="modal-body">
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
<dt class="col-4 col-sm-3 text-end"><kbd>/</kbd></dt>
|
|
31
31
|
<dd class="col-8 col-sm-9 mb-2">{{ ui.shortcuts_search | default: "Open search" }}</dd>
|
|
32
32
|
|
|
33
|
+
<dt class="col-4 col-sm-3 text-end"><kbd>⌘</kbd>/<kbd>Ctrl</kbd>+<kbd>K</kbd></dt>
|
|
34
|
+
<dd class="col-8 col-sm-9 mb-2">{{ ui.shortcuts_search | default: "Open search" }}</dd>
|
|
35
|
+
|
|
33
36
|
<dt class="col-4 col-sm-3 text-end"><kbd>[</kbd></dt>
|
|
34
37
|
<dd class="col-8 col-sm-9 mb-2">{{ ui.shortcuts_prev_section | default: "Previous section" }}</dd>
|
|
35
38
|
|
|
@@ -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>
|
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
===================================================================
|
|
15
15
|
-->
|
|
16
16
|
|
|
17
|
-
{
|
|
18
|
-
|
|
17
|
+
{%- comment -%}
|
|
18
|
+
_data/theme_skins.yml is optional for consumers (remote_theme cannot ship
|
|
19
|
+
_data), so fall back to the compiled skin registry. Keep this list in sync
|
|
20
|
+
with the [data-theme-skin] blocks in _sass/theme/_skins.scss.
|
|
21
|
+
{%- endcomment -%}
|
|
22
|
+
{% assign _skin_fallback = "air|aqua|dirt|neon|mint|plum|sunrise" | split: "|" %}
|
|
23
|
+
{% assign current_skin = site.theme_skin | default: site.data.theme_skins.default | default: "air" %}
|
|
24
|
+
{% assign skins = site.data.theme_skins.order | default: _skin_fallback %}
|
|
19
25
|
|
|
20
26
|
<p class="text-body-secondary mb-3">
|
|
21
27
|
Current skin: <strong>{{ current_skin }}</strong>. Click a card to preview (page-level only — does not persist).
|
|
@@ -54,7 +54,14 @@
|
|
|
54
54
|
<div class="col-6">
|
|
55
55
|
<div class="p-2 bg-body-tertiary rounded text-center">
|
|
56
56
|
<small class="text-body-secondary d-block">Last Build</small>
|
|
57
|
-
|
|
57
|
+
{%- comment -%}
|
|
58
|
+
data-testid is the stable hook the pixel-snapshot tier masks on
|
|
59
|
+
(test/visual/features/appearance-snapshot.spec.js). This stamp has
|
|
60
|
+
minute resolution, so it becomes an unconditional snapshot flake the
|
|
61
|
+
moment this panel is shown by default rather than inside a collapsed
|
|
62
|
+
offcanvas. Don't remove the attribute without updating that mask.
|
|
63
|
+
{%- endcomment -%}
|
|
64
|
+
<code class="small" data-testid="theme-build-stamp">{{ site.time | date: "%b %d, %H:%M" }}</code>
|
|
58
65
|
</div>
|
|
59
66
|
</div>
|
|
60
67
|
|
|
@@ -9,22 +9,41 @@
|
|
|
9
9
|
engines to accurately cite and describe the zer0-mistakes
|
|
10
10
|
Jekyll theme as a SoftwareApplication entity.
|
|
11
11
|
|
|
12
|
+
OPT-IN ONLY — this describes THE THEME, not the site rendering it.
|
|
13
|
+
The payload below is hardcoded to zer0-mistakes: its RubyGems download
|
|
14
|
+
URL, its GitHub repository, its MIT licence, its feature list, its
|
|
15
|
+
mascot screenshot, and a Person node naming the theme's author. Emitted
|
|
16
|
+
from a consumer's homepage it asserts, in machine-readable form, that
|
|
17
|
+
THAT site is a free open-source Ruby gem authored by someone else — e.g.
|
|
18
|
+
a consulting practice's homepage claiming to be a developer tool.
|
|
19
|
+
|
|
20
|
+
It therefore renders only when a site explicitly opts in with
|
|
21
|
+
`jsonld_software_application: true` in _config.yml, which the theme's own
|
|
22
|
+
site does. Consumers get no software schema (their standard SEO metadata
|
|
23
|
+
is unaffected) unless they deliberately turn this on.
|
|
24
|
+
|
|
12
25
|
Template Logic:
|
|
26
|
+
- Requires site.jsonld_software_application == true
|
|
13
27
|
- Renders SoftwareApplication schema on the homepage only
|
|
14
28
|
- Links to a WebPage wrapper via @id references
|
|
15
29
|
- Pulls version, author, and description from site config
|
|
16
30
|
|
|
17
31
|
Dependencies:
|
|
18
|
-
- _config.yml: site.url, site.title,
|
|
32
|
+
- _config.yml: jsonld_software_application, site.url, site.title,
|
|
33
|
+
site.description, site.author
|
|
19
34
|
- lib/jekyll-theme-zer0/version.rb (version referenced in config)
|
|
20
35
|
|
|
21
36
|
Usage:
|
|
22
37
|
- Included automatically in _includes/core/head.html
|
|
23
|
-
- Conditional:
|
|
38
|
+
- Conditional: opted in AND page.permalink == "/"
|
|
24
39
|
===================================================================
|
|
25
40
|
-->
|
|
26
41
|
|
|
27
|
-
{
|
|
42
|
+
{%- assign _is_homepage = false -%}
|
|
43
|
+
{%- if page.permalink == "/" or page.url == "/" -%}
|
|
44
|
+
{%- assign _is_homepage = true -%}
|
|
45
|
+
{%- endif -%}
|
|
46
|
+
{% if site.jsonld_software_application and _is_homepage %}
|
|
28
47
|
<script type="application/ld+json">
|
|
29
48
|
{
|
|
30
49
|
"@context": "https://schema.org",
|
data/_includes/content/seo.html
CHANGED
|
@@ -37,17 +37,23 @@
|
|
|
37
37
|
{%- assign assets_prefix = site.preview_images.assets_prefix | default: '/assets' -%}
|
|
38
38
|
{%- assign auto_prefix = site.preview_images.auto_prefix | default: true -%}
|
|
39
39
|
|
|
40
|
+
{%- comment -%}
|
|
41
|
+
Guard with truthiness AND blank: in Liquid 4 `nil != blank` is TRUE, so a
|
|
42
|
+
bare `!= blank` check on an unset variable takes the branch and assigns a
|
|
43
|
+
nil seo_image — og:image was silently dropped on every page without a
|
|
44
|
+
page.header image, defeating the site.og_image fallback entirely.
|
|
45
|
+
{%- endcomment -%}
|
|
40
46
|
{%- if page_teaser_image contains '://' -%}
|
|
41
47
|
{%- comment -%} External URL - use as-is {%- endcomment -%}
|
|
42
|
-
{%- elsif auto_prefix and page_teaser_image != blank -%}
|
|
48
|
+
{%- elsif auto_prefix and page_teaser_image and page_teaser_image != blank -%}
|
|
43
49
|
{%- unless page_teaser_image contains assets_prefix -%}
|
|
44
50
|
{%- assign page_teaser_image = assets_prefix | append: page_teaser_image -%}
|
|
45
51
|
{%- endunless -%}
|
|
46
52
|
{%- endif -%}
|
|
47
53
|
|
|
48
|
-
{%- if page_large_image != blank -%}
|
|
54
|
+
{%- if page_large_image and page_large_image != blank -%}
|
|
49
55
|
{%- assign seo_image = page_large_image | absolute_url | escape -%}
|
|
50
|
-
{%- elsif page_teaser_image != blank -%}
|
|
56
|
+
{%- elsif page_teaser_image and page_teaser_image != blank -%}
|
|
51
57
|
{%- assign seo_image = page_teaser_image | absolute_url | escape -%}
|
|
52
58
|
{%- endif -%}
|
|
53
59
|
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
when Bootstrap's [data-bs-theme] selectors are first evaluated.
|
|
14
14
|
|
|
15
15
|
Priority order (highest → lowest):
|
|
16
|
-
1.
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
1. site.color_mode_lock — when true, config wins unconditionally:
|
|
17
|
+
visitor preference and OS scheme are ignored
|
|
18
|
+
(data-color-mode-lock attribute, set by root.html)
|
|
19
|
+
2. localStorage["theme"] — user's explicit override via Appearance panel
|
|
20
|
+
3. data-color-mode-default — site.color_mode_default config value
|
|
21
|
+
4. "auto" fallback — resolves via prefers-color-scheme
|
|
19
22
|
{%- endcomment -%}
|
|
20
23
|
<script>
|
|
21
24
|
(function () {
|
|
@@ -23,8 +26,14 @@
|
|
|
23
26
|
try {
|
|
24
27
|
var root = document.documentElement;
|
|
25
28
|
var configDefault = root.getAttribute('data-color-mode-default') || 'auto';
|
|
26
|
-
var
|
|
29
|
+
var locked = root.getAttribute('data-color-mode-lock') === 'true';
|
|
30
|
+
var stored = locked ? null : window.localStorage.getItem('theme');
|
|
27
31
|
var mode = stored || configDefault;
|
|
32
|
+
if (mode === 'auto' && locked) {
|
|
33
|
+
{%- comment -%} A locked "auto" is contradictory; treat it as dark so
|
|
34
|
+
the brand-pinned mode never flips with the OS. {%- endcomment %}
|
|
35
|
+
mode = 'dark';
|
|
36
|
+
}
|
|
28
37
|
if (mode === 'auto') {
|
|
29
38
|
root.setAttribute('data-bs-theme',
|
|
30
39
|
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-079 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: favicon
|
|
4
|
+
Path: _includes/core/favicon.html
|
|
5
|
+
Purpose: Emit the browser-identity tags (favicon, SVG icon, Apple touch
|
|
6
|
+
icon, web manifest, theme-color) for every page. Before this
|
|
7
|
+
include existed, sites relied on the browser's implicit
|
|
8
|
+
/favicon.ico probe — which silently 404s on sites without the
|
|
9
|
+
file and never works for baseurl (project-page) deployments.
|
|
10
|
+
Params: none — reads the optional `favicon:` block in _config.yml:
|
|
11
|
+
|
|
12
|
+
favicon:
|
|
13
|
+
ico : /favicon.ico # legacy .ico (default)
|
|
14
|
+
svg : /assets/images/favicon.svg # scalable icon (optional)
|
|
15
|
+
png : /assets/images/favicon-32.png # PNG icon (optional)
|
|
16
|
+
png_size : 32x32 # sizes attr for png (optional)
|
|
17
|
+
apple_touch : /assets/images/apple-touch.png # iOS home-screen icon (optional)
|
|
18
|
+
manifest : /site.webmanifest # PWA manifest (optional)
|
|
19
|
+
theme_color : "#0d1117" # browser chrome color (optional)
|
|
20
|
+
|
|
21
|
+
With NO config at all this still emits the explicit
|
|
22
|
+
/favicon.ico link (every consumer site carries one at root),
|
|
23
|
+
and theme_color falls back to site.theme_color.main so the
|
|
24
|
+
mobile address bar matches the site's design tokens.
|
|
25
|
+
Notes: Page-invariant — safe to render via include_cached. All paths
|
|
26
|
+
run through relative_url so baseurl deployments resolve.
|
|
27
|
+
{%- endcomment -%}
|
|
28
|
+
{%- assign _fav = site.favicon -%}
|
|
29
|
+
{%- assign _fav_ico = _fav.ico | default: '/favicon.ico' -%}
|
|
30
|
+
<link rel="icon" href="{{ _fav_ico | relative_url }}" sizes="32x32">
|
|
31
|
+
{%- if _fav.svg %}
|
|
32
|
+
<link rel="icon" type="image/svg+xml" href="{{ _fav.svg | relative_url }}">
|
|
33
|
+
{%- endif %}
|
|
34
|
+
{%- if _fav.png %}
|
|
35
|
+
<link rel="icon" type="image/png" href="{{ _fav.png | relative_url }}"{% if _fav.png_size %} sizes="{{ _fav.png_size }}"{% endif %}>
|
|
36
|
+
{%- endif %}
|
|
37
|
+
{%- if _fav.apple_touch %}
|
|
38
|
+
<link rel="apple-touch-icon" href="{{ _fav.apple_touch | relative_url }}">
|
|
39
|
+
{%- endif %}
|
|
40
|
+
{%- if _fav.manifest %}
|
|
41
|
+
<link rel="manifest" href="{{ _fav.manifest | relative_url }}">
|
|
42
|
+
{%- endif %}
|
|
43
|
+
{%- assign _fav_theme_color = _fav.theme_color | default: site.theme_color.main -%}
|
|
44
|
+
{%- if _fav_theme_color %}
|
|
45
|
+
<meta name="theme-color" content="{{ _fav_theme_color }}">
|
|
46
|
+
{%- endif %}
|