Publish perf blog post + Such Moon Launch iOS links

- New post 'My Animated Splash Was Tanking Our Mobile PageSpeed Score' (draft:false):
  the LCP investigation, the data-URI diagnostic, and the real fix (shrink the
  use-client boundary / server-render the static parts). Two SVG diagrams.
- Such Moon Launch now live on iOS: App Store link wired on the apps and product
  pages.
This commit is contained in:
jwinterm
2026-06-06 20:41:31 -04:00
parent b43c9d851b
commit 33400c5879
5 changed files with 177 additions and 33 deletions
@@ -0,0 +1,41 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 340" role="img"
aria-label="Timeline comparing LCP before and after. Before: first contentful paint at 1.1s, then a roughly 4 second render-delay gap, then LCP at 5.2s. After: first contentful paint at 1.1s and LCP at 1.4s with the gap gone.">
<rect x="2" y="2" width="756" height="336" rx="16" fill="#f8fafc" stroke="#e2e8f0"/>
<text x="100" y="38" font-family="ui-sans-serif, system-ui, sans-serif" font-size="19" font-weight="700" fill="#1e293b">Where the LCP time goes</text>
<!-- Before row -->
<text x="100" y="100" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14" font-weight="600" fill="#334155">Before: one big client component</text>
<rect x="100" y="112" width="110" height="16" fill="#e2e8f0"/>
<text x="155" y="124" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="10" fill="#64748b">load</text>
<rect x="210" y="112" width="410" height="16" fill="#fde68a" stroke="#d97706"/>
<text x="415" y="124" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#b45309">render delay &#8776; 4 s</text>
<line x1="210" y1="106" x2="210" y2="134" stroke="#059669" stroke-width="2"/>
<circle cx="210" cy="120" r="4" fill="#059669"/>
<text x="210" y="150" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="11" fill="#059669">FCP 1.1 s</text>
<line x1="620" y1="106" x2="620" y2="134" stroke="#dc2626" stroke-width="2"/>
<circle cx="620" cy="120" r="5" fill="#dc2626"/>
<text x="620" y="150" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="11" font-weight="600" fill="#dc2626">LCP 5.2 s</text>
<!-- After row -->
<text x="100" y="197" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14" font-weight="600" fill="#334155">After: server HTML + small client island</text>
<rect x="100" y="209" width="110" height="16" fill="#e2e8f0"/>
<text x="155" y="221" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="10" fill="#64748b">load</text>
<rect x="210" y="209" width="30" height="16" fill="#fde68a" stroke="#d97706"/>
<line x1="210" y1="203" x2="210" y2="231" stroke="#059669" stroke-width="2"/>
<circle cx="210" cy="217" r="4" fill="#059669"/>
<circle cx="240" cy="217" r="5" fill="#059669"/>
<text x="252" y="221" font-family="ui-sans-serif, system-ui, sans-serif" font-size="11" font-weight="600" fill="#059669">FCP 1.1 s &#183; LCP 1.4 s</text>
<text x="430" y="221" font-family="ui-sans-serif, system-ui, sans-serif" font-size="11" fill="#64748b">render delay: gone</text>
<!-- time axis -->
<line x1="100" y1="290" x2="700" y2="290" stroke="#cbd5e1" stroke-width="1"/>
<g font-family="ui-sans-serif, system-ui, sans-serif" font-size="11" fill="#94a3b8" text-anchor="middle">
<line x1="100" y1="290" x2="100" y2="296" stroke="#cbd5e1"/><text x="100" y="312">0 s</text>
<line x1="200" y1="290" x2="200" y2="296" stroke="#cbd5e1"/><text x="200" y="312">1 s</text>
<line x1="300" y1="290" x2="300" y2="296" stroke="#cbd5e1"/><text x="300" y="312">2 s</text>
<line x1="400" y1="290" x2="400" y2="296" stroke="#cbd5e1"/><text x="400" y="312">3 s</text>
<line x1="500" y1="290" x2="500" y2="296" stroke="#cbd5e1"/><text x="500" y="312">4 s</text>
<line x1="600" y1="290" x2="600" y2="296" stroke="#cbd5e1"/><text x="600" y="312">5 s</text>
<line x1="700" y1="290" x2="700" y2="296" stroke="#cbd5e1"/><text x="700" y="312">6 s</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 360" role="img"
aria-label="Before: page.tsx is one client component holding the splash, header, hero, sections, and contact form, all of which ship and run as JavaScript, so the page paints late. After: page.tsx is a Server Component; only a small client island (splash, header, nav, background) ships JavaScript, while the hero and sections are server HTML, so the page paints early.">
<rect x="2" y="2" width="756" height="356" rx="16" fill="#f8fafc" stroke="#e2e8f0"/>
<text x="40" y="40" font-family="ui-sans-serif, system-ui, sans-serif" font-size="18" font-weight="700" fill="#1e293b">One client component vs. server HTML plus an island</text>
<!-- Before -->
<text x="60" y="82" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15" font-weight="700" fill="#334155">Before</text>
<rect x="40" y="92" width="300" height="206" rx="10" fill="none" stroke="#94a3b8" stroke-width="1.5"/>
<text x="54" y="114" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#475569">page.tsx &#8212; "use client"</text>
<g font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" fill="#b45309" text-anchor="middle">
<rect x="54" y="124" width="272" height="28" rx="5" fill="#fde68a" stroke="#d97706"/><text x="190" y="142">Splash</text>
<rect x="54" y="158" width="272" height="28" rx="5" fill="#fde68a" stroke="#d97706"/><text x="190" y="176">Header + nav</text>
<rect x="54" y="192" width="272" height="28" rx="5" fill="#fde68a" stroke="#d97706"/><text x="190" y="210">Hero + tagline</text>
<rect x="54" y="226" width="272" height="28" rx="5" fill="#fde68a" stroke="#d97706"/><text x="190" y="244">Sections</text>
<rect x="54" y="260" width="272" height="28" rx="5" fill="#fde68a" stroke="#d97706"/><text x="190" y="278">Contact form</text>
</g>
<text x="190" y="322" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" fill="#b45309">all of it ships and runs as JavaScript</text>
<text x="190" y="342" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#dc2626">&#8594; the page paints late</text>
<!-- After -->
<text x="420" y="82" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15" font-weight="700" fill="#334155">After</text>
<rect x="400" y="92" width="320" height="206" rx="10" fill="none" stroke="#94a3b8" stroke-width="1.5"/>
<text x="414" y="114" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#475569">page.tsx &#8212; Server Component</text>
<rect x="414" y="124" width="292" height="44" rx="5" fill="#fde68a" stroke="#d97706"/>
<text x="560" y="142" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#b45309">client island</text>
<text x="560" y="159" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="10" fill="#b45309">splash &#183; header &#183; nav &#183; background</text>
<rect x="414" y="176" width="292" height="40" rx="5" fill="#d1fae5" stroke="#059669"/>
<text x="560" y="200" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" fill="#047857">Hero + tagline &#8212; server HTML</text>
<rect x="414" y="224" width="292" height="40" rx="5" fill="#d1fae5" stroke="#059669"/>
<text x="560" y="248" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" fill="#047857">Sections &#8212; server HTML</text>
<text x="560" y="322" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" fill="#059669">only the island ships JavaScript</text>
<text x="560" y="342" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12" font-weight="600" fill="#047857">&#8594; the page paints early</text>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB