diff --git a/apps/site/src/components/Avatar.astro b/apps/site/src/components/Avatar.astro
index 7a1df277c..3b5e89c6f 100644
--- a/apps/site/src/components/Avatar.astro
+++ b/apps/site/src/components/Avatar.astro
@@ -20,10 +20,11 @@ const {
height = 48,
} = Astro.props;
-const src = Astro.locals.sanity
- .urlForImage(coverImage ?? undefined)
- ?.width(width)
- .height(height)
+const builder = Astro.locals.sanity.urlForImage(coverImage ?? undefined);
+const src = builder
+ ?.width(width * 2)
+ .height(height * 2)
+ .quality(82)
.url();
// stegaClean: href is an attribute; zero-width characters break the URL.
diff --git a/apps/site/src/components/ContentCard.astro b/apps/site/src/components/ContentCard.astro
index cfe4e8ee1..066f5e4df 100644
--- a/apps/site/src/components/ContentCard.astro
+++ b/apps/site/src/components/ContentCard.astro
@@ -28,8 +28,9 @@ const people = [
@@ -37,6 +38,7 @@ const people = [
)}
diff --git a/apps/site/src/components/CoverImage.astro b/apps/site/src/components/CoverImage.astro
index 75c875a9e..943e32ced 100644
--- a/apps/site/src/components/CoverImage.astro
+++ b/apps/site/src/components/CoverImage.astro
@@ -8,6 +8,7 @@ interface Props {
width?: number;
height?: number;
quality?: number;
+ sizes?: string;
/** Above-the-fold images opt out of lazy loading. */
priority?: boolean;
class?: string;
@@ -18,18 +19,37 @@ const {
fallbackAlt = "CodingCat.dev tutorial cover image",
width = 1920,
height = 1080,
- quality = 80,
+ quality = 82,
+ sizes = "(max-width: 768px) 100vw, (max-width: 1280px) 50vw, 960px",
priority = false,
class: className = "w-full h-auto aspect-video rounded-md",
} = Astro.props;
-const src = Astro.locals.sanity
- .urlForImage(image ?? undefined)
+const builder = Astro.locals.sanity.urlForImage(image ?? undefined);
+const aspect = height / width;
+
+const src = builder
?.width(width)
.height(height)
.quality(quality)
.url();
+const responsiveWidths = [320, 480, 640, 960, 1280, 1600, 1920].filter(
+ (w) => w <= width,
+);
+if (!responsiveWidths.includes(width)) {
+ responsiveWidths.push(width);
+}
+const srcset = builder
+ ? responsiveWidths
+ .map((w) => {
+ const h = Math.round(w * aspect);
+ const u = builder.width(w).height(h).quality(quality).url();
+ return `${u} ${w}w`;
+ })
+ .join(", ")
+ : undefined;
+
// stegaClean: alt lands in an attribute, where stega's zero-width characters
// would be read out verbatim by screen readers.
const rawAlt = stegaClean((image as { alt?: string } | null)?.alt);
@@ -42,13 +62,14 @@ const alt = rawAlt && rawAlt.trim().length > 0 ? rawAlt : fallbackAlt;
) : (
diff --git a/apps/site/src/components/CoverMedia.astro b/apps/site/src/components/CoverMedia.astro
index f31bc3bd2..01315f2a6 100644
--- a/apps/site/src/components/CoverMedia.astro
+++ b/apps/site/src/components/CoverMedia.astro
@@ -54,8 +54,14 @@ const youtubeUrl = stegaClean(youtube);
) : youtubeUrl ? (
-
+
) : (
-
+
)
}
diff --git a/apps/site/src/components/HomeSection.astro b/apps/site/src/components/HomeSection.astro
index f63ad522f..15b39da2e 100644
--- a/apps/site/src/components/HomeSection.astro
+++ b/apps/site/src/components/HomeSection.astro
@@ -43,8 +43,9 @@ const { title, description, items, muted = false } = Astro.props;
)}
diff --git a/apps/site/src/components/portable-text/PtYouTube.astro b/apps/site/src/components/portable-text/PtYouTube.astro
index 2c0a504c7..fabdb3175 100644
--- a/apps/site/src/components/portable-text/PtYouTube.astro
+++ b/apps/site/src/components/portable-text/PtYouTube.astro
@@ -1,39 +1,74 @@
---
import { stegaClean } from "@sanity/client/stega";
+import type { SanityImageSource } from "@sanity/image-url";
import type { Props as PtProps } from "astro-portabletext/types";
import { youtubeId } from "@/lib/youtube";
type YouTubeNode = { _type: "youtube"; youtube?: string };
-type Props = PtProps;
+type Props = PtProps & {
+ coverImage?: (SanityImageSource & { alt?: string }) | null;
+ priority?: boolean;
+};
-const { node } = Astro.props;
+const { node, coverImage, priority = false } = Astro.props;
const id = youtubeId(stegaClean(node.youtube));
+
+const builder = Astro.locals.sanity.urlForImage(coverImage ?? undefined);
+const sanitySrc = builder?.width(1920).height(1080).quality(82).url();
+const sanitySrcset = builder
+ ? [480, 640, 960, 1280, 1600, 1920]
+ .map((w) => {
+ const h = Math.round((w * 9) / 16);
+ return `${builder.width(w).height(h).quality(82).url()} ${w}w`;
+ })
+ .join(", ")
+ : undefined;
+const rawAlt = stegaClean((coverImage as { alt?: string } | null)?.alt);
+const alt = rawAlt && rawAlt.trim().length > 0 ? rawAlt : "";
---
{
id && (
-
-
+ {sanitySrc ? (
-
+ ) : (
+
+
+
+
+ )}