| Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier yesterday | 1 | <!doctype html> |
| 2 | <html lang="{{ site.Language.Lang }}"> |
| 3 | <head> |
| 4 | <meta charset="utf-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | <meta name="color-scheme" content="light dark"> |
| 7 | <meta name="theme-color" content="#f5f2ec" media="(prefers-color-scheme: light)"> |
| 8 | <meta name="theme-color" content="#15171a" media="(prefers-color-scheme: dark)"> |
| 9 | |
| 10 | <title>{{ if .IsHome }}{{ site.Title }}{{ else }}{{ .Title }} · {{ site.Title }}{{ end }}</title> |
| 11 | {{- /* |
| 12 | Priority: this page's own front matter > an explicit site-wide |
| 13 | params.description override > params.profile.bio, stripped of markdown |
| 14 | down to plain text. Set params.description only when the SEO snippet |
| 15 | should say something different from the colophon bio; otherwise bio is |
| 16 | the single source of truth for both. |
| 17 | */}} |
| 18 | {{- $desc := .Description }} |
| 19 | {{- if not $desc }}{{ $desc = site.Params.description }}{{ end }} |
| 20 | {{- if not $desc }}{{ with site.Params.profile.bio }}{{ $desc = . | markdownify | plainify }}{{ end }}{{ end }} |
| 21 | {{ with $desc -}} |
| 22 | <meta name="description" content="{{ . }}"> |
| 23 | {{- end }} |
| 24 | |
| 25 | <!-- Fonts are self-hosted, so there is no third-party DNS + TLS before first paint. --> |
| 26 | <link rel="preload" href="/fonts/Newsreader-latin.woff2" as="font" type="font/woff2" crossorigin> |
| 27 | <link rel="preload" href="/fonts/JetBrainsMono-Regular.woff2" as="font" type="font/woff2" crossorigin> |
| 28 | {{ with resources.Get "css/style.scss" }} |
| 29 | {{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" }} |
| 30 | {{ with . | toCSS $opts }} |
| 31 | <link rel="stylesheet" type="text/css" href="{{ .RelPermalink }}"> |
| 32 | {{ end }} |
| 33 | {{ end }} |
| 34 | |
| 35 | <link rel="alternate" type="application/rss+xml" title="{{ site.Title }}" href="{{ "index.xml" | absURL }}"> |
| Smooth page transitions with htmx boost, morph, and preload ca8b900 Romain Gautier yesterday | 36 | {{- /* |
| 37 | Declarative, not htmx.config.preload = {...} in a script: a <meta> tag |
| 38 | is parsed with the rest of the document, before any script (deferred or |
| 39 | not) runs, so htmx's constructor sees it synchronously and correctly |
| 40 | every time - no dependency on inline-script/defer/DOMContentLoaded |
| 41 | ordering at all. boostEvent switches hx-preload's trigger from the |
| 42 | default mousedown/touchstart to mouseover, so the fetch for a boosted |
| 43 | link starts the instant the cursor touches it rather than waiting for a |
| 44 | press. |
| 45 | */}} |
| 46 | <meta name="htmx-config" content="preload.boostEvent:mouseover"> |
| 47 | <script src="{{ "js/htmx.min.js" | relURL }}" defer></script> |
| 48 | {{- /* |
| 49 | Official htmx extension, vendored from the same htmx.org package as |
| 50 | htmx.min.js above (node_modules/htmx.org/dist/ext/hx-preload.min.js - |
| 51 | re-copy it from there on a version bump). Prefetches a boosted link's |
| 52 | GET response, reused automatically if the click follows within 5s. |
| 53 | Needs no hx-ext or other opt-in - just being loaded activates it for |
| 54 | every boosted anchor. |
| 55 | */}} |
| 56 | <script src="{{ "js/hx-preload.min.js" | relURL }}" defer></script> |
| 57 | {{- /* |
| 58 | htmx's own browser back/forward handling (#restoreHistory) is a |
| 59 | separate code path from a boosted click - it calls htmx.ajax() directly |
| 60 | with swap:"outerSync" hardcoded, a full body replace, not our |
| 61 | innerMorph below. Left alone, a link click morphed smoothly but |
| 62 | back/forward tore the masthead down and rebuilt it. Preempt it with the |
| 63 | same config used everywhere else (kept as one Hugo value, $swapSpec, |
| 64 | rather than duplicated literals, so the two can't drift apart), and |
| 65 | abort a still-in-flight restore before starting the next one - the same |
| 66 | guard htmx's own implementation has - so mashing back/forward can't let |
| 67 | a slow, stale response land after a faster, newer one. |
| 68 | |
| 69 | `defer` only affects scripts with a `src` - htmx.min.js and |
| 70 | hx-preload.min.js above genuinely wait until after parsing, but this |
| 71 | inline script has no `src` and runs immediately in place during |
| 72 | parsing, defer or not. It must not touch `htmx` at its top level, only |
| 73 | from inside a listener that fires later - hence DOMContentLoaded, which |
| 74 | is guaranteed to fire only after every deferred script, htmx.min.js and |
| 75 | hx-preload.min.js included, has already run. |
| 76 | */}} |
| 77 | {{- $swapSpec := dict "swap" "innerMorph" "transition" true }} |
| 78 | <script> |
| 79 | document.addEventListener("DOMContentLoaded", () => { |
| 80 | let restoreAbort; |
| 81 | document.addEventListener("htmx:before:history:restore", (e) => { |
| 82 | e.preventDefault(); |
| 83 | restoreAbort?.abort(); |
| 84 | restoreAbort = new AbortController(); |
| 85 | htmx.ajax("GET", e.detail.path, { |
| 86 | target: document.body, |
| 87 | swap: "{{ $swapSpec.swap }}", |
| 88 | transition: {{ $swapSpec.transition }}, |
| 89 | request: {headers: {}, signal: restoreAbort.signal}, |
| 90 | }); |
| 91 | }); |
| 92 | }); |
| 93 | </script> |
| Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier yesterday | 94 | {{ block "head" . }}{{ end }} |
| 95 | </head> |
| Smooth page transitions with htmx boost, morph, and preload ca8b900 Romain Gautier yesterday | 96 | {{- /* |
| 97 | Boosts every same-origin link/form into an AJAX navigation, inherited from |
| 98 | body down (htmx 4 requires the ":inherited" suffix explicitly - it no |
| 99 | longer inherits hx-* attributes implicitly). Boosted elements default to |
| 100 | targeting the whole <body>; innerMorph diffs the new body onto the old |
| 101 | one node-by-node instead of replacing it wholesale, so unchanged nodes |
| 102 | (the masthead's "mykiwi.blog" link) are patched in place rather than |
| 103 | torn down and re-flashed, while changed ones - the per-page signal SVG |
| 104 | path, the swapped #main content - update normally. transition:true wraps |
| 105 | that diff in a native View Transition for a soft cross-fade. |
| 106 | */}} |
| 107 | <body hx-boost:inherited="swap:{{ $swapSpec.swap }} transition:{{ $swapSpec.transition }}"> |
| Port fzakaria.com design to Hugo and add a homepage profile 1bdbd06 Romain Gautier yesterday | 108 | <a class="skip-link" href="#main">Skip to content</a> |
| 109 | <div class="wrap"> |
| 110 | {{ partial "masthead.html" . }} |
| 111 | <main id="main">{{ block "main" . }}{{ end }}</main> |
| 112 | {{ partial "site-foot.html" . }} |
| 113 | </div> |
| 114 | </body> |
| 115 | </html> |