diff --git a/website/scripts/generate-placeholders.ts b/website/scripts/generate-placeholders.ts
index 61304c1..3c17893 100644
--- a/website/scripts/generate-placeholders.ts
+++ b/website/scripts/generate-placeholders.ts
@@ -2,9 +2,14 @@
* Generate pixelated placeholder images for the website.
*
* Scans website/public/ for all image files (png, jpg, jpeg, webp) and
- * generates a tiny 32px-wide version of each. When displayed at full size
- * with CSS `image-rendering: pixelated` (nearest-neighbor / point sampling),
- * these produce a crisp mosaic effect instead of a blurry upscale.
+ * generates a tiny 32px-wide version into src/assets/placeholders/.
+ * When displayed at full size with CSS `image-rendering: pixelated`
+ * (nearest-neighbor / point sampling), these produce a crisp mosaic effect.
+ *
+ * Output goes to src/assets/placeholders/ (NOT public/) so that Vite's
+ * asset pipeline processes them. Since all placeholders are < 4KB, Vite
+ * automatically inlines them as base64 data URIs via assetsInlineLimit,
+ * eliminating extra HTTP requests.
*
* Skips files that already have the `placeholder-` prefix.
* Skips regeneration if the placeholder is newer than the source image.
@@ -17,11 +22,14 @@ import path from "node:path";
import fs from "node:fs";
const PUBLIC_DIR = path.resolve(import.meta.dirname, "../public");
+const OUTPUT_DIR = path.resolve(import.meta.dirname, "../src/assets/placeholders");
const PLACEHOLDER_PREFIX = "placeholder-";
const PLACEHOLDER_WIDTH = 32;
const IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp"]);
async function main() {
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
+
const entries = fs.readdirSync(PUBLIC_DIR);
const images = entries.filter((name) => {
@@ -41,7 +49,7 @@ async function main() {
const inputPath = path.join(PUBLIC_DIR, name);
const ext = path.extname(name);
const base = path.basename(name, ext);
- const outputPath = path.join(PUBLIC_DIR, `${PLACEHOLDER_PREFIX}${base}${ext}`);
+ const outputPath = path.join(OUTPUT_DIR, `${PLACEHOLDER_PREFIX}${base}${ext}`);
// Skip if placeholder already exists and is newer than source
if (fs.existsSync(outputPath)) {
diff --git a/website/src/assets/placeholders/placeholder-favicon-16.png b/website/src/assets/placeholders/placeholder-favicon-16.png
new file mode 100644
index 0000000..0d386bd
Binary files /dev/null and b/website/src/assets/placeholders/placeholder-favicon-16.png differ
diff --git a/website/src/assets/placeholders/placeholder-favicon-32.png b/website/src/assets/placeholders/placeholder-favicon-32.png
new file mode 100644
index 0000000..425b1ad
Binary files /dev/null and b/website/src/assets/placeholders/placeholder-favicon-32.png differ
diff --git a/website/src/assets/placeholders/placeholder-og-image.png b/website/src/assets/placeholders/placeholder-og-image.png
new file mode 100644
index 0000000..5cfdbea
Binary files /dev/null and b/website/src/assets/placeholders/placeholder-og-image.png differ
diff --git a/website/src/assets/placeholders/placeholder-screenshot@2x.png b/website/src/assets/placeholders/placeholder-screenshot@2x.png
new file mode 100644
index 0000000..c4e1c3f
Binary files /dev/null and b/website/src/assets/placeholders/placeholder-screenshot@2x.png differ
diff --git a/website/src/components/markdown.tsx b/website/src/components/markdown.tsx
index 02f4aee..a234484 100644
--- a/website/src/components/markdown.tsx
+++ b/website/src/components/markdown.tsx
@@ -434,6 +434,20 @@ export function PixelatedImage({
style,
}: {
src: string;
+ /**
+ * URL of the tiny pixelated placeholder image. Use a static import so Vite
+ * inlines it as a base64 data URI (all placeholders are < 4KB, well under
+ * Vite's default assetsInlineLimit of 4096 bytes). This makes the
+ * placeholder available synchronously on first render with zero HTTP
+ * requests. Do NOT use dynamic imports or public/ paths — dynamic imports
+ * add a microtask delay, and public/ files bypass Vite's asset pipeline.
+ *
+ * @example
+ * ```tsx
+ * import placeholderScreenshot from "../assets/placeholders/placeholder-screenshot@2x.png";
+ *
+ * ```
+ */
placeholder: string;
alt: string;
width: number;
@@ -504,6 +518,133 @@ export function PixelatedImage({
);
}
+/* =========================================================================
+ Lazy video with pixelated poster placeholder
+ Same visual pattern as PixelatedImage but for