mykiwi/mykiwi.blogpublic Fork 0
51380a7
Commits
Clone
git clone https://git.rickub.com/mykiwi/mykiwi.blog.git
git clone ssh://git@rickub.com/mykiwi/mykiwi.blog.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Add SRI fingerprinting, inline avatar blurhash, and a language switcher icon

CSS and JS assets now go through fingerprint so browsers/CDNs pick up
new content immediately instead of caching a stale copy, and the
generated hash also gives us a real integrity attribute for free.

The avatar blurhash placeholder is now inlined as a data URI instead
of served as a separate file, so it paints with the first HTML
response rather than racing the real photo over the network.

The footer language switcher gets a globe icon and wraps onto its own
line, with the site-foot markup reordered so it renders after the
store-path row.

htmx and hx-preload move from static/ to assets/ so they can be
fingerprinted too.
Romain Gautier committed 2026-09-18T21:36:23+02:00 Browse files
51380a7 parent: 1ce0eef
modified blog/assets/css/style.scss +22 -4
@@ -985,13 +985,31 @@ blockquote.alert {
985985 // competing for space inline with them.
986986 .lang-switch {
987987 display: inline-flex;
988+ flex-wrap: wrap;
989+ align-items: center;
988990 justify-content: center;
989991 flex-basis: 100%;
990- gap: 0.5rem;
991- }
992+ gap: 0.4rem;
993+
994+ // flex-shrink:0 alone isn't enough: a flex item's automatic min-width
995+ // still falls back to its min-content size, which for spaceless CJK
996+ // text is a single character - so without white-space:nowrap too, a
997+ // too-narrow row splits "日本語" across lines instead of wrapping the
998+ // whole link as one unit.
999+ a,
1000+ span {
1001+ flex-shrink: 0;
1002+ white-space: nowrap;
1003+ }
9921004
993- .lang-switch__current {
994- color: var(--ink-muted);
1005+ // Bigger than the RSS icon's 0.85em - at that size this icon's finer
1006+ // detail (two characters, a curved arrow) turns to mush.
1007+ svg {
1008+ width: 1.4em;
1009+ height: 1.4em;
1010+ fill: currentColor;
1011+ flex-shrink: 0;
1012+ }
9951013 }
9961014
9971015 // Own row, so the path gets the whole measure rather than whatever is left
@@ -985,13 +985,31 @@ blockquote.alert {
985 // competing for space inline with them.985 // competing for space inline with them.
986 .lang-switch {986 .lang-switch {
987 display: inline-flex;987 display: inline-flex;
988+ flex-wrap: wrap;
989+ align-items: center;
988 justify-content: center;990 justify-content: center;
989 flex-basis: 100%;991 flex-basis: 100%;
990- gap: 0.5rem;992+ gap: 0.4rem;
991- }993+
994+ // flex-shrink:0 alone isn't enough: a flex item's automatic min-width
995+ // still falls back to its min-content size, which for spaceless CJK
996+ // text is a single character - so without white-space:nowrap too, a
997+ // too-narrow row splits "日本語" across lines instead of wrapping the
998+ // whole link as one unit.
999+ a,
1000+ span {
1001+ flex-shrink: 0;
1002+ white-space: nowrap;
1003+ }
992 1004
993- .lang-switch__current {1005+ // Bigger than the RSS icon's 0.85em - at that size this icon's finer
994- color: var(--ink-muted);1006+ // detail (two characters, a curved arrow) turns to mush.
1007+ svg {
1008+ width: 1.4em;
1009+ height: 1.4em;
1010+ fill: currentColor;
1011+ flex-shrink: 0;
1012+ }
995 }1013 }
996 1014
997 // Own row, so the path gets the whole measure rather than whatever is left1015 // Own row, so the path gets the whole measure rather than whatever is left
deleted blog/assets/images/avatar-blurhash.png +0 -0
deleted file mode 100644
Binary files a/blog/assets/images/avatar-blurhash.png and /dev/null differ
deleted file mode 100644
Binary files a/blog/assets/images/avatar-blurhash.png and /dev/null differBinary files a/blog/assets/images/avatar-blurhash.png and /dev/null differ
renamed blog/assets/js/htmx.min.js +0 -0
similarity index 100%
rename from blog/static/js/htmx.min.js
rename to blog/assets/js/htmx.min.js
similarity index 100%
rename from blog/static/js/htmx.min.js
rename to blog/assets/js/htmx.min.js
renamed blog/assets/js/hx-preload.min.js +0 -0
similarity index 100%
rename from blog/static/js/hx-preload.min.js
rename to blog/assets/js/hx-preload.min.js
similarity index 100%
rename from blog/static/js/hx-preload.min.js
rename to blog/assets/js/hx-preload.min.js
modified blog/hugo.toml +3 -3
@@ -58,14 +58,14 @@ noindex = true
5858 # Drop a square photo at blog/assets/images/avatar.jpg (or change this path)
5959 # to have it show up; the colophon quietly skips itself if the file is missing.
6060 avatar = "images/avatar.jpg"
61-avatarAlt = "mykiwi's GitHub avatar"
61+avatarAlt = "mykiwi's avatar"
6262 firstName = "Romain"
6363 lastName = "GAUTIER"
6464 atprotoHandle = 'mykiwi.dev'
6565 # Markdown, rendered as-is in the colophon (links work: [text](url)). Also
6666 # the source for the <meta name="description"> tag, unless params.description
6767 # above is set to override it.
68-bio = "I'm a software engineer, passionate about technology, sharing knowledge and open source like #PHP, #Symfony, #Docker, #Dagger, #Nix and atproto:// Also [🐕️ & 🐈️](https://instagram.com/apy.leon \"Pictures of Apy & Léon\") dad."
68+bio = "I'm a software engineer, passionate about technology, sharing knowledge and open source like #PHP, #Symfony, #Docker, #Dagger, #Nix, atproto:// and ATmosphere. Also [🐕️ & 🐈️](https://instagram.com/apy.leon \"Pictures of Apy & Léon\") dad."
6969
7070 [[params.profile.links]]
7171 key = "Tweets on ATmosphere"
@@ -79,7 +79,7 @@ url = "https://mamot.fr/@mykiwi"
7979
8080 [[params.profile.links]]
8181 key = "Tweets on Twitter"
82-value = "@mykiwi"
82+value = "@my_kiwi"
8383 url = "https://x.com/mykiwi"
8484
8585 [[params.profile.links]]
@@ -58,14 +58,14 @@ noindex = true
58 # Drop a square photo at blog/assets/images/avatar.jpg (or change this path)58 # Drop a square photo at blog/assets/images/avatar.jpg (or change this path)
59 # to have it show up; the colophon quietly skips itself if the file is missing.59 # to have it show up; the colophon quietly skips itself if the file is missing.
60 avatar = "images/avatar.jpg"60 avatar = "images/avatar.jpg"
61-avatarAlt = "mykiwi's GitHub avatar"61+avatarAlt = "mykiwi's avatar"
62 firstName = "Romain"62 firstName = "Romain"
63 lastName = "GAUTIER"63 lastName = "GAUTIER"
64 atprotoHandle = 'mykiwi.dev'64 atprotoHandle = 'mykiwi.dev'
65 # Markdown, rendered as-is in the colophon (links work: [text](url)). Also65 # Markdown, rendered as-is in the colophon (links work: [text](url)). Also
66 # the source for the <meta name="description"> tag, unless params.description66 # the source for the <meta name="description"> tag, unless params.description
67 # above is set to override it.67 # above is set to override it.
68-bio = "I'm a software engineer, passionate about technology, sharing knowledge and open source like #PHP, #Symfony, #Docker, #Dagger, #Nix and atproto:// Also [🐕️ & 🐈️](https://instagram.com/apy.leon \"Pictures of Apy & Léon\") dad."68+bio = "I'm a software engineer, passionate about technology, sharing knowledge and open source like #PHP, #Symfony, #Docker, #Dagger, #Nix, atproto:// and ATmosphere. Also [🐕️ & 🐈️](https://instagram.com/apy.leon \"Pictures of Apy & Léon\") dad."
69 69
70 [[params.profile.links]]70 [[params.profile.links]]
71 key = "Tweets on ATmosphere"71 key = "Tweets on ATmosphere"
@@ -79,7 +79,7 @@ url = "https://mamot.fr/@mykiwi"
79 79
80 [[params.profile.links]]80 [[params.profile.links]]
81 key = "Tweets on Twitter"81 key = "Tweets on Twitter"
82-value = "@mykiwi"82+value = "@my_kiwi"
83 url = "https://x.com/mykiwi"83 url = "https://x.com/mykiwi"
84 84
85 [[params.profile.links]]85 [[params.profile.links]]
modified blog/layouts/_default/baseof.html +15 -4
@@ -28,10 +28,17 @@
2828 <!-- Fonts are self-hosted, so there is no third-party DNS + TLS before first paint. -->
2929 <link rel="preload" href="/fonts/Newsreader-latin.woff2" as="font" type="font/woff2" crossorigin>
3030 <link rel="preload" href="/fonts/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin>
31+ {{- /*
32+ fingerprint appends a content hash to the filename (style.abc123.css)
33+ and gives us a real Subresource Integrity hash for free - without it
34+ this was the one asset with no cache-busting at all: same URL forever,
35+ so a browser (or CDN) holding a stale cached copy from before a
36+ deploy has no way to know a new one exists.
37+ */}}
3138 {{ with resources.Get "css/style.scss" }}
3239 {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }}
33- {{ with . | toCSS $opts }}
34- <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}">
40+ {{ with . | toCSS $opts | fingerprint }}
41+ <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}">
3542 {{ end }}
3643 {{ end }}
3744
@@ -58,7 +65,9 @@
5865 press.
5966 */}}
6067 <meta name="htmx-config" content="preload.boostEvent:mouseover">
61- <script src="{{ "js/htmx.min.js" | relURL }}" defer></script>
68+ {{ with resources.Get "js/htmx.min.js" | fingerprint }}
69+ <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
70+ {{ end }}
6271 {{- /*
6372 Official htmx extension, vendored from the same htmx.org package as
6473 htmx.min.js above (node_modules/htmx.org/dist/ext/hx-preload.min.js -
@@ -67,7 +76,9 @@
6776 Needs no hx-ext or other opt-in - just being loaded activates it for
6877 every boosted anchor.
6978 */}}
70- <script src="{{ "js/hx-preload.min.js" | relURL }}" defer></script>
79+ {{ with resources.Get "js/hx-preload.min.js" | fingerprint }}
80+ <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
81+ {{ end }}
7182 {{- /*
7283 htmx's own browser back/forward handling (#restoreHistory) is a
7384 separate code path from a boosted click - it calls htmx.ajax() directly
@@ -28,10 +28,17 @@
28 <!-- Fonts are self-hosted, so there is no third-party DNS + TLS before first paint. -->28 <!-- Fonts are self-hosted, so there is no third-party DNS + TLS before first paint. -->
29 <link rel="preload" href="/fonts/Newsreader-latin.woff2" as="font" type="font/woff2" crossorigin>29 <link rel="preload" href="/fonts/Newsreader-latin.woff2" as="font" type="font/woff2" crossorigin>
30 <link rel="preload" href="/fonts/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin>30 <link rel="preload" href="/fonts/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin>
31+ {{- /*
32+ fingerprint appends a content hash to the filename (style.abc123.css)
33+ and gives us a real Subresource Integrity hash for free - without it
34+ this was the one asset with no cache-busting at all: same URL forever,
35+ so a browser (or CDN) holding a stale cached copy from before a
36+ deploy has no way to know a new one exists.
37+ */}}
31 {{ with resources.Get "css/style.scss" }}38 {{ with resources.Get "css/style.scss" }}
32 {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }}39 {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }}
33- {{ with . | toCSS $opts }}40+ {{ with . | toCSS $opts | fingerprint }}
34- <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}">41+ <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}">
35 {{ end }}42 {{ end }}
36 {{ end }}43 {{ end }}
37 44
@@ -58,7 +65,9 @@
58 press.65 press.
59 */}}66 */}}
60 <meta name="htmx-config" content="preload.boostEvent:mouseover">67 <meta name="htmx-config" content="preload.boostEvent:mouseover">
61- <script src="{{ "js/htmx.min.js" | relURL }}" defer></script>68+ {{ with resources.Get "js/htmx.min.js" | fingerprint }}
69+ <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
70+ {{ end }}
62 {{- /*71 {{- /*
63 Official htmx extension, vendored from the same htmx.org package as72 Official htmx extension, vendored from the same htmx.org package as
64 htmx.min.js above (node_modules/htmx.org/dist/ext/hx-preload.min.js -73 htmx.min.js above (node_modules/htmx.org/dist/ext/hx-preload.min.js -
@@ -67,7 +76,9 @@
67 Needs no hx-ext or other opt-in - just being loaded activates it for76 Needs no hx-ext or other opt-in - just being loaded activates it for
68 every boosted anchor.77 every boosted anchor.
69 */}}78 */}}
70- <script src="{{ "js/hx-preload.min.js" | relURL }}" defer></script>79+ {{ with resources.Get "js/hx-preload.min.js" | fingerprint }}
80+ <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
81+ {{ end }}
71 {{- /*82 {{- /*
72 htmx's own browser back/forward handling (#restoreHistory) is a83 htmx's own browser back/forward handling (#restoreHistory) is a
73 separate code path from a boosted click - it calls htmx.ajax() directly84 separate code path from a boosted click - it calls htmx.ajax() directly
modified blog/layouts/partials/colophon.html +8 -9
@@ -9,21 +9,20 @@
99 {{- with .avatar }}
1010 {{- with resources.Get . }}
1111 {{- /*
12- avatar-blurhash.png is a 16x16 decode of the avatar's blurhash
13- (oZL;Z^4m?wng-PoL~XNbOsShRjNGXTxt={xtM{xZo$t8%1a$IVRk%Nxut6W;IoWBj]V[V@RjR+s.),
14- shown via CSS while the real photo loads -
12+ Inlined (not a separate resources.Get file) so it paints with the
13+ first HTML response instead of racing the real photo over the
14+ network. 4x4 decode of the avatar's blurhash
15+ (oZL;Z^4m?wng-PoL~XNbOsShRjNGXTxt={xtM{xZo$t8%1a$IVRk%Nxut6W;IoWBj]V[V@RjR+s.) -
1516 regenerate it from a new avatar with a blurhash encoder/decoder if
1617 the avatar ever changes.
1718 */}}
1819 <img
19- class="colophon__avatar"
20- src="{{ .RelPermalink }}"
20+ alt="{{ $profile.avatarAlt | default site.Title }}"
21+ style="background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAP0lEQVR42gE0AMv/AO3r6u7v+6Sco//9/wDLyqd5hnfju4PHx8QAsLOita+p0bOOycTFALuTaCBoRoeDRaKsnq0HINP5xdMWAAAAAElFTkSuQmCC')"
2122 width="{{ .Width }}"
2223 height="{{ .Height }}"
23- alt="{{ $profile.avatarAlt | default site.Title }}"
24- {{- with resources.Get "images/avatar-blurhash.png" }}
25- style="background-image:url('{{ .RelPermalink }}')"
26- {{- end }}
24+ class="colophon__avatar"
25+ src="{{ .RelPermalink }}"
2726 >
2827 {{- end }}
2928 {{- end }}
@@ -9,21 +9,20 @@
9 {{- with .avatar }}9 {{- with .avatar }}
10 {{- with resources.Get . }}10 {{- with resources.Get . }}
11 {{- /*11 {{- /*
12- avatar-blurhash.png is a 16x16 decode of the avatar's blurhash12+ Inlined (not a separate resources.Get file) so it paints with the
13- (oZL;Z^4m?wng-PoL~XNbOsShRjNGXTxt={xtM{xZo$t8%1a$IVRk%Nxut6W;IoWBj]V[V@RjR+s.),13+ first HTML response instead of racing the real photo over the
14- shown via CSS while the real photo loads -14+ network. 4x4 decode of the avatar's blurhash
15+ (oZL;Z^4m?wng-PoL~XNbOsShRjNGXTxt={xtM{xZo$t8%1a$IVRk%Nxut6W;IoWBj]V[V@RjR+s.) -
15 regenerate it from a new avatar with a blurhash encoder/decoder if16 regenerate it from a new avatar with a blurhash encoder/decoder if
16 the avatar ever changes.17 the avatar ever changes.
17 */}}18 */}}
18 <img19 <img
19- class="colophon__avatar"20+ alt="{{ $profile.avatarAlt | default site.Title }}"
20- src="{{ .RelPermalink }}"21+ style="background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAP0lEQVR42gE0AMv/AO3r6u7v+6Sco//9/wDLyqd5hnfju4PHx8QAsLOita+p0bOOycTFALuTaCBoRoeDRaKsnq0HINP5xdMWAAAAAElFTkSuQmCC')"
21 width="{{ .Width }}"22 width="{{ .Width }}"
22 height="{{ .Height }}"23 height="{{ .Height }}"
23- alt="{{ $profile.avatarAlt | default site.Title }}"24+ class="colophon__avatar"
24- {{- with resources.Get "images/avatar-blurhash.png" }}25+ src="{{ .RelPermalink }}"
25- style="background-image:url('{{ .RelPermalink }}')"
26- {{- end }}
27 >26 >
28 {{- end }}27 {{- end }}
29 {{- end }}28 {{- end }}
modified blog/layouts/partials/site-foot.html +15 -8
@@ -11,21 +11,28 @@
1111 {{ i18n "rssLabel" }}
1212 </a>
1313 </span>
14- {{ with site.Params.nixstorepath }}
15- <span class="store-path-row">
16- <code class="store-path" title="This site was built by Nix">{{ . }}</code>
17- </span>
18- {{ end }}
1914 {{- $translations := .AllTranslations }}
2015 {{- if gt (len $translations) 1 }}
2116 <span class="lang-switch">
17+ {{- /*
18+ "Turnstile Language Icon" by Farhat Datta (A' Design Award 2011,
19+ languageicon.org), traced coordinates rounded to 2 decimals - already
20+ single-color, so fill:currentColor (in CSS, not per-path) is enough
21+ to match the RSS icon's convention. Sized larger than that icon (see
22+ .lang-switch svg) since its finer detail (the two characters, the
23+ arrow) turns to mush at the RSS icon's ~0.85em.
24+ */}}
25+ <svg viewBox="0 0 841.89 595.28" aria-hidden="true"><polygon points="512.55,488.24 542.37,537.3 558.09,491.72 "/><path d="M277.8,213.22c-1.12-1.1,1.46,8.97,5.04,12.59 c6.35,6.41,11.31,7.23,13.95,7.34c5.84,0.23,13.06-1.46,17.34-3.25 c4.14-1.77,11.4-5.47,14.15-10.88c0.58-1.16,2.17-3.1,1.17-7.89 c-0.76-3.69-3.11-4.98-5.97-4.78c-2.86,0.19-11.53,2.51-15.73,3.79 c-4.19,1.27-12.83,3.9-16.6,4.72 C287.4,215.68,279.12,214.48,277.8,213.22z"/><path d="M383.96,333.55c-1.66-0.6-35.96-14.81-40.83-17.14 c-3.98-1.91-13.74-6.04-18.33-7.91c12.93-19.94,21.09-34.98,22.18-37.28 c2.01-4.19,15.7-30.98,16.02-32.62c0.31-1.67,0.7-7.84,0.4-9.31 c-0.3-1.5-5.32,1.38-12.13,3.69c-6.82,2.3-19.79,10.74-24.8,11.79 c-5.03,1.05-21.09,7.13-29.32,9.86c-8.22,2.73-23.77,7.47-30.17,9.2 c-6.41,1.73-12,1.86-15.58,2.95c0,0,0.48,5.02,1.43,6.52 c0.94,1.5,4.33,5.19,8.27,6.22c3.94,1.04,10.46,0.62,13.44-0.06 c2.97-0.69,8.11-3.2,8.8-4.3c0.7-1.12-0.36-4.55,0.81-5.59 c1.19-1.03,16.84-4.69,22.75-6.47c5.91-1.82,28.54-9.61,31.61-9.21 c-0.97,3.22-19.17,39.28-25.04,50.03 c-5.86,10.75-39.93,58.07-47.18,66.41 c-5.5,6.34-18.84,22.56-23.46,26.22c1.17,0.32,9.42-0.39,10.93-1.32 c9.38-5.78,25-25.22,30.03-31.14 c14.95-17.53,28.08-35.95,38.5-51.75h0.01 c2.03,0.85,18.43,14.21,22.71,17.17c4.28,2.96,21.17,12.39,24.83,13.95 c3.66,1.58,17.72,8.07,18.32,5.87 C388.76,347.12,385.62,334.18,383.96,333.55z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M304.69,512.21 c3.29,2.01,6.39,3.65,9.86,5.3c6.94,3.47,14.79,7.12,22.28,9.86 c10.23,3.83,20.45,6.94,30.68,9.31c5.66,1.28,11.87,2.37,17.9,3.29 c0.55,0,16.8,2.01,20.09,2.01h16.44c6.39-0.55,12.42-0.91,18.81-1.83 c5.11-0.73,10.77-1.64,16.25-2.92c4.02-0.91,8.22-1.83,12.24-3.1 c3.83-1.1,8.22-2.56,12.42-4.02c2.74-0.91,5.66-2.19,8.58-3.29 c2.37-1.1,5.3-2.37,8.04-3.47c3.29-1.46,7.12-3.47,10.77-5.3 c2.92-1.46,6.21-3.29,9.31-5.11c2.37-1.28,7.85-5.48,10.77-5.48 c3.29,0,5.48,2.92,5.48,5.48c0,5.3-7.12,6.94-10.41,9.31 c-3.47,2.37-7.67,4.2-11.32,6.21c-7.3,3.83-14.79,7.12-21.91,9.86 c-9.31,3.47-19.54,6.76-28.67,8.95c-3.47,0.73-6.94,1.64-10.41,2.19 c-1.83,0.37-20.82,3.29-26.11,3.29h-24.11 c-6.39-0.55-13.15-1.28-19.54-2.19c-5.66-0.91-11.69-2.01-17.35-3.29 c-4.38-0.91-9.13-2.19-13.33-3.47c-7.3-2.01-14.43-4.57-21.37-7.3 c-12.6-4.75-25.75-10.96-38.17-19.17c-2.19-1.46-2.37-2.92-2.37-4.57 c0-2.74,2.01-5.3,5.3-5.3C297.75,507.47,303.59,511.67,304.69,512.21z"/><g><path d="M639.84,180.4l-40.77-12.98V48.36 c0-3.47-2.56-5.84-5.84-5.84c-2.56,0-84.92,28.31-91.49,30.68 c-22.33,7.44-86.8,29.73-86.8,29.73l-0,0 c-1.03,0.3-2.64,0.79-4.74,1.45L252.85,48.85 c-1.19-0.42-2.43,0.46-2.43,1.72v2v104.72 c-24.41,8.17-41.81,14.02-42.7,14.34c-1.64,0.55-4.2,0.91-5.66,2.92 c-0.73,0.73-0.91,2.01-1.28,2.92V484.46c0,0.37,0.18,0.55,0.18,0.73 c1.1,2.37,3.1,3.84,5.3,3.84c2.74,0,208.37-69.03,212.93-70.86 c0.22-0.07,0.46-0.24,0.7-0.44l218.84,69.75 c1.18,0.38,2.38-0.5,2.38-1.74V182.14 C641.11,181.35,640.6,180.64,639.84,180.4z M410.97,409.4l-199.05,66.29 V182.04l199.05-66.29V409.4z M587.93,55.67v108.21l-164.49-52.35 L587.93,55.67z M567.69,385.27l-10.52-38.43l-60.52-18.34l-13.01,31.3 l-29.29-8.89l62.18-152.59l28.51,8.64l51.94,187.19L567.69,385.27z"/><polygon points="507.84,300.38 547.77,312.48 529.56,247.88 "/></g></svg>
2226 {{- range sort $translations "Language.Weight" }}
23- {{- if eq .Lang $.Lang }}
24- <span class="lang-switch__current">{{ .Language.Label }}</span>
25- {{- else }}
27+ {{- if ne .Lang $.Lang }}
2628 <a href="{{ .Permalink }}" hreflang="{{ .Lang }}" hx-boost="false">{{ .Language.Label }}</a>
2729 {{- end }}
2830 {{- end }}
2931 </span>
3032 {{- end }}
33+ {{ with site.Params.nixstorepath }}
34+ <span class="store-path-row">
35+ <code class="store-path" title="This site was built by Nix">{{ . }}</code>
36+ </span>
37+ {{ end }}
3138 </footer>
@@ -11,21 +11,28 @@
11 {{ i18n "rssLabel" }}11 {{ i18n "rssLabel" }}
12 </a>12 </a>
13 </span>13 </span>
14- {{ with site.Params.nixstorepath }}
15- <span class="store-path-row">
16- <code class="store-path" title="This site was built by Nix">{{ . }}</code>
17- </span>
18- {{ end }}
19 {{- $translations := .AllTranslations }}14 {{- $translations := .AllTranslations }}
20 {{- if gt (len $translations) 1 }}15 {{- if gt (len $translations) 1 }}
21 <span class="lang-switch">16 <span class="lang-switch">
17+ {{- /*
18+ "Turnstile Language Icon" by Farhat Datta (A' Design Award 2011,
19+ languageicon.org), traced coordinates rounded to 2 decimals - already
20+ single-color, so fill:currentColor (in CSS, not per-path) is enough
21+ to match the RSS icon's convention. Sized larger than that icon (see
22+ .lang-switch svg) since its finer detail (the two characters, the
23+ arrow) turns to mush at the RSS icon's ~0.85em.
24+ */}}
25+ <svg viewBox="0 0 841.89 595.28" aria-hidden="true"><polygon points="512.55,488.24 542.37,537.3 558.09,491.72 "/><path d="M277.8,213.22c-1.12-1.1,1.46,8.97,5.04,12.59 c6.35,6.41,11.31,7.23,13.95,7.34c5.84,0.23,13.06-1.46,17.34-3.25 c4.14-1.77,11.4-5.47,14.15-10.88c0.58-1.16,2.17-3.1,1.17-7.89 c-0.76-3.69-3.11-4.98-5.97-4.78c-2.86,0.19-11.53,2.51-15.73,3.79 c-4.19,1.27-12.83,3.9-16.6,4.72 C287.4,215.68,279.12,214.48,277.8,213.22z"/><path d="M383.96,333.55c-1.66-0.6-35.96-14.81-40.83-17.14 c-3.98-1.91-13.74-6.04-18.33-7.91c12.93-19.94,21.09-34.98,22.18-37.28 c2.01-4.19,15.7-30.98,16.02-32.62c0.31-1.67,0.7-7.84,0.4-9.31 c-0.3-1.5-5.32,1.38-12.13,3.69c-6.82,2.3-19.79,10.74-24.8,11.79 c-5.03,1.05-21.09,7.13-29.32,9.86c-8.22,2.73-23.77,7.47-30.17,9.2 c-6.41,1.73-12,1.86-15.58,2.95c0,0,0.48,5.02,1.43,6.52 c0.94,1.5,4.33,5.19,8.27,6.22c3.94,1.04,10.46,0.62,13.44-0.06 c2.97-0.69,8.11-3.2,8.8-4.3c0.7-1.12-0.36-4.55,0.81-5.59 c1.19-1.03,16.84-4.69,22.75-6.47c5.91-1.82,28.54-9.61,31.61-9.21 c-0.97,3.22-19.17,39.28-25.04,50.03 c-5.86,10.75-39.93,58.07-47.18,66.41 c-5.5,6.34-18.84,22.56-23.46,26.22c1.17,0.32,9.42-0.39,10.93-1.32 c9.38-5.78,25-25.22,30.03-31.14 c14.95-17.53,28.08-35.95,38.5-51.75h0.01 c2.03,0.85,18.43,14.21,22.71,17.17c4.28,2.96,21.17,12.39,24.83,13.95 c3.66,1.58,17.72,8.07,18.32,5.87 C388.76,347.12,385.62,334.18,383.96,333.55z"/><path fill-rule="evenodd" clip-rule="evenodd" d="M304.69,512.21 c3.29,2.01,6.39,3.65,9.86,5.3c6.94,3.47,14.79,7.12,22.28,9.86 c10.23,3.83,20.45,6.94,30.68,9.31c5.66,1.28,11.87,2.37,17.9,3.29 c0.55,0,16.8,2.01,20.09,2.01h16.44c6.39-0.55,12.42-0.91,18.81-1.83 c5.11-0.73,10.77-1.64,16.25-2.92c4.02-0.91,8.22-1.83,12.24-3.1 c3.83-1.1,8.22-2.56,12.42-4.02c2.74-0.91,5.66-2.19,8.58-3.29 c2.37-1.1,5.3-2.37,8.04-3.47c3.29-1.46,7.12-3.47,10.77-5.3 c2.92-1.46,6.21-3.29,9.31-5.11c2.37-1.28,7.85-5.48,10.77-5.48 c3.29,0,5.48,2.92,5.48,5.48c0,5.3-7.12,6.94-10.41,9.31 c-3.47,2.37-7.67,4.2-11.32,6.21c-7.3,3.83-14.79,7.12-21.91,9.86 c-9.31,3.47-19.54,6.76-28.67,8.95c-3.47,0.73-6.94,1.64-10.41,2.19 c-1.83,0.37-20.82,3.29-26.11,3.29h-24.11 c-6.39-0.55-13.15-1.28-19.54-2.19c-5.66-0.91-11.69-2.01-17.35-3.29 c-4.38-0.91-9.13-2.19-13.33-3.47c-7.3-2.01-14.43-4.57-21.37-7.3 c-12.6-4.75-25.75-10.96-38.17-19.17c-2.19-1.46-2.37-2.92-2.37-4.57 c0-2.74,2.01-5.3,5.3-5.3C297.75,507.47,303.59,511.67,304.69,512.21z"/><g><path d="M639.84,180.4l-40.77-12.98V48.36 c0-3.47-2.56-5.84-5.84-5.84c-2.56,0-84.92,28.31-91.49,30.68 c-22.33,7.44-86.8,29.73-86.8,29.73l-0,0 c-1.03,0.3-2.64,0.79-4.74,1.45L252.85,48.85 c-1.19-0.42-2.43,0.46-2.43,1.72v2v104.72 c-24.41,8.17-41.81,14.02-42.7,14.34c-1.64,0.55-4.2,0.91-5.66,2.92 c-0.73,0.73-0.91,2.01-1.28,2.92V484.46c0,0.37,0.18,0.55,0.18,0.73 c1.1,2.37,3.1,3.84,5.3,3.84c2.74,0,208.37-69.03,212.93-70.86 c0.22-0.07,0.46-0.24,0.7-0.44l218.84,69.75 c1.18,0.38,2.38-0.5,2.38-1.74V182.14 C641.11,181.35,640.6,180.64,639.84,180.4z M410.97,409.4l-199.05,66.29 V182.04l199.05-66.29V409.4z M587.93,55.67v108.21l-164.49-52.35 L587.93,55.67z M567.69,385.27l-10.52-38.43l-60.52-18.34l-13.01,31.3 l-29.29-8.89l62.18-152.59l28.51,8.64l51.94,187.19L567.69,385.27z"/><polygon points="507.84,300.38 547.77,312.48 529.56,247.88 "/></g></svg>
22 {{- range sort $translations "Language.Weight" }}26 {{- range sort $translations "Language.Weight" }}
23- {{- if eq .Lang $.Lang }}27+ {{- if ne .Lang $.Lang }}
24- <span class="lang-switch__current">{{ .Language.Label }}</span>
25- {{- else }}
26 <a href="{{ .Permalink }}" hreflang="{{ .Lang }}" hx-boost="false">{{ .Language.Label }}</a>28 <a href="{{ .Permalink }}" hreflang="{{ .Lang }}" hx-boost="false">{{ .Language.Label }}</a>
27 {{- end }}29 {{- end }}
28 {{- end }}30 {{- end }}
29 </span>31 </span>
30 {{- end }}32 {{- end }}
33+ {{ with site.Params.nixstorepath }}
34+ <span class="store-path-row">
35+ <code class="store-path" title="This site was built by Nix">{{ . }}</code>
36+ </span>
37+ {{ end }}
31 </footer>38 </footer>