diff --git a/src/apphost/Aspire.Dev.AppHost/AppHost.cs b/src/apphost/Aspire.Dev.AppHost/AppHost.cs
index 62ff26e743..6471a4b806 100644
--- a/src/apphost/Aspire.Dev.AppHost/AppHost.cs
+++ b/src/apphost/Aspire.Dev.AppHost/AppHost.cs
@@ -13,6 +13,11 @@
.PublishAsAzureAppServiceWebsite((infra, website) =>
{
website.IsEndToEndEncryptionEnabled = true;
+
+ // App Service negotiates HTTP/1.1 unless HTTP/2 is enabled, which limits browsers to six
+ // connections per origin. The landing page loads dozens of small assets, so multiplexing
+ // them over one connection matters most on slow, high-latency links.
+ website.SiteConfig.IsHttp20Enabled = true;
});
var frontDoor = builder.AddAzureFrontDoor(staticHostWebsite);
diff --git a/src/frontend/src/components/AsciinemaPlayer.astro b/src/frontend/src/components/AsciinemaPlayer.astro
index 74e08e7431..b2b2ac43d7 100644
--- a/src/frontend/src/components/AsciinemaPlayer.astro
+++ b/src/frontend/src/components/AsciinemaPlayer.astro
@@ -1,4 +1,8 @@
---
+// Imported here (not in the client
>
)
}
@@ -79,6 +110,7 @@ const firstSeenKey = bannerHash
// so shipped behavior can't diverge from the tests. This bundled module reads
// its per-banner config from `data-*` attributes on the element (a bundled
// script can't use `define:vars`), leaving only DOM/localStorage plumbing here.
+ // The pre-paint script above applies the same decision before first paint.
import { parseFirstSeen, resolveBannerVisibility } from '@utils/banner-expiry';
const initializeBanner = () => {
diff --git a/src/frontend/src/components/starlight/Head.astro b/src/frontend/src/components/starlight/Head.astro
index 9fce574711..ef3ae838aa 100644
--- a/src/frontend/src/components/starlight/Head.astro
+++ b/src/frontend/src/components/starlight/Head.astro
@@ -1,5 +1,8 @@
---
import 'tippy.js/dist/tippy.css';
+import poppinsRegular from '@fontsource/poppins/files/poppins-latin-400-normal.woff2?url';
+import poppinsSemiBold from '@fontsource/poppins/files/poppins-latin-600-normal.woff2?url';
+import poppinsBold from '@fontsource/poppins/files/poppins-latin-700-normal.woff2?url';
import DefaultHead from '@astrojs/starlight/components/Head.astro';
import { ClientRouter } from 'astro:transitions';
import AccessibleCodeButtons from '../AccessibleCodeButtons.astro';
@@ -7,6 +10,10 @@ import SiteTour from '../SiteTour.astro';
import { getStructuredData } from '@utils/structured-data';
import { getOgMetadata } from '@utils/page-metadata';
+// The Poppins weights that text in the first viewport renders with. The same files back the
+// @font-face rules in site.css, so these resolve to the already-emitted assets.
+const preloadedFonts = [poppinsRegular, poppinsSemiBold, poppinsBold];
+
const repoUrl = (
import.meta.env.PUBLIC_REPO_URL ||
import.meta.env.REPO_URL ||
@@ -40,6 +47,18 @@ function computeSourceUrl() {
+{
+ /*
+ * Fonts are declared in site.css, so without these the browser only discovers them after that
+ * stylesheet has downloaded and parsed. Preloading starts the downloads with the document and
+ * lets the text paint in Poppins instead of swapping in (and re-flowing) after first paint.
+ */
+}
+{
+ preloadedFonts.map((href) => (
+
+ ))
+}
")
+ ).toBe(true);
+ expect(
+ importsCssFromBundledScript(
+ "---\nimport 'pkg/player.css';\n---\n"
+ )
+ ).toBe(false);
+ expect(
+ importsCssFromBundledScript(
+ "---\n// not a "
+ )
+ ).toBe(false);
+ expect(
+ importsCssFromBundledScript(
+ "---\n---\n"
+ )
+ ).toBe(false);
+ });
+
+ test('images that opt out of lazy loading', () => {
+ expect(optsOutOfLazyLoading('')).toBe(true);
+ expect(
+ optsOutOfLazyLoading("")
+ ).toBe(true);
+ expect(optsOutOfLazyLoading("const props = { loading: 'eager' as const };")).toBe(true);
+ expect(optsOutOfLazyLoading("images.forEach((image) => { image.loading = 'eager'; });")).toBe(
+ false
+ );
+ expect(optsOutOfLazyLoading('')).toBe(false);
+ });
+ });
+});