mykiwi/mykiwi.blogpublic Fork 0
51380a74d964c44b92c0eb3b04aab56bfdcf7545
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 51380a7 · on 51380a74d964c44b92c0eb3b04aab56bfdcf7545 · Romain Gautier · 10h ago
baseof.html · 140 lines · 7.2 KBHTML Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<!doctype html>
<html lang="{{ site.Language.Lang }}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="color-scheme" content="light dark">
    <meta name="theme-color" content="#f5f2ec" media="(prefers-color-scheme: light)">
    <meta name="theme-color" content="#15171a" media="(prefers-color-scheme: dark)">
    {{ if site.Params.noindex }}
      <meta name="robots" content="noindex, nofollow">
    {{ end }}

    <title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} &middot; {{ site.Title }}{{ end }}</title>
    {{- /*
      Priority: this page's own front matter > an explicit site-wide
      params.description override > params.profile.bio, stripped of markdown
      down to plain text. Set params.description only when the SEO snippet
      should say something different from the colophon bio; otherwise bio is
      the single source of truth for both.
    */}}
    {{- $desc := .Description }}
    {{- if not $desc }}{{ $desc = site.Params.description }}{{ end }}
    {{- if not $desc }}{{ with site.Params.profile.bio }}{{ $desc = . | markdownify | plainify }}{{ end }}{{ end }}
    {{ with $desc -}}
      <meta name="description" content="{{ . }}">
    {{- end }}

    <!-- Fonts are self-hosted, so there is no third-party DNS + TLS before first paint. -->
    <link rel="preload" href="/fonts/Newsreader-latin.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/fonts/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin>
    {{- /*
      fingerprint appends a content hash to the filename (style.abc123.css)
      and gives us a real Subresource Integrity hash for free - without it
      this was the one asset with no cache-busting at all: same URL forever,
      so a browser (or CDN) holding a stale cached copy from before a
      deploy has no way to know a new one exists.
    */}}
    {{ with resources.Get "css/style.scss" }}
      {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }}
      {{ with . | toCSS $opts | fingerprint }}
        <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}">
      {{ end }}
    {{ end }}

    <link rel="alternate" type="application/rss+xml" title="{{ site.Title }}" href="{{ "index.xml" | absLangURL }}">
    {{- /* One hreflang tag per translated version of this page, so search
      engines serve readers the right language instead of always the one
      that happens to rank. x-default points at the lowest-weight (English)
      translation, since that's the site's default language. */}}
    {{- $translations := sort .AllTranslations "Language.Weight" }}
    {{- range $translations }}
      <link rel="alternate" hreflang="{{ .Language.Lang }}" href="{{ .Permalink }}">
    {{- end }}
    {{- with index $translations 0 }}
      <link rel="alternate" hreflang="x-default" href="{{ .Permalink }}">
    {{- end }}
    {{- /*
      Declarative, not htmx.config.preload = {...} in a script: a <meta> tag
      is parsed with the rest of the document, before any script (deferred or
      not) runs, so htmx's constructor sees it synchronously and correctly
      every time - no dependency on inline-script/defer/DOMContentLoaded
      ordering at all. boostEvent switches hx-preload's trigger from the
      default mousedown/touchstart to mouseover, so the fetch for a boosted
      link starts the instant the cursor touches it rather than waiting for a
      press.
    */}}
    <meta name="htmx-config" content="preload.boostEvent:mouseover">
    {{ with resources.Get "js/htmx.min.js" | fingerprint }}
      <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
    {{ end }}
    {{- /*
      Official htmx extension, vendored from the same htmx.org package as
      htmx.min.js above (node_modules/htmx.org/dist/ext/hx-preload.min.js -
      re-copy it from there on a version bump). Prefetches a boosted link's
      GET response, reused automatically if the click follows within 5s.
      Needs no hx-ext or other opt-in - just being loaded activates it for
      every boosted anchor.
    */}}
    {{ with resources.Get "js/hx-preload.min.js" | fingerprint }}
      <script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" defer></script>
    {{ end }}
    {{- /*
      htmx's own browser back/forward handling (#restoreHistory) is a
      separate code path from a boosted click - it calls htmx.ajax() directly
      with swap:"outerSync" hardcoded, a full body replace, not our
      innerMorph below. Left alone, a link click morphed smoothly but
      back/forward tore the masthead down and rebuilt it. Preempt it with the
      same config used everywhere else (kept as one Hugo value, $swapSpec,
      rather than duplicated literals, so the two can't drift apart), and
      abort a still-in-flight restore before starting the next one - the same
      guard htmx's own implementation has - so mashing back/forward can't let
      a slow, stale response land after a faster, newer one.

      `defer` only affects scripts with a `src` - htmx.min.js and
      hx-preload.min.js above genuinely wait until after parsing, but this
      inline script has no `src` and runs immediately in place during
      parsing, defer or not. It must not touch `htmx` at its top level, only
      from inside a listener that fires later - hence DOMContentLoaded, which
      is guaranteed to fire only after every deferred script, htmx.min.js and
      hx-preload.min.js included, has already run.
    */}}
    {{- $swapSpec := dict "swap" "innerMorph" "transition" true }}
    <script>
      document.addEventListener("DOMContentLoaded", () => {
        let restoreAbort;
        document.addEventListener("htmx:before:history:restore", (e) => {
          e.preventDefault();
          restoreAbort?.abort();
          restoreAbort = new AbortController();
          htmx.ajax("GET", e.detail.path, {
            target: document.body,
            swap: "{{ $swapSpec.swap }}",
            transition: {{ $swapSpec.transition }},
            request: {headers: {}, signal: restoreAbort.signal},
          });
        });
      });
    </script>
    {{ block "head" . }}{{ end }}
  </head>
  {{- /*
    Boosts every same-origin link/form into an AJAX navigation, inherited from
    body down (htmx 4 requires the ":inherited" suffix explicitly - it no
    longer inherits hx-* attributes implicitly). Boosted elements default to
    targeting the whole <body>; innerMorph diffs the new body onto the old
    one node-by-node instead of replacing it wholesale, so unchanged nodes
    (the masthead's "mykiwi.blog" link) are patched in place rather than
    torn down and re-flashed, while changed ones - the per-page signal SVG
    path, the swapped #main content - update normally. transition:true wraps
    that diff in a native View Transition for a soft cross-fade.
  */}}
  <body hx-boost:inherited="swap:{{ $swapSpec.swap }} transition:{{ $swapSpec.transition }}">
    <a class="skip-link" href="#main">{{ i18n "skipToContent" }}</a>
    <div class="wrap">
      {{ partial "masthead.html" . }}
      <main id="main">{{ block "main" . }}{{ end }}</main>
      {{ partial "site-foot.html" . }}
    </div>
  </body>
</html>