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); + }); + }); +});