Skip to content

[Website] Optimize images#9875

Merged
tobias-tengler merged 5 commits into
mainfrom
tte/image-optimization
Jun 9, 2026
Merged

[Website] Optimize images#9875
tobias-tengler merged 5 commits into
mainfrom
tte/image-optimization

Conversation

@tobias-tengler

Copy link
Copy Markdown
Member

No description provided.

@tobias-tengler tobias-tengler force-pushed the tte/image-optimization branch from fe2821b to 0f5e0ee Compare June 9, 2026 15:29
@tobias-tengler tobias-tengler marked this pull request as ready for review June 9, 2026 15:29
Copilot AI review requested due to automatic review settings June 9, 2026 15:29
@tobias-tengler tobias-tengler merged commit 54b7f3d into main Jun 9, 2026
146 checks passed
@tobias-tengler tobias-tengler deleted the tte/image-optimization branch June 9, 2026 15:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a build-time image optimization pipeline for website-next (Sharp-based), plus a generated git-metadata manifest, and updates the website code to consume optimized variants/blur placeholders during static export and in CI.

Changes:

  • Introduces image optimization scripts (local + CI), remote image harvesting (avatars/YouTube thumbnails), and a runtime manifest reader.
  • Replaces many next/image / raw <img> usages with a new Picture/Image design-system implementation supporting AVIF/WebP + blur placeholders.
  • Moves git attribution from runtime git log to a pre-generated git-metadata.generated.json produced in the publish workflow.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
website-next/yarn.lock Adds sharp resolution/metadata.
website-next/package.json Adds scripts for optimization + git metadata generation (and a local CI script entry).
website-next/start-optimized.sh New local workflow script to optimize, build, dockerize, and run the site.
website-next/scripts/optimize-images.mjs New CLI to generate optimized variants + progress output.
website-next/src/image-optimization/generate.mjs Core optimizer: local + remote images, manifest generation, caching, pruning.
website-next/src/image-optimization/remote.mjs Scans content for remote avatar URLs + YouTube IDs to self-host.
website-next/src/image-optimization/manifest.ts Runtime manifest loader for optimized image lookups.
website-next/src/design-system/Picture.tsx New <picture> wrapper to serve AVIF/WebP + fallback <img>.
website-next/src/design-system/Image.tsx Adds blur placeholder background + load reconciliation.
website-next/src/design-system/Video.tsx Uses optimized YouTube thumbnail poster data when available.
website-next/src/design-system/VideoFacade.tsx Supports optimized poster object; falls back to external thumbnail.
website-next/src/design-system/Video.stories.tsx Updates story to new poster shape.
website-next/src/helpers/gitMetadata.ts Switches to reading generated git metadata manifest.
website-next/mdx-components.tsx Routes MDX <img> to Picture with consistent sizing/classes.
website-next/src/components/* Swaps various image render sites to Picture; also footer layout tweaks.
website-next/app/**/page.tsx Updates pages to use Picture + priority sizing where needed.
website-next/.gitignore Ignores generated optimization output and git-metadata manifest.
.github/workflows/publish-website-next.yml Adds caching and steps for optimization + git metadata generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 28 to 33
type GenericFacadeProps = CommonProps & {
provider: "generic";
poster: string;
poster?: VideoPoster;
embedSrc: string;
videoId?: never;
};
Comment on lines +24 to +45
function blurBackground(
blurDataURL: string,
blurWidth = 8,
blurHeight = 8
): CSSProperties {
const svgWidth = blurWidth * 40;
const svgHeight = blurHeight * 40;
const svg =
`%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${svgWidth} ${svgHeight}'%3E` +
`%3Cfilter id='b' color-interpolation-filters='sRGB'%3E` +
`%3CfeGaussianBlur stdDeviation='20'/%3E` +
`%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1'/%3E%3C/feComponentTransfer%3E` +
`%3C/filter%3E` +
`%3Cimage preserveAspectRatio='none' filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='${blurDataURL}'/%3E` +
`%3C/svg%3E`;
return {
backgroundImage: `url("data:image/svg+xml;charset=utf-8,${svg}")`,
backgroundSize: "cover",
backgroundPosition: "50% 50%",
backgroundRepeat: "no-repeat",
};
}
Comment on lines +8 to +12
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

IMAGE_TAG="website-next-local:ci"
CONTAINER_NAME="website-next-local-$$"

Comment thread website-next/package.json
Comment on lines 8 to 13
"start": "next start -p 3001",
"optimize-images": "node scripts/optimize-images.mjs",
"generate-git-metadata": "node scripts/generate-git-metadata.mjs",
"ci-local": "bash scripts/ci-local.sh",
"lint": "eslint",
"storybook": "storybook dev -p 6006",
Comment on lines +218 to +222
// Prune entries whose source no longer exists and best-effort delete orphans.
for (const [url, entry] of Object.entries(manifest)) {
if (!newManifest[url]) {
deleteOrphans(entry, outputDir, sourceDir);
}
Comment on lines 89 to 94
<SocialLink href={tools.blog} label="ChilliCream Blog">
<BlogIcon className="h-[22px] w-auto fill-current" />
<BlogIcon className="h-5.5 w-auto fill-current" />
</SocialLink>
<SocialLink href={tools.github} label="ChilliCream on GitHub">
<GitHubIcon className="h-[26px] w-auto fill-current" />
<GitHubIcon className="h-6.5 w-auto fill-current" />
</SocialLink>
Comment on lines +135 to 137
<div className="flex min-w-37.5 flex-col gap-6">
<h3 className="flex h-7.5 items-end text-base font-semibold text-cc-ink">
{title}
Comment on lines +19 to +32
const posterUrl = `https://i.ytimg.com/vi/${id}/maxresdefault.jpg`;
const opt = getOptimizedImage(posterUrl);
const poster = opt
? {
fallbackSrc: opt.fallbackSrc ?? posterUrl,
avifSrcSet: opt.formats.avif?.map((v) => `${v.path} ${v.w}w`).join(", "),
webpSrcSet: opt.formats.webp?.map((v) => `${v.path} ${v.w}w`).join(", "),
blurDataURL: opt.blurDataURL,
blurWidth: opt.blurWidth,
blurHeight: opt.blurHeight,
width: opt.width,
height: opt.height,
}
: undefined;
Comment on lines +44 to +46
<span className="inline-flex leading-none text-cc-ink">
<ChilliCreamText className="h-7.5 w-auto fill-current" />
</span>
Comment on lines +287 to +300
function deleteOrphans(entry, outputDir) {
for (const variants of Object.values(entry.formats ?? {})) {
for (const variant of variants) {
const file = urlToOutputFile(variant.path, outputDir);
try {
if (file && fs.existsSync(file)) {
fs.unlinkSync(file);
}
} catch {
// best-effort
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants